美文网首页
Handler原理

Handler原理

作者: azmohan | 来源:发表于2020-09-01 13:51 被阅读0次

    Handler是什么

    Handler主要用于异步消息的处理: 封装了消息投递、消息处理等接口。当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

    /**
     * A Handler allows you to send and process {@link Message} and Runnable
     * objects associated with a thread's {@link MessageQueue}.  Each Handler
     * instance is associated with a single thread and that thread's message
     * queue.  When you create a new Handler, it is bound to the thread /
     * message queue of the thread that is creating it -- from that point on,
     * it will deliver messages and runnables to that message queue and execute
     * them as they come out of the message queue.
    

    Handler的用途

     * There are two main uses for a Handler: (1) to schedule messages and
     * runnables to be executed at some point in the future; and (2) to enqueue
     * an action to be performed on a different thread than your own.
    

    举例:

    • 当你在broadcast receiver的onReceiver做耗时的操作可能会报ANR,使用Handler把要处理的消息发出去。这样不会block此接口。
    • 当在一个接口中,需要接口返回后,再去执行某个操作,也可以使用Hanlder把Message或Runnable发出去。
    • 当想在非UI线程更新UI时,可以使用Handler把Message或Runnable发出去,在UI线程中更新。

    Handler使用

    package com.example.handlertest;
    
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.view.View;
    import android.widget.TextView;
    
    public class MainActivity extends Activity {
        private TextView mTextView;
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                mTextView.setText((String) msg.obj);
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = findViewById(R.id.text_view_id);
            mTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Thread thread = new Thread() {
                        @Override
                        public void run() {
                            // mTextView.setText("non-ui thread update text");
                            Message message = Message.obtain();
                            message.obj = "onClick";
                            mHandler.sendMessage(message);
                        }
                    };
                    thread.start();
                }
            });
    
        }
    }
    
    

    上面代码是在工作线程通过Handler给主线程发消息更新UI。注:如果直接在工作线程设置text,会扔如下异常:

    08-31 16:57:57.788 26607 26664 E AndroidRuntime: Process: com.example.handlertest, PID: 26607
    08-31 16:57:57.788 26607 26664 E AndroidRuntime: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8535)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1519)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.view.View.requestLayout(View.java:24642)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.view.View.requestLayout(View.java:24642)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.view.View.requestLayout(View.java:24642)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.view.View.requestLayout(View.java:24642)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3172)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.view.View.requestLayout(View.java:24642)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.widget.TextView.checkForRelayout(TextView.java:9691)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.widget.TextView.setText(TextView.java:6269)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.widget.TextView.setText(TextView.java:6097)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at android.widget.TextView.setText(TextView.java:6049)
    08-31 16:57:57.788 26607 26664 E AndroidRuntime:        at com.example.handlertest.MainActivity$2$1.run(MainActivity.java:31)
    

    Handler图解

    image

    上图可以看出Handler就是干两件事件,sendMessage和handleMessage,乍一看,这个设计有点傻X啊,自己发消息,自己处理,就不会写个接口,自己调用自己吗?这是有病吗?

    原因:其实sendMessage和handleMessage是可以不在同一个线程执行的。sendMessage可以在任何线程发送Message,handleMessage只是在Looper所在线程中回调。里面包含了线程转换(当然是同一个线程也没有问题,不管多少个线程,本质是不变的,只不过线程越多,处理越复杂)。

    Handler源码分析

    • sendMessage消息发给了谁?发送传了什么数据?
    • handleMessage是何时回调的?
    • loop是什么时候被调用?
    • Looper是什么?如何让Handler机制的回调不在主线程中回调?
    • HandlerThread
    • MessageQueue又是什么,数据结构是什么?

    Message时序图

    image

    上图可知,把Message发送给了MessageQueue,那发送的是那些信息呢?

     private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                long uptimeMillis) {
            msg.target = this;
            msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    出了我们填充的what、arg1、arg2、obj外,还会把Handler对象本身、workdSourceUid发给MessageQueue。(可以思考一下为什么?)

    handleMessage的回调逻辑

     /**
         * Handle system messages here.
         */
        public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    public static void loop() {
    for (;;) {
    // begin xxxxx
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }    
                try {
                    msg.target.dispatchMessage(msg);
                    if (observer != null) {
                        observer.messageDispatched(token, msg);
                    }
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } catch (Exception exception) {
                    if (observer != null) {
                        observer.dispatchingThrewException(token, msg, exception);
                    }
                    throw exception;
                } 
            }
    // end xxxxx
    }
    

    可以看到msg.target.dispatchMessage(msg);进而调到handleMessage。

    loop什么时候调用

    前面说了,handleMessage是在指定的线程中调用,如果new 一个无参的handler,默认使用的调用线程的looper。既然要在此线程中运行,理论上会在线程run方法中调用loop。AS搜索调用的地方,相关的就是ActivityThread.java和HandlerThread.java。而HandlerThread.java里是在run方法中调用的,很好理解。但ActivityThread.java,居然是在main里面调用的。(开始学习Android时,最痛苦的是没有main。哈哈,现在有了)。ActivityThread其实就是Android的主线程,有main函数,程序执行的入口 。(讲Linux环境高级编程的时候,讲过进程运行起来后,会默认创建一个线程,可理解成主线程,在Android中叫UI线程)。
    有关ActivityThread的介绍请参考
    https://www.cnblogs.com/mingfeng002/p/10323668.html

    Looper是什么,如何让Handler机制的回调不在主线程中回调?

    /**
      * Class used to run a message loop for a thread.  Threads by default do
      * not have a message loop associated with them; to create one, call
      * {@link #prepare} in the thread that is to run the loop, and then
      * {@link #loop} to have it process messages until the loop is stopped.
      *
      * <p>Most interaction with a message loop is through the
      * {@link Handler} class.
      *
      * <p>This is a typical example of the implementation of a Looper thread,
      * using the separation of {@link #prepare} and {@link #loop} to create an
      * initial Handler to communicate with the Looper.
      *
      * <pre>
      *  class LooperThread extends Thread {
      *      public Handler mHandler;
      *
      *      public void run() {
      *          Looper.prepare();
      *
      *          mHandler = new Handler() {
      *              public void handleMessage(Message msg) {
      *                  // process incoming messages here
      *              }
      *          };
      *
      *          Looper.loop();
      *      }
      *  }</pre>
      */
    

    Looper就是在一个线程中运行消息循环的帮助类。示例代码就是在一个自己创建的线程中完成消息轮询。通过此case可以实现消息在指定线程处理。

    HandlerThread

    HandlerThread是Android实现一个绑定Looper消息处理线程类。核心代码如下:

    @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
    

    使用如下:

    private HandlerThread mRespondThread;
        private Handler mResponseHandler;
    private void startThreads() {
            mRespondThread = new HandlerThread("Response_Camera_AR3.0");
            mRespondThread.start();
            mResponseHandler = new Handler(mRespondThread.getLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
        }
    
        private void stopThreads() {
            if (mRespondThread != null) {
                mRespondThread.quitSafely();
                try {
                    mRespondThread.join(JOIN_THREAD_TIMEOUT);
                    mRespondThread = null;
                    mResponseHandler = null;
                } catch (InterruptedException e) {
                    Log.e(TAG, "Interrupted while trying to join mRespondThread", e);
                }
            }
        }
    

    MessageQueue又是什么,数据结构是什么?

    看loop的实现,在for中会调用Message msg = queue.next(); // might block取消息。而sendMessage会调用enqueueMessage送消息,这样就是可以玩下去了。

    问题

    • 如果没有消息了,looper所在线程会怎样?
    • 空消息一段时间后,突然来了消息looper所在线程会怎样?
      看loop的实现就是for循环在那取消息,感觉像是轮询。如果是轮询,主线程就不用干其它事了。

    这就需要研究MessageQueue的数据结构和实现原理了。
    详细介绍参考:
    https://blog.csdn.net/kisty_yao/article/details/71191175

    相关文章

      网友评论

          本文标题:Handler原理

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