美文网首页
Handler消息机制(Handler、Looper、Messa

Handler消息机制(Handler、Looper、Messa

作者: 小谭小 | 来源:发表于2020-10-30 22:40 被阅读0次

一、为什么要学习Handler消息机制:

感觉Android App都是通过Handler消息机制驱动起来的, 比如activity的生命周期、View的绘制/刷新等

二、总结

Handler: 发送和处理消息 ; Looper: 轮询消息队列, 通过loop()方法调用MessageQueue.next()方法,取出消息交给Handler处理 ; MessageQueue: 优先级队列,存储消息,next()函数真正取消息得地方; Message: 消息体 , 通讯/切换的根本, 因为Message是可以在线程间共享的

整个工作流程感觉和传送带工作流程比较像:
Handler(工人)把Message(包裹)从别的地方(可以是当前线程也可以是子线程)放到MessageQueue(传送带)上, Looper(电机)通过loop()轮询,从MessageQueue中取出消息,交给Handler的dispatchMessage处理


Handler消息机制流程.png

三、Handler:

1.作用以及使用方法: 线程间通讯、延时任务/循环任务、消息处理

1、线程间通讯: 主线程中创建Handler实例: MyHandler mHandler = new MyHandler () , 子线程中执行完成任务后通过mHandler.sendMessage等方法发送消息, 然后Handler在自己的handleMessage方法中处理消息,这样就完成了把消息从子线程发送到了主线程
2、延时任务/循环任务: sendMessageDelayed(Message msg, long delayMillis)/postDelayed(Runnable r, long delayMillis)等方法
3、消息处理: dispatchMessage ->runnable.run() 、callBack回调、重写handleMessage()

2.相关函数:

1、发送消息/任务: sendMessage(Message msg)/post(Runnable run) -> sendMessageAtTime(Message msg, long uptimeMillis) ->enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
2、处理Looper取出来的消息: dispatchMessage(Message msg), 感觉像责任链模式:
如果消息的callBack(一个Runnable)不为空,那就直接调用callBack的run(0方法, 如果msg.callBack为空, 再看new Handler 的时候是否设置CallBack, 如果设置了就调用CallBack的handleMessage方法, 如果前2者都为空, 那么再调用Handler的方法: handleMessage

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//如果消息设置了callBack(就是一个Runnable)
            handleCallback(msg);
        } else {
            if (mCallback != null) {//如果new Handler的时候设置了CallBack回调
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg); //如果前2者都没
        }
    }

3、移除消息: removeMessages(int what): 移除msg.what = what的消息,可能是多个;
removeCallbacksAndMessages(Object token): 移除msg.obj = token的消息, 如果token = null,那么移除所有消息和callBack;
4、runWithScissors(final Runnable r, long timeout): 同步执行代码,意思是等待Runnable中的代码执行完成后, 后面的代码才会执行;
应用场景: 子线程向主线发送一个任务,等待主线程任务执行完成后,再继续执行

3.注意事项

1、内存泄漏:
(1).在使用Handler的时候, 最好用静态内部类+弱引用的方式(静态内部类不持有外部类引用), 并且在activity/fragment等要销毁的候:removeCallbacksAndMessages(nulll), 避免内存泄漏

private MyHandler mHandler =new MyHandler(TestHandlerActivity.this)
private static class MyHandler extend Handler{
       private final WeakReference<TestHandlerActivity> mWeakReference;
       MyHandler(TestHandlerActivity activity){
              mWeakReference = new  WeakReference<>(activity)
       }
      @OverRide
      public void handleMessage(Message msg){
                TestHandlerActivity activity = mWeakReference.get();
                if(activity!=null){
                      //doing something
                }
      }
}

(2).为何会内存泄漏: Message.target = handler > handler又持有activity的引用(内部类默认持有外部类引用), 如果activity关闭的时候, Message还在MessageQueue里面(比如发送的延时任务还未执行), 那么target就一直在, target在activity就得不到释放

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//把this handler赋值给了Message
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

四、Looper

1.作用和使用方法:

1.作用: 通过loop()方法死循环轮询消息列表, 取出可以执行的消息交给Handler处理
2.使用方法: 先调用Looper.prepare() 然后 new Handler , 然后调用Looper.loop()开始轮询

    private lateinit var mHandler: Handler
    private fun startLoopAtAsyncThread() {
        Thread {
            Looper.prepare()
            mHandler = object : Handler(Looper.myLooper()){
                override fun handleMessage(msg: Message?) {
                    super.handleMessage(msg)
                    //子线程处理消息
                }
            }
            Looper.loop()
        }.start()
        mHandler.sendEmptyMessage(11)
    }
2.主要函数

1.构造函数: 私有的别的地方不能直接new, 同时创建了一个消息队列MessageQueue, 然后和当前线程进行了绑定

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

2.prepare()函数: 只能调用一次,保证一个线程只有一个Looper; 需要先于loop()调用 ; 通过ThreadLocal保存, 保证Looper是和当前线程有关的, 和其他线程无关(线程隔离)

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//判断下如果有Looper了, 那么就抛出错误
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

3.loop()函数:

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {//1.如果还没有调用prepare,就抛错
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
         
        for (;;) {//2.死循环轮询
            Message msg = queue.next(); // might block 3.取消息得时候大部分是阻塞的
            if (msg == null) {//4.调用quit()之后, 结束轮询,线程结束
                // No message indicates that the message queue is quitting.
                return;
            }

            //5. 1、通过给Looper设置Printer 可以监听每个消息的处理时间, 这就是BlockCanary的原理
            //Looper.getMainLooper().setMessageLogging()
            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            try {
                msg.target.dispatchMessage(msg);//6. 把消息交给Handler处理的地方
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            
            if (logging != null) {//5.2、日志打印监听结束的地方
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            //7. 享元模式, 复用内存的地方
            msg.recycleUnchecked();
        }
    }

4.quit()函数: 中断loop轮询, 线程销毁; 流程是: 调用MessageQueue的quit(), 设置mQuitting标志位为true, 如果为true, 那么MessageQueue的next()返回一个null的消息, Looper loop的时候,如果判断取到的消息为null, 那么直接return, 退出死循环

public void quit() {
    mQueue.quit(false);
}
Message next() {
 if (mQuitting) {
    dispose();
    return null;
 }
}

5.prepareMainLooper() 、getMainLooper(): 在ActivityThread的main()方法中调用了,所以我们平时使用主线程的Handler的时候,不用先调用Looper.prepare();
作用是: App启动的时候就准备好主线程, 通过这个主线程的Handler可以驱动整个app运行

public static void main(String[] args) {
        Looper.prepareMainLooper();//1. 准备好主线的Looper

        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);// 2. 感觉是开始的地方

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

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Looper.loop();//3. 开始轮询

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

五、MessageQueue

1.作用: 存/取消息的一个优先级队列
2.主要函数:

1.存消息: enqueueMessage(Message msg, long when)
2.取消息: Message next()

3.如何保证线程安全: 存/取消息的时候都加: synchronized (this)

这个锁说明: 所有调用对一个MessageQueue对象的线程都是互斥的, 也就是说存/取消息的时候,每次都只能执行一个线程的请求, 也就说是: 存/取动作都只能一个个来;
next()函数中也要加锁, 是为了保证在取的时候, 不会被别的地方存消息进来, 这样就能保证取的消息不混乱

4.IdleHandler: 当MessageQueue.next()取消息的时候,发现现在没有可以执行的消息,也就是Handler空闲的时候, 会回调这个类

应用场景:
1.Activity启动优化, onCreate/onResume/onStart中非必要且耗时较短的操作,可以放在IdleHandler中执行

Looper.myLooper()?.queue?.addIdleHandler {
            LogUtil.i("------IdleHandler回调------------")
            //返回false  表示 只执行一次,  true表示 每次空闲的时候都会回调
            false
}
5.消息机制同步屏障

1.什么是同步屏障: 阻碍同步消息, 让异步消息优先通过,得到处理, 我理解的是: 消息队列设置一个障碍, 如果遇到这个障碍的时候, 优先取障碍后的下一个异步消息
2.如何开启/关闭同步屏障: MessageQueue.postSyncBarrier() / removeSyncBarrier(int token)
3.源码理解:
postSyncBarrier()的时候会直接在队列里面加一个target==null 的消息, 然后调用next()的时候,会判断如果当前消息的target == null , 就优先取队列里面下一个异步消息(isAsynchronous)

Message next() {
              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());
               }
}

4.应用场景:
UI更新的相关消息都是异步消息,需要优先处理
View的draw、invalidate、requestLayout等方法都会调到:
ViewRootImpl的scheduleTraversals:

  void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//1.开启同步屏障
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

//编舞者Choreographer: mChoreographer.postCallback会走到这里
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 <= now) {
                scheduleFrameLocked(now);
            } else {//2.发送异步消息 msg.setAsynchronous(true);
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);//3.移除掉同步屏障
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

六、Handler消息机制常见问题

1:一个线程有几个 Handler?
2:一个线程有几个 Looper?如何保证?
3:Handler 内存泄漏原因? 为什么其他的内部类没有说过有这个问题?
4:为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
5:子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
6:既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?
7:我们使用 Message 时应该如何创建它?
8:Looper死循环为什么不会导致应用卡死

七、HandlerThread

1.什么是HandlerThread: 创建好了Looper的一个子线程
2.作用:

(1)方便使用: 创建好了Looper,
(2)保证了获取looper的时候的安全
synchronized 锁住mLooper的赋值, getLooper的时候也用synchronized锁住,while循环一直等待,
只有当mLooper不为null的时候才返回, wait() 会释放锁, notifyAll{}不释放锁

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {//1.赋值的时候锁住, 别人getLooper会等待这里执行完成后才返回
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {//如果looper还是null, 那么就等创建好了再返回
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
3.使用
        val handlerThreadOne = HandlerThread("handlerThreadOne")
        handlerThreadOne.start()
        //获取子线程one的handler, handlerThread 只有在start之后,looper才会准备好, 因为Looper是
        //在run方法里面准备的
        val threadOneHandler = ThreadOneHandler(handlerThreadOne.looper)

     private class ThreadOneHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message?) {
            super.handleMessage(msg)
            msg?.run {
                LogUtil.i("线程: ${Thread.currentThread().name}  接收到了消息: msg.what = ${msg.what}")
            }
        }
    }

八、IntentService

1.什么是IntentService: 处理耗时任务的service, 处理完成后自动销毁;

处理串行任务: 多次startService任务会依次串行执行,然后才会自动销毁(stop()方法的参数startId)

2.使用:
        val intentOne = Intent(applicationContext, MyIntentService::class.java)
        intentOne.action = ACTION_ONE
        startService(intentOne)

    class MyIntentService(name: String) : IntentService(name) {

        //如果没有0参数的构造,会报错
        constructor() : this("default")

        override fun onHandleIntent(intent: Intent?) {
            intent?.apply {
                val action = intent.action
                if (!TextUtils.isEmpty(intent.action)) {
                    when (action) {
                        ACTION_ONE -> {//第一个intent
                            Thread.sleep(5000)
                            LogUtil.i("第一个intent任务完成, 线程: " + Thread.currentThread().name)
                        }
                        ACTION_TWO -> {//第二个任务
                            Thread.sleep(4000)
                            LogUtil.i("第二个intent任务完成, 线程: " + Thread.currentThread().name)
                        }
                        ACTION_THREE -> {//第二个任务
                            Thread.sleep(3000)
                            LogUtil.i("第三个intent任务完成, 线程: " + Thread.currentThread().name)
                        }
                    }
                }
            }
        }

        override fun onDestroy() {
            super.onDestroy()
            LogUtil.i("onDestroy")
        }
    }

相关文章

网友评论

      本文标题:Handler消息机制(Handler、Looper、Messa

      本文链接:https://www.haomeiwen.com/subject/nsejvktx.html