Android Handler机制 同步屏障

时间:2021-7-4 作者:qvyue

Android Handler机制

1.什么是handler?

我们都知道在App开发中,View的刷新等操作都需要在App的主线程来执行,那么我们需要在子线程更新UI的操作是可以使用Handler来完成UI的更新。

Handler的可以post一个延时消息的机制,我们可以使用Handler来完成几个简单的定时任务。

在一些子线程之间的通信,我们也可以使用Handler来实现。

那么什么是Handler?

Handler是Android 用来处理消息通信机制,Handler可以将一个任务切换到构成handler的looper所在的线程中去出处理。

所以Handler能够保证某一个模块在被多线程访问的时候,让多线程发出的消息有序安全的在同一个线程执行的消息。比如工作线程对Android的UI更新操作都需要使用Handler来保证UI的更新动作只能在UI线程中处理;

2.Handler的作用

多个线程并发更新UI的同时 保证线程安全

能够保证某一个模块在被多线程访问的时候,让多线程发出的消息有序、安全的在Handelr所在的线程执行的消息。

在一个App启动的时候,system_server进程会向zygote进程发送创建进程的请求,之后Zygote进程fork出新的子进程,即App进程,之后进入ActivityThread的main方法开始App的初始化工作;

在main方法中:

Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
}
 Looper.loop();

thread.getHandler()方法:

@UnsupportedAppUsage
final Handler getHandler() {
    return mH;
}

final H mH = new H();
class H extends Handler {

所以在app启动的时候回去创建一个Handler,Handler是支撑整个Android系统运行的基础,本质上Android系统都是由事件驱动的,而Handler能够保证所有其他工作线程的访问能够安全、有序的分发到主线程中。

3.Handler的简单使用和避免内存泄漏

handler的使用大概分为三类:

1.工作线程与UI主线程的消息通信;

2.工作线程、子线程需要让主线程执行一个延时任务;

3.工作线程中使用Handler实现并发安全(Android中不常用,只是一种方式)

具体使用流程这里不做介绍

Handler使用需要注意内存泄漏

我们handler的创建一般会在Acivity中,使用如下的方式:

//错误示例
    private var uiHander1 = object : Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
        }
    }

    inner class uiHander3: Handler(){
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
        }

内部类和匿名内部类都会持有外部类的引用,但是我们创建的mHander实例可能需要在耗时线程中使用,所以会造成内存泄漏。

示例:

public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable  Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    class UIHandler extends Handler{}
}

可以用通过Lint来看看上面代码:

Android Handler机制 同步屏障
image-20210522142756914

Handler在Activity中创建使用内部类和匿名内部类都是不正确的,正确的使用应该如下:

使用静态内部类或者将类单独新建,并且将使用到的Context或者Acivity使用弱引用:

fun work(){
        thread {
            Log.e("yhp", "workThread do some work!")
            Thread.sleep(2000)
            uiHandle2?.sendEmptyMessage(0)
        }
    }

    var uiHandle2: Handler? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_empty)

        uiHandle2 = UIHandler(this)
        work()
        
    }


    class UIHandler  (t: T) : Handler(Looper.getMainLooper()) {
        var weakReference: WeakReference = WeakReference(t)
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            weakReference.get()?.let {
                //UI线程
                Log.e("yhp", "uiThread do some work!")
            }
        }

    }

工作线程中使用Handler

工作线程中Handler的创建需要单独开启Looper

class ThreadHanderlActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_thread)
        startWorkThread()
        btn_send.setOnClickListener {
            workHandler?.run {
                sendEmptyMessage(0)
            }
        }

        btn_stop.setOnClickListener { stopWork() }

    }

    //子线程中的Handler
    var workHandler: Handler? = null

    //子线程
    var workThread: Thread? = null

    //记录子线程开启的Looper,在任务取消的时候使用quite结束Looper,此时子线程才算结束
    var workHandlerLooper: Looper? = null

    //主线程去开启子线程
    private fun startWorkThread(){
        workThread = thread {
            Looper.prepare()
            workHandlerLooper = Looper.myLooper()?.apply {
                //不能使用这种方式,因为这种方式默认为匿名内部类,一样持有外部类引用
               /* workHandler = object : Handler(this){
                    override fun handleMessage(msg: Message) {
                        super.handleMessage(msg)

                    }

                }*/
                workHandler = WorkHandler(this)
            }
            //处理耗时任务
            Log.e("yhp", "workHandler Thread do work!")
            Thread.sleep(2000)
            Looper.loop()
            //如果不调用Looper.quit(),该线程下面的部分将不会执行
            Log.e("yhp", "workHandler Looper end")
        }
    }


    private fun stopWork(){
        workHandlerLooper?.quit()
    }
    override fun onDestroy() {
        super.onDestroy()
        workHandlerLooper?.quit()
    }

    //不能使用inner class,因为使用inner class相当于是一个内部类 单独使用class相当于一个静态内部类,不持有外部类引用
    class WorkHandler @JvmOverloads constructor(looper: Looper, callback: Callback? = null) : Handler(looper, callback) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            Log.e("yhp", "子线程的Handler收到消息!")
        }
    }
}

在上面的实例中,如果在onDestroy中不将Lopper停止,那么工作线程将会一直执行, Looper.loop()后面的也不会执行;

Android Handler机制 同步屏障
image-20210522153623850

注意点:

1.子线程创建的Handler也不能使用内部类或者匿名内部类;

2.子线程创建Handler时 Looper.loop()后面将不会执行;

3.在Activity关闭或者其他因素时需要手动调用Looper?.quit()停止工作线程;

4.Handler机制及源码

主要成员

  1. Message:消息本体
  2. Hanlder:消息的发起者
  3. Looper:消息的遍历者
  4. MessageQueue:消息队列

Message

关键信息

when:消息需要被执行的时间

target:发送消息的Handler对象

callback:Runnable形式的消息

next:该消息的下一个消息

Android Handler机制 同步屏障
image-20210522170745848

Hanlder

负责消息发送和消息分发

MessageQueue

主要用来消息的插入、获取,维护消息的顺序;

Looper

Looper主要循环,不断的去取出MessageQueue中的messsage,然后转发消息给Handler

主要流程

Looper和MessageQueue创建

new Handler之前调用Looper.prepare()

Looper.prepare()主要是初始化Looper,此时newHandler操作的当前线程存在一个不可变的sThreadLocal变量;

static final ThreadLocal sThreadLocal = new ThreadLocal();

prepare中会new一个Looper给sThreadLocal,所以sThreadLocal.get()已经存在Looper时,那么就不允许再次创建,所以这里保证了一个线程只允许拥有一个Loper

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

Looper实例化中创建了一个MessageQueue,并记录了当前线程

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

那么到此可以知道:

一个线程只允许拥有一个Loper和一个MessageQueue

Handler的初始化

主要是获取到了当前线程Looper的实例和MessageQueue的实例

public Handler(@Nullable Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Looper.loop()

主要工作是使用MessageQueue的next()获取到消息,然后使用消息的target即发送消息的Handler实例调用dispatchMessage将消息分发至Handler中;

 public static void loop() {
        final Looper me = myLooper();
        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            try {
                msg.target.dispatchMessage(msg);
                
            } 
            msg.recycleUnchecked();
        }
    }

MessageQueue的enqueueMessage方法

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            //消息是否被消费
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
            //Looper循环是否被停止
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            //当前消息队列最顶端的消息
            Message p = mMessages;
            //是否要唤醒
            boolean needWake;
            //当队列最顶端的消息不存在时,将当前消息移到最顶端,指向下一个p其实是为空
            //mBlocked记录当前loop循环是否已经休眠,如果执行时间已经到了那么会根据当前的休眠状态来设置是否需要唤醒的标志位
            if (p == null || when == 0 || when 

大概流程就是将传入的Message插入到按照执行时间排序的队列中,并根据最顶端的消息的执行时间和线程的休眠状态来设置唤醒标志位,然后唤醒。

MessageQueue的next()方法

    Message next() {
        //如果消息循环已经退出并被释放,则在这里返回。
        //如果应用程序试图在不支持的quit之后重新启动一个循环程序,就会发生这种情况。
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //进入睡眠
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            //队列最顶端、最先要被执行的
            Message msg = mMessages;
            //如果消息同步屏障,则将消息放入到队列的最顶端
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                //如果最顶端的消息执行时间还没到,则记录需要休眠的时间
                if (now 

根据队列最顶高端的消息执行时间和条件,处理睡眠的时间或者返回最顶端的消息,消息屏障见后面章节,我们应用层发送的消息都不是同步屏障。

Handler发送消息

Android Handler机制 同步屏障
image-20210522180434131
Android Handler机制 同步屏障
image-20210522180447083

Handler发送的消息或者post的Runnable都会进入enqueueMessage中,也就进入了MessageQueue的enqueueMessage方法。

Handler消息分发

Looper.loop()最终调用Message的发送者handler的dispatchMessage方法

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

handleCallback:执行Runnable任务

handleMessage:也就是我们自己重写的handleMessage方法

消息回收

Looper.loop()最终调用Message的发送者handler的dispatchMessage方法之后会使用

msg.recycleUnchecked();

回收消息

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize 

这步骤也就是将处理过的Message中的所有参数置为初始值,然后放到一个队列中,这个回收队列最大值为50.

private static final int MAX_POOL_SIZE = 50;

所以Handler可以使用obtain来获取已经被回收的Message

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

5.消息同步屏障

MessageQueue的next()方法中会有一个msg.target == null的判断,这个是用来小处理消息同步屏障的

  if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }


什么是同步屏障

线程的消息都是放到同一个MessageQueue里面,取消息的时候是互斥取消息,而

且只能从头部取消息,而添加消息是按照消息的执行的先后顺序进行的排序,那么问题来了,同一个时间范围内的消息,如果它是需要立刻执行的,那我们怎么办,按照常规的办法,我们需要等到队列轮询到我自己的时候才能执行哦,那岂不是黄花菜都凉了。所以,我们需要给紧急需要执行的消息一个绿色通道,这个绿色通道就是同步屏障的概念。

屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过。如何开启同步屏障呢?

MessageQueue#postSyncBarrier()
private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when 

postSyncBarrier方法并没有设置target ,并且该方法已经新建了一条消息插入到队列最顶端。

同步消息的应用场景

似乎在日常的应用开发中,很少会用到同步屏障。那么,同步屏障在系统源码中有哪些使用场景呢?Android 系统中

的 UI 更新相关的消息即为异步消息,需要优先处理。

比如,在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了

ViewRootImpl#scheduleTraversals() ,如下:

ViewRootImpl.java

   void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

postCallback() 最终走到了 Choreographer postCallbackDelayedInternal()

Choreographer.java

private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {
    if (DEBUG_FRAMES) {
        Log.d(TAG, "PostCallback: type=" + callbackType
                + ", action=" + action + ", token=" + token
                + ", delayMillis=" + delayMillis);
    }

    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime 

由于 UI 更新相关的消息是优先级最高的,这样系统就会优先处理这些异

步消息。

最后,当要移除同步屏障的时候需要调用 ViewRootImpl#unscheduleTraversals() 。

void unscheduleTraversals() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        mChoreographer.removeCallbacks(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

结论:

同步屏障的设置可以方便地处理那些优先级较高的异步消息。当我们调用

Handler.getLooper().getQueue().postSyncBarrier() 并设置消息的 setAsynchronous(true) 时,target 即 为 null ,也就开启了同步屏障。当在消息轮询器 Looper 在 loop() 中循环处理消息时,如若开启了同步屏障,会优先处理其中的异步消息,而阻碍同步消息。

6.Handler机制流程图

主要流程如下图:

Android Handler机制 同步屏障
Handler

5.HandlerThread

HandlerThread是Thread的子类,严格意义上来说就是一个线程,只是它在自己的线程里面帮我们创建了Looper

@Override
    public void run() {
        // 1. 获得当前线程的id
        mTid = Process.myTid();
        // 2. 创建1个Looper对象 & MessageQueue对象
        Looper.prepare();
        // 3. 通过持有锁机制来获得当前线程的Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();
            // 发出通知:当前线程已经创建mLooper对象成功
            // 此处主要是通知getLooper()中的wait()
            notifyAll();
        }
        // 4. 设置当前线程的优先级
        Process.setThreadPriority(mPriority);
        // 5. 在线程循环前做一些准备工作
        // 该方法实现体是空的,由子类决定是否实现
        onLooperPrepared();
        // 6. 进行消息循环,即不断从MessageQueue中取消息 & 派发消息
        Looper.loop();
        mTid = -1;
    }

工作原理

HandlerThread继承Thread,在Thread开始执行时跟主线程在ActivityThread.main()方法内执行代码逻辑类似,初始化Looper–Looper.prepare(),轮询消息–Looper.loop();

初始化Handler时,使用HandlerThread线程的Looper对象初始化—- new Handler(Looper)构造方法。

使用实例

 // 步骤1:创建HandlerThread实例对象
  // 传入参数 = 线程名字,作用 = 标记该线程
  HandlerThread mHandlerThread = new HandlerThread("handlerThread");
  // 步骤2:启动线程
  mHandlerThread.start();
  // 步骤3:创建工作线程Handler & 复写handleMessage()
  // 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与 其他线程进行通信
  // 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
  Handler workHandler = new Handler( handlerThread.getLooper() ) {
            @Override
            public boolean handleMessage(Message msg) {
                ...//消息处理
                return true;
            }
        });

  // 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
  // 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
  // a. 定义要发送的消息
  Message msg = Message.obtain();
  //消息的标识
  msg.what = 1;
  // b. 通过Handler发送消息到其绑定的消息队列
  workHandler.sendMessage(msg);

// 步骤5:结束线程,即停止线程的消息循环
  mHandlerThread.quit();

getLooper同步问题

public Looper getLooper() {
        // 若线程不是存活的,则直接返回null
        if (!isAlive()) {
            return null;
        } 
        // 若当前线程存活,再判断线程的成员变量mLooper是否为null
        // 直到线程创建完Looper对象后才能获得Looper对象,若Looper对象未创建成功,则阻塞
        synchronized (this) {
  
      
            while (isAlive() && mLooper == null) {
                try {
                    // 此处会调用wait方法去等待
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        // 上述步骤run()使用 持有锁机制 + notifyAll()  获得Looper对象后
        // 则通知当前线程的wait()结束等待 & 跳出循环
        // 最终getLooper()返回的是在run()中创建的mLooper对象
        return mLooper;
    }

使用同步锁、wait() 和 notifyAll()

即 在run()中成功创建Looper对象后,立即调用notifyAll()通知 getLooper()中的wait()结束等待 & 返回run()中成功创建的Looper对象,使得Handler与该Looper对象绑定。

参考文章:https://www.jianshu.com/p/db915d207e6d

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。