• Android多线程分析之三:Handler,Looper的实现


    Android多线程分析之三:Handler,Looper的实现

    CC 许可,转载请注明出处

    在前文《Android多线程分析之二:Thread的实现》中已经具体分析了Android Thread 是怎样创建,执行以及销毁的,其重点是对对应 native 方法进行分析,今天我将聚焦于 Android Framework 层多线程相关的类:Handler, Looper, MessageQueue, Message 以及它们与Thread 之间的关系。能够用一个不太妥当的比喻来形容它们之间的关联:假设把 Thread 比作生产车间,那么 Looper 就是放在这车间里的生产线,这条生产线源源不断地从 MessageQueue 中获取材料 Messsage,并分发处理 Message (因为Message 一般是完备的,所以 Looper 大多数情况下仅仅是调度让 Message 的 Handler 去处理 Message)。正是因为消息须要在 Looper 中处理,而 Looper 又需执行在 Thread 中,所以不能随随便便在非 UI 线程中进行 UI 操作。 UI 操作一般会通过投递消息来实现,仅仅有往正确的 Looper 投递消息才干得到处理,对于 UI 来说,这个 Looper 一定是执行在 UI 线程中。

    在编写 app 的过程中,我们经常会这样来使用 Handler:

    Handler mHandler = new Handler();
    mHandler.post(new Runnable(){
    	@Override
    	public void run() {
    		// do somework
    	}
    });
    或者如这系列文章第一篇中的演示样例那样:
        private Handler mHandler= new Handler(){
            @Override
            public void handleMessage(Message msg) {
            	Log.i("UI thread", " >> handleMessage()");
    
                switch(msg.what){
                case MSG_LOAD_SUCCESS:
                	Bitmap bitmap = (Bitmap) msg.obj;
                    mImageView.setImageBitmap(bitmap);
    
                    mProgressBar.setProgress(100);
                    mProgressBar.setMessage("Image downloading success!");
                    mProgressBar.dismiss();
                    break;
    
                case MSG_LOAD_FAILURE:
                    mProgressBar.setMessage("Image downloading failure!");
                    mProgressBar.dismiss();
                	break;
                }
            }
        };
    
        Message msg = mHandler.obtainMessage(MSG_LOAD_FAILURE, null);
        mHandler.sendMessage(msg);


    那么,在 Handler 的 post/sendMessage 背后究竟发生了什么事情呢?以下就来解开这个谜团。
    首先我们从 Handler 的构造函数開始分析:

        final MessageQueue mQueue; 
        final Looper mLooper; 
        final Callback mCallback; 
        final boolean mAsynchronous;
    
        public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
        public Handler(Callback callback, boolean async) {
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
        public Handler() {
            this(null, false);
        }

    上面列出了 Handler 的一些成员变量:

    mLooper:线程的消息处理循环,注意:并不是每个线程都有消息处理循环,因此 Framework 中线程能够分为两种:有 Looper 的和无 Looper 的。为了方便 app 开发,Framework 提供了一个有 Looper 的 Thread 实现:HandlerThread。在前一篇《Thread的实现》中也提到了两种不同 Thread 的 run() 方法的差别。

    /**
     * Handy class for starting a new thread that has a looper. The looper can then be 
     * used to create handler classes. Note that start() must still be called.
     */
    public class HandlerThread extends Thread {
        Looper mLooper;
        /**
         * Call back method that can be explicitly overridden if needed to execute some
         * setup before Looper loops.
         */
        protected void onLooperPrepared() {
        }
    
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
    
        /**
         * This method returns the Looper associated with this thread. If this thread not been started
         * or for any reason is isAlive() returns false, this method will return null. If this thread 
         * has been started, this method will block until the looper has been initialized.  
         * @return The looper.
         */
        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) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    }

    这个 HandlerThread 与 Thread 相比,多了一个类型为 Looper 成员变量 mLooper,它是在 run() 函数中由 Looper::prepare() 创建的,并保存在 TLS 中:

    /** Initialize the current thread as a looper.
          * This gives you a chance to create handlers that then reference
          * this looper, before actually starting the loop. Be sure to call
          * {@link #loop()} after calling this method, and end it by calling
          * {@link #quit()}.
          */
        public static void prepare() {
            prepare(true);
        }
    
        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::myLooper() 获取创建的 Looper:

        /**
         * Return the Looper object associated with the current thread.  Returns
         * null if the calling thread is not associated with a Looper.
         */
        public static Looper myLooper() {
            return sThreadLocal.get();
        }

    最后通过 Looper::Loop() 方法执行消息处理循环:从 MessageQueue 中取出消息,并分发处理该消息,不断地循环这个过程。这种方法将在后面介绍。

    Handler 的成员变量 mQueue 是其成员变量 mLooper 的成员变量,这里仅仅是为了简化书写,单独拿出来作为 Handler 的成员变量;成员变量 mCallback 提供了还有一种使用Handler 的简便途径:仅仅需实现回调接口 Callback,而无需子类化Handler,以下会讲到的:

        /**
         * Callback interface you can use when instantiating a Handler to avoid
         * having to implement your own subclass of Handler.
         */
        public interface Callback {
            public boolean handleMessage(Message msg);
        }

    成员变量 mAsynchronous 是标识是否异步处理消息,假设是的话,通过该 Handler obtain 得到的消息都被强制设置为异步的。

    同是否有无 Looper 来区分 Thread 一样,Handler 的构造函数也分为自带 Looper 和外部 Looper 两大类:假设提供了 Looper,在消息会在该 Looper 中处理,否则消息就会在当前线程的 Looper 中处理,注意这里要确保当前线程一定有 Looper。全部的 UI thread 都是有 Looper 的,因为 view/widget 的实现中大量使用了消息,须要 UI thread 提供 Looper 来处理,能够參考view.java:

    view.java
    
        public boolean post(Runnable action) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                return attachInfo.mHandler.post(action);
            }
            // Assume that post will succeed later
            ViewRootImpl.getRunQueue().post(action);
            return true;
        }
    
    ViewRootImpl.java
    
        private void performTraversals() {
            ....
            // Execute enqueued actions on every traversal in case a detached view enqueued an action
            getRunQueue().executeActions(attachInfo.mHandler);
          ...
        }
    
        static RunQueue getRunQueue() {
            RunQueue rq = sRunQueues.get();
            if (rq != null) {
                return rq;
            }
            rq = new RunQueue();
            sRunQueues.set(rq);
            return rq;
        }
    
        /**
         * The run queue is used to enqueue pending work from Views when no Handler is
         * attached.  The work is executed during the next call to performTraversals on
         * the thread.
         * @hide
         */
        static final class RunQueue {
        ...
            void executeActions(Handler handler) {
                synchronized (mActions) {
                    final ArrayList<HandlerAction> actions = mActions;
                    final int count = actions.size();
    
                    for (int i = 0; i < count; i++) {
                        final HandlerAction handlerAction = actions.get(i);
                        handler.postDelayed(handlerAction.action, handlerAction.delay);
                    }
    
                    actions.clear();
                }
            }
        }

    从上面的代码能够看出,作为全部控件基类的 view 提供了 post 方法,用于向 UI Thread 发送消息,并在 UI Thread 的 Looper 中处理这些消息,而 UI Thread  一定有 Looper 这是由 ActivityThread 来保证的:

    public final class ActivityThread {
    ...
        final Looper mLooper = Looper.myLooper();
    }

    UI 操作须要向 UI 线程发送消息并在其 Looper 中处理这些消息。这就是为什么我们不能在非 UI 线程中更新 UI 的原因,在控件在非 UI 线程中构造 Handler 时,要么因为非 UI 线程没有 Looper 使得获取 myLooper 失败而抛出 RunTimeException,要么即便提供了 Looper,但这个 Looper 并不是 UI 线程的 Looper 而不能处理控件消息。为此在 ViewRootImpl 中有一个强制检測 UI 操作是否是在 UI 线程中处理的方法 checkThread():该方法中的 mThread 是在 ViewRootImpl 的构造函数中赋值的,它就是 UI 线程;该方法中的 Thread.currentThread() 是当前进行 UI 操作的线程,假设这个线程不是非 UI 线程就会抛出异常CalledFromWrongThreadException。

        void checkThread() {
            if (mThread != Thread.currentThread()) {
                throw new CalledFromWrongThreadException(
                        "Only the original thread that created a view hierarchy can touch its views.");
            }
        }

    假设改动《使用Thread异步下载图像》中演示样例,下载完图像 bitmap 之后,在 Thread 的 run() 函数中设置 ImageView 的图像为该 bitmap,即会抛出上面提到的异常:

    W/dalvikvm(796): threadid=11: thread exiting with uncaught exception (group=0x40a71930)
    E/AndroidRuntime(796): FATAL EXCEPTION: Thread-75
    E/AndroidRuntime(796): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    E/AndroidRuntime(796): 	at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4746)
    E/AndroidRuntime(796): 	at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:823)
    E/AndroidRuntime(796): 	at android.view.View.requestLayout(View.java:15473)
    E/AndroidRuntime(796): 	at android.view.View.requestLayout(View.java:15473)
    E/AndroidRuntime(796): 	at android.view.View.requestLayout(View.java:15473)
    E/AndroidRuntime(796): 	at android.view.View.requestLayout(View.java:15473)
    E/AndroidRuntime(796): 	at android.view.View.requestLayout(View.java:15473)
    E/AndroidRuntime(796): 	at android.widget.ImageView.setImageDrawable(ImageView.java:406)
    E/AndroidRuntime(796): 	at android.widget.ImageView.setImageBitmap(ImageView.java:421)
    E/AndroidRuntime(796): 	at com.example.thread01.MainActivity$2$1.run(MainActivity.java:80)

    Handler 的构造函数暂且介绍到这里,接下来介绍:handleMessage 和 dispatchMessage:

    /**
         * Subclasses must implement this to receive messages.
         */
        public void handleMessage(Message msg) {
        }
    
        /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }

    前面提到有两种方式来设置处理消息的代码:一种是设置 Callback 回调,一种是子类化 Handler。而子类化 Handler 其子类就要实现 handleMessage 来处理自己定义的消息,如前面的匿名子类演示样例一样。dispatchMessage 是在 Looper::Loop() 中被调用,即它是在线程的消息处理循环中被调用,这样就能让 Handler 不断地处理各种消息。在 dispatchMessage 的实现中能够看到,假设 Message 有自己的消息处理回调,那么就优先调用消息自己的消息处理回调:

        private static void handleCallback(Message message) {
            message.callback.run();
        }

    否则看Handler 是否有消息处理回调 mCallback,假设有且 mCallback 成功处理了这个消息就返回了,否则就调用 handleMessage(一般是子类的实现) 来处理消息。

    在分析 Looper::Loop() 这个关键函数之前,先来理一理 Thread,Looper,Handler,MessageQueue 的关系:Thread 须要有 Looper 才干处理消息(也就是说 Looper 是执行在 Thread 中),这是通过在自己定义 Thread 的 run() 函数中调用 Looper::prepare() 和 Looper::loop() 来实现,然后在 Looper::loop() 中不断地从 MessageQueue 获取由 Handler 投递到当中的 Message,并调用 Message 的成员变量 Handler 的 dispatchMessage 来处理消息。

    以下先来看看 Looper 的构造函数:

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

    Looper 的构造函数非常easy,创建MessageQueue,保存当前线程到 mThread 中。但它是私有的,仅仅能通过两个静态函数 prepare()/prepareMainLooper() 来调用,前面已经介绍了 prepare(),以下来介绍 prepareMainLooper():

    /**
         * Initialize the current thread as a looper, marking it as an
         * application's main looper. The main looper for your application
         * is created by the Android environment, so you should never need
         * to call this function yourself.  See also: {@link #prepare()}
         */
        public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }

    prepareMainLooper 是通过调用 prepare 实现的,只是传入的參数为 false,表示 main Looper 不同意中途被中止,创建之后将looper 保持在静态变量 sMainLooper 中。整个 Framework 框架仅仅有两个地方调用了 prepareMainLooper 方法:

    SystemServer.java 中的 ServerThread,ServerThread 的重要性就不用说了,绝大部分 Android Service 都是这个线程中初始化的。这个线程是在 Android 启动过程中的 init2() 方法启动的:

    public static final void init2() {
            Slog.i(TAG, "Entered the Android system server!");
            Thread thr = new ServerThread();
            thr.setName("android.server.ServerThread");
            thr.start();
        }
    class ServerThread extends Thread {
        @Override
        public void run() {
            ...
            Looper.prepareMainLooper();
            ...
            Looper.loop();
            Slog.d(TAG, "System ServerThread is exiting!");
        }
    }


    以及 ActivityThread.java 的 main() 方法:
    public static void main(String[] args) {
            ....
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            AsyncTask.init();
    
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

    ActivityThread 的重要性也不言而喻,它是 Activity 的主线程,也就是 UI 线程。注意这里的 AsyncTask.init() ,在后面介绍 AsyncTask 时会具体介绍的,这里仅仅提一下:AsyncTask 能够进行 UI 操作正是因为在这里调用了 init()。

    有了前面的铺垫,这下我们就能够来分析 Looper::Loop() 这个关键函数了:

    /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
        public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            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;
                }
    
                msg.target.dispatchMessage(msg);
    
                msg.recycle();
            }
        }

    loop() 的实现非常easy,一如前面一再说过的那样:不断地从 MessageQueue 中获取消息,分发消息,回收消息。从上面的代码能够看出,loop() 仅仅是一个不断循环作业的生产流水线,而 MessageQueue 则为它提供原材料 Message,让它去分发处理。至于 Handler 是怎么提交消息到 MessageQueue 中,MessageQueue 又是怎么管理消息的,且待下文分解。


  • 相关阅读:
    java简单计算器,只能鼠标点击数字
    可以掉落和滑动的星星
    servlet生成数字验证码
    web服务器底层-http请求与相应
    Codeforces 990D Graph And Its Complement 【构造】【性质】
    2018美团CodeM编程大赛 Round A Problem 2 下棋 【贪心】
    Codeforces 988F Rain and Umbrellas 【dp】
    Codeforces 988D Points and Powers of Two 【性质】【卡常】
    Codeforces 981D Bookshelves 【dp】【性质】
    POJ 2104 K-th Number 【主席树】【不带修改】
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4216387.html
Copyright © 2020-2023  润新知