安卓handler线程通信以及源码学习

安卓是单线程模型,就是说更新UI的操作只能在主线程中进行,但在开发一款应用时,只用一条主线程是不行的,因为一些耗时操作在主线程中进行时,就会造成线程阻塞,用户体验不好,所以当我们需要做一些耗时操作,比如网络请求、图片加载等,就需要开启一个子线程,在子线程中进行这些耗时操作。既然开启一个子线程,那么这其中就涉及到了线程之间的通信,今天就通过这篇文章记录一下通过handler进行线程之间的通信。

Handler通信机制是通过Handler、Looper、MessageQueue、Message这几个类建立起来的,下面我们通过这几个类来展开对Handler通信机制的学习。

下面是我对这几个类的一个认识理解和概括:

1、Handler,负责线程间消息的发送和接收处理;
2、Looper,负责创建消息队列(MessageQueue),并循环读取消息队列中的消息;
3、MessageQueue,负责存放由Handler发送的消息;
4、Message,负责对需要通信的消息进行封装。

下面我通过一个实现倒计时的demo来加深对上面四个类的认识以及应用。先贴上布局以及其代码:


布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="START"
        android:id="@+id/start_btn"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textColor="#000000"
        android:textSize="32sp"
        android:id="@+id/time_text"/>

</RelativeLayout>

布局里面就一个button和一个text view,点击button,text view每一秒刷新一次,实现倒计时的效果。
下面先贴上MainActivity的代码:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button mStartBtn;
    private TextView mTimeText;

    private static final int TIME_START_ACTION = 1;
    private static int TIME = 60;
    //创建一个Handler对象并实例化  重写handleMessage()方法对消息进行处理
    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case TIME_START_ACTION:
                    mTimeText.setText(String.valueOf(msg.obj));
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
      
        //初始化控件
        initView();
    }

    private void initView() {
        mStartBtn = findViewById(R.id.start_btn);
        mTimeText = findViewById(R.id.time_text);

        mStartBtn.setOnClickListener(this);
        mTimeText.setText(String.valueOf(TIME));
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.start_btn:
                //开启一个子线程,传入一个Runnable对象,重写run方法执行耗时操作
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                          //循环发送消息
                          for (int i = 0;i<60;i++){
                              //通过Handler.sendMessage()发送消息
                              mHandler.sendEmptyMessage(TIME_START_ACTION);
                              try {
                                  //线程睡眠1s
                                  Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          }
                    }
                }).start();
                break;
        }
    }
}

从代码中可以看出,在按钮的点击事件中创建一个子线程,在子线程中让子线程每睡眠1s发送一次消息,一次达到倒计时的效果。发送消息我调用的sendEmptyMessage(int what)方法。这里概括一下发送消息的几种方法:

1、第一类:send方案
sendEmptyMessage(int what)——发送一个空消息,传入一个int类型标识符what,因为消息队列中可以存放多条消息,所以发送消息的时候传入标识符,handler在处理消息的时候靠这个来区分并作出不同的处理;
sendEmptyMessageDelayed(int what, long delayMillis)——发送一个延时空消息,从方法名也能大概猜出这个方法能实现的功能了,其中传入了两个参数,第一个就不说了,和上面的都一样,第二个参数表示延时多少毫秒后发送消息;
sendEmptyMessageAtTime(int what, long uptimeMillis)——在固定的时间发送一个消息,其中第二个参数类型是long型,所以需要将设置的时间转换成long型的毫秒值;
以上是发送一个空消息,消息中不包含Message对象,handler接收到消息后,知道了在某个子线程中发生了这个事件,并随之做出相应的操作。
sendMessage(Message msg)——发送一个消息,传入一个Message对象,将需要传送的信息封装在Message这个类里;
sendMessageDelayed(Message msg, long delayMillis)——发送一个延时消息,和上面发送一个空消息差不多,区别在于这个方法需要传入一个Message对象;
sendMessageAtFrontOfQueue(Message msg)——发送的消息放在队列的最前面;
sendMessageAtTime(Message msg, long uptimeMillis)——在固定的事件发送一个消息;
上面几种方法和发送一个空消息的区别在于,sendMessage()方法需要传入一个Message对象。下面通过一段代码看看这个方法的使用:

Message message = new Message();//实例化一个Message对象
message.obj = i;//填充数据
message.what = TIME_START_ACTION;//设置标识
mHandler.sendMessage(message);//发送消息

在代码中填充数据我是通过message.obj来赋值的,此外还可以通过message.arg来填充一些int类型的数据,也可以用message.setData(Bundle data)方法,传入一个Bundle对象,使用Bundle传递数据就跟fragment之间传递数据时一样的。
此时,有了Message对象,也可以通过message.setTarget(Handler target)方法设置一个目标Handler(消息Message时通过Handler发送和接收的,所以每一个Message总会有一个目标Handler与之对应),绑定了目标,就可以通过方法message.sendToTarget()方法发送消息。

上面提到的send方案中,发送消息时我是直接new了一个Message对象,一般不推荐这样,在平时开发中,多用obtianMessage()方法从消息池中获取一个消息对象,因为直接new的话会为Meaage分配新的内存,性能优化时是不推崇的,所以在开发中一般这样来发送一个消息:

mHandler.obtainMessage(TIME_START_ACTION,i).sendToTarget();

以上就是对send方案的一个介绍,下面我们看看另外一种方案:

2、第二类:post方案
先贴上点击事件下使用post发送一个消息的代码:

new Thread(() -> {
        for (int i = 60;i>0;i--){
                try {
                       Thread.sleep(1000);
                    } catch (InterruptedException e) {
                       e.printStackTrace();
                    }
                mHandler.post(() -> mTimeText.setText(String.valueOf(--TIME)));
        }
}).start();

使用post发送消息,其中又传入了一个Runnable对象,重写了run方法,在run方法中可以直接进行ui的相关的操作,使用lambda表达式,一行代码就搞定,第二种方案相较于第一种非常的简洁。
以上两种方案都可以发送消息,实现线程之间的通信。


下面我们通过Handler源码看看这几个类之间的关系,看看底层消息通信的实现。
消息Message的发送和接收处理都是通过Handler来实现的,我们就从最简单的sendMessage(Message msg)看看Handler时如何将Message存放到MessageQueue的:

    /**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

注释已经有了简要说明,这个方法是推送一条消息到MessageQueue中所有待定消息的最下面,这也就意味着不出意外最后进来的消息将最后被取出。传进来的消息是通过附属在当前线程的handler调用handlerMessage(Message msg)方法来接收处理的。
方法下面返回了执行sendMessageDelayed(msg,0)方法后返回的值,所以sendMessage()方法也可以看成是延迟时间为0的一个sendMessageDelayed()方法,再往下走,看看sendMessageDelayed()方法做了啥操作:

    /**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

在这个方法里面,对传进来的long型时间参数做了个判断,当时间小于0时,重新赋值为0,然后返回了调用sendMessageAtTime()方法返回的值。
接着往下看sendMessageAtTime()方法:

    /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    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);
    }

在这个方法里面,获取了MessageQueue对象,并作了一个非空判断,最后返回调用enqueueMessage()方法返回的值,将MessageQueue对象、Message对象,以及一个时值传入进去,下面看看enqueueMessage()中对这三个值做了什么操作:

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

因为Message时通过Handler来发送和接收的,所以Message一定会有一个目标Handler,就是在这里通过msg.target = this;绑定的,最后MessageQueue对象调用了自身的enqueueMessage()方法,将消息对象传了进去,下面是enqueueMessage()方法:

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            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;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

这个方法里面主要就是将Message放到MessageQueue的操作了。这里总结一下Handler发送消息Message到消息队列MessageQueue的过程:

Handler通过sendMessage()方法将Message发送给MessageQueue,然后MessageQueue将Message存放到底部去(若无特别指定)。


以上是Handler发送消息的过程,下面来看看Handler接收个处理消息的过程,首先是Looper.prepare()方法创建Looper对象和Looper.loop()方法循环消息队列:

     /** 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));
    }

通过注释可以知道,prepare()方法是给当前线程初始化一个循环类,并且指出要确保在循环开始前调用方法loop(),结束后调用方法quit()。
代码中,prepare()方法又调用了prepare()方法,并且传入一个boolean类型的参数,从参数名称可以看出这个参数决定是否允许退出,这里给定一个恒值true,具体意义先放一放。
下面这个prepare()方法中首先做了一个非空判断,这里面有一个sThreadLocal,找到它后有一个介绍:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

sThreadLocal.get()在没有调用过prepare()方法时会返回一个null,所以当从sThreadLocal这里面取出的value不为空时,说明prepare()方法已经调用过了,这时就会抛出一个异常Only one Looper may be created per thread(每个线程只能创建一个Looper),从这里我们可以得知线程和Looper的关系:每个线程只能有一个Looper,在一个线程中,Looper只能调用一次prepare()方法
接着往下看,当初次调用prepare()方法进来,执行了sThreadLocal.set()方法,新建了一个Looper对象,并传了进去,下面看看Looper中的构造方法做了啥操作。

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

在构造方法中可以看到,在这里创建了一个消息队列MessageQueue,这也就是之前概念提到的Looper创建一个MessageQueue,此外将当前线程赋值给了一个全局变量。
最开始调用方法prepare()时就传入的一个boolean类型的参数,一直没有用到,并且在创建消息队列的时候传了进去,点进去可以看到,在MessagQueue的构造方法中,将这个值又赋值给了一个全局变量。

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;//赋值给了一个全局变量
        mPtr = nativeInit();
}

这个全局变量在quit()方法中的一个判断语句中用到,当是false的时候抛出异常Main thread not allowed to quit.,表明主线程不允许执行退出操作,从这儿就能看出,创建子线程时,这个参数为true,表明可以执行退出操作,主线程在创建时,这个参数时false,不能执行退出操作,这也表明,安卓系统最少会有一条主线程。

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

小结:通过查看prepare()方法的源码,我们知道这个方法主要创建了Looper对象,Looper又创建了MessageQueue对象。

下面来看Looper.loop()方法的源码:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
1    public static void loop() {
2       final Looper me = myLooper();
3        if (me == null) {
4            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
5        }
6        final MessageQueue queue = me.mQueue;
7
8        // Make sure the identity of this thread is that of the local process,
9        // and keep track of what that identity token actually is.
10        Binder.clearCallingIdentity();
11        final long ident = Binder.clearCallingIdentity();
12
13       // Allow overriding a threshold with a system prop. e.g.
14        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
15        final int thresholdOverride =
16                SystemProperties.getInt("log.looper."
17                       + Process.myUid() + "."
18                        + Thread.currentThread().getName()
19                        + ".slow", 0);
20
21        boolean slowDeliveryDetected = false;
22
23        for (;;) {
24            Message msg = queue.next(); // might block
25            if (msg == null) {
26                // No message indicates that the message queue is quitting.
27                return;
28            }
29
30            // This must be in a local variable, in case a UI event sets the logger
31            final Printer logging = me.mLogging;
32            if (logging != null) {
33                logging.println(">>>>> Dispatching to " + msg.target + " " +
34                        msg.callback + ": " + msg.what);
35            }
36
37            final long traceTag = me.mTraceTag;
38            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
39            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
40            if (thresholdOverride > 0) {
41                slowDispatchThresholdMs = thresholdOverride;
42                slowDeliveryThresholdMs = thresholdOverride;
43            }
44            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
45            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
46
47            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
48            final boolean needEndTime = logSlowDispatch;
49
50            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
51                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
52            }
53
54            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
55            final long dispatchEnd;
56            try {
57                msg.target.dispatchMessage(msg);
58                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
59            } finally {
60                if (traceTag != 0) {
61                    Trace.traceEnd(traceTag);
62                }
63            }
64            if (logSlowDelivery) {
65                if (slowDeliveryDetected) {
66                    if ((dispatchStart - msg.when) <= 10) {
67                        Slog.w(TAG, "Drained");
68                        slowDeliveryDetected = false;
69                    }
70                } else {
71                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
72                            msg)) {
73                        // Once we write a slow delivery log, suppress until the queue drains.
74                        slowDeliveryDetected = true;
75                    }
76                }
77            }
78            if (logSlowDispatch) {
79                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
80            }
81
82            if (logging != null) {
83                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
84            }
85
86            // Make sure that during the course of dispatching the
87            // identity of the thread wasn't corrupted.
88            final long newIdent = Binder.clearCallingIdentity();
89            if (ident != newIdent) {
90                Log.wtf(TAG, "Thread identity changed from 0x"
91                        + Long.toHexString(ident) + " to 0x"
92                        + Long.toHexString(newIdent) + " while dispatching to "
93                        + msg.target.getClass().getName() + " "
94                        + msg.callback + " what=" + msg.what);
95            }
96
97            msg.recycleUnchecked();
98        }
99    }

在loop()方法上面有段注释,要确保在循环结束调用quit()方法。
首先通过myLooper()方法获取了一个Looper对象,其实myLooper()方法中就是执行了sThreadLocal.get();这个操作,跟prepare()方法下一样,获取一个Looper对象。
3-5行是一个判断语句,如果获取到的Looper对象为null,会抛出异常,提示没有Looper对象,在Looper调用方法loop()之前要先调用方法prepare()方法。
第6行是获取一个MessageQueue对象,在之前prepare()方法下创建的Looper下,其构造方法中创建了一个MessageQueue对象,并将这个对象赋值给了全局变量mQueue,所以在这里,直接通过me.mQueue获取到这个MessageQueue对象。
7-22行看不大懂,跟我想要了解的业务逻辑貌似关联不大,这里先不去探究。
23-98行是做的一个无限循环,for(;;)这种循环方式跟while(true)一样,没有外力干扰的情况下就会一直循环下去,地球不爆炸,它就不放假。
第24行就是读取消息队列中的消息,并且提示有可能造成阻塞,在主线程中也是调用的这个方法进行一个无限循环,读取队列中的消息,也有可能在去除消息的时候造成阻塞,在子线程中影响不大,在主线程中,如果超过5s未响应,就会出现ANR。这就是为什么说耗时操作在子线程中进行。
25-28是一个判断语句,当取出的消息为null时,表明MessageQueue已经退出了,此时就跳出循环。
30-55行也看不大懂,跟业务逻辑关联也不大,这里先不做探究。
第57行我又看到了对取出的消息msg的处理,msg.target.dispatchMessage(msg),在这行代码中,首先Message类的对象msg通过msg.target获取到它的目标Handler,记得上面在讲到handler发送消息的send方案中有一种是hander.sendMessage(Message msg),在这个方案中需要传入一个Message对象,在创建Message对象后,可以通过message.setTarget(Handler handler)方法给Message设置目标Handler,在这个方法中就是将传进来的handler赋值给了全局变量target,所以这里就直接通过msg.target获取到它的目标Handler,然后调用方法dispatchMessage()将消息发出去。
中间的代码好像跟业务逻辑关系也不大,这里也不做探究了。
最后,第97行代码,执行了msg.recycleUnchecked()这个操作,当消息从队列中取出后,释放它在队列中所占的资源,以便其他消息能够进入队列复用资源。

小结:Looper.loop()方法主要是循环读取MessageQueue中的Message,并由与Message绑定的Handler去发送消息。
下面我们就看看Handler是如何通过dispatchMessage(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);
        }
    }

在这个方法中,只有一个判断语句,不同的条件执行不同的操作。在这里面msg.callback先不去看,后面配合post方案一起讲,else里面,对mCallback这个全局变量进行了一个判断,那我先去看看这个全局变量mCallback是什么东西:

public Handler(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;
}
public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
}

在上面两个Handler构造方法中,对mCallback进行了声明,但我们一般创建Handler对象都没用这两个构造方法,而是用的这个:

Handler构造方法

为了方便阅读,我截图了这个构造方法,从图中可以看出,我们一般创建Handler对象时啥都没传,那么进入到这个方法,callback就为null,所以此时Handler发送消息就会调用dispatchMessage(Message msg)下的handleMessage(msg)方法。
接着,我们来看看handleMessage(msg)方法:

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

这个方法并没有具体的方法,其中只包含一个Message对象,但是注释说明了,子类必须实现这个方法才能接收到消息,所以这就有了我们在使用Hander的时候,在主线程中创建Hnadler对象的时候要重写这个方法,然后在在这个方法中对msg进行处理,这个msg就是从子线程传过来的消息。

以上就是对Handler通信的源码学习,在这里做个总结:

1、线程间Message的发送和接收处理是在Handler中完成的;
2、Message存放于MessageQueue中,MessageQueue由Looper调用prepare()方法创建,并通过loop()方法循环读取;
3、一个Thread只能有一个Looper,一个Looper只能由一个MessageQueue,一个MessageQueue中可以存放多条Message。


上面再Handler发送消息的时候说到另外一种更加简洁的方案,就是通过post发送消息,我们就通过源码来看看post方案为什么会更加简洁:

    /**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

注释中说到,因为传入的Runnable对象被添加到了MessageQueue中,所以,Runnable会运行在当前handler附属的线程上,也就是说,当前Handler在哪个线程创建的,那么Runnable中的操作将在那个线程中运行。从代码可以看到post底层的实现方法其实也是sendMessage,不过这里使用getPostMessage(Runnable r)方法传入了一个Message对象:

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
}

这个方法下面主要是从消息池里面获取一个消息对象,然后通过m.callback = r;将Runnable对象设置给Message对象,记得在Handler处理消息的时候有一个判断是这样的:

if (msg.callback != null) {
       handleCallback(msg);
}

所以此时Message对象里的callback不为空了,那么在处理消息的时候将走handleCallback(msg);这个方法,并且把Message对象传入了这个方法里面,我们看看这个方法里是怎么对Message做处理的:

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

看到这里,我们就已经可以得知,post方案代码更加简洁的原因:
因为post方案处理消息是通过Runnable中的run方法来处理的,而Runnable又是在当前Handler附属的Thread中运行的,所以当我们的Handler在主线程中创建,在子线程中调用post(Runnable r)方法发送消息时,可以直接在run方法下对UI进行操作。
最后,众所周知,如果在子线程中接收消息,则需要通过Looper.prepare()方法来创建Looper对象,然后通过Looper.loop()来对消息队列进行循环读取,但这些操作在主线程中都是不需要的,这是因为,安卓系统是单线程模型,在应用启动时候会创建一条主线程,此时系统会自动完成Looper的相关操作,下面贴上主线创建的源码:

public static void main(String[] args) {//安卓程序运行入口
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();//创建一个Looper对象

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

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

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();//开启循环

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

好了,以上就是本人对Handler的学习和理解,在此记录一下。若读者朋友发现文中有描述不当的地方,还请指正,万分感谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。