天天看点

Handler消息机制源码学习记录

Handler消息机制源码学习记录

在开发中为了避免在主线程执行耗时任务而产生ANR,我们通常会把耗时任务放到子线程中其处理(网络请求,IO操作等),当子线程在处理完某件任务需要更新UI的时候(比如完成网络图片的读取)就要使用到Android中Handler消息传递机制了。

从上面的这一段描述中可以看出,消息机制其实就是不同线程之间的通信。

这里有个疑问就要抛出来了,为什么不能在子线程中更新UI?

Android的UI更新是单线程模型的,因为如果支持多线程更新View的话就会不可避免的产生线程同步和线程安全的问题,解决起来是比较繁琐的,所以Android就直接规定了只能Ui线程也就是Main线程中更新View。

这里记录一下ANR在那些情况下会被触发:

  • 在UI线程中被一个事件阻塞5秒中而不能继续响应下一个事件
  • BroadcastReceive超过10秒未响应

下面来创建一个Handler的使用实例:

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView)findViewById(R.id.textView);        

         mHandler = new Handler(){

            @Override
            public void handleMessage(Message msg) {

                int what = msg.what;
                textView.setText("收到从子线程发来的消息了==>"+what);

            }
        };



        new Thread(new Runnable() {
            @Override
            public void run() {

                //模拟执行耗时操作
                try {
                    Thread.sleep();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                Message message = mHandler.obtain();
                message.what = ;
                mHandler.sendMessage(message);


            }
        }).start();
  }
}
           

OK!接下来我们就通过上面的这段Handler的示例代码的执行顺序来进行我们的源码学习。

先贴一个生动形象的Handler消息机制原理图以助于大家理解 :

Handler消息机制源码学习记录

在正式开始学习Handler消息机制前先来一个小小的总结,为什么还没开始就要先总结呢?当然时为了防止在下面源码学习的过程中抓不到侧重点,所以在这里提前介绍一下Handler消息机制所需要依赖的类以及它们的作用:

  • Handler就不提了,肯定是重中之重
  • Message: Message对象用来存储我们需要通过Handler发送给Ui线程的数据
  • MessageQueue: 消息队列,通过Handler发送的Message对象会被放置到MessageQueue中,会以队列的形式提供插入和删除的操作
  • Looper: 轮询器,它的作用就是不断的轮询MessageQueue,如果有新的消息就交给Handler处理,如果轮询不到新的消息,就会处于阻塞状态

了解了以上这些,下面我们带着着重点开始学习源码:

首先我们线程10行的创建Handler开始。

点开Handler的构造方法,让我们看一下在创建Handler的时候它内部做了那些事情

Handler的构造方法:

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) == ) {
                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 that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
           

关注第11行代码:

mLooper = Looper.myLooper();
           

我们点进去看一下 Looper.myLooper()做的事情

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
           

咦,怎么只调用了一句 sThreadLocal.get() 就获取到Looper对象了呢?我好像没有创建Looper 对象呀!还有 sThreadLocal是什么东东啊?

Ok,下面让我们逐一解释上面所遇到的困惑:

Looper对象是谁创建的?

主线程(ActivityThread)被创建的时候,Looper对象就跟着被创建了,并调用了Looper.loop()方法开始轮询MessageQueue。因此当我们在主线程中创建Handler的时候是不需要手动调用Looper.prepare()和Looper.loop()方法的因为ActivityThread创建的时候已经被调用过了,但如果是在子线程中创建就一定要调用前面提到的这两个方法,不然会出现下面的错误:

Can't create handler inside thread that has not called Looper.prepare()
           

说了不算,下面我们来看一下ActivityThread的源码:

(推荐给大家一个可以查看安卓源码的网站:http://grepcode.com)

ActivityThread

public static void  main(String[] args) {
        SamplingProfilerIntegration.start();
        CloseGuard.setEnabled(false);
        Environment.initForCurrentUser();
        EventLogger.setReporter(new EventLoggingReporter());
        Security.addProvider(new AndroidKeyStoreProvider());

        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());

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

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();

        thread.attach(false);

        if (sMainThreadHandler == null) {

            sMainThreadHandler = thread.getHandler();

        }

        if (false) {
            Looper.myLooper().setMessageLogging(new

                    LogPrinter(Log.DEBUG, "ActivityThread"));

        }

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

    }

}
           

可以看到在第13行的时候调用了Looper.prepareMainLooper()方法,下面我们来看一下Looper中的prepareMainLooper()方法:

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
           

在prepare的方法中创建Looper对象并通过 sThreadLocal.set(new Looper(quitAllowed))将其设置给了sThreadLocal

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

这就说通了在Handler中调用 mLooper = Looper.myLooper();为什么能拿到Looper对象了,这里需要说明一下ThreadLocal是什么东东:

ThreadLocal使用来解决多线程程序的并发问题,ThreadLocal并不是一个Thread,而是Thread中的一个局部变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可独立改变自己的副本,而不会影响其他线程所对应的副本。

ThreadLocal是如何做到为一每一个线程维护变量的副本的呢?其实思路很简单,在ThreadLoacl类中有一个Map,用于存储每一个线程变量的副本,Map中元素的健为线程对象,而值对应线程变量的副本。

通过ThreadLocal的set方法存放在线程中的Looper对象是当前线程自己使用的对象,其他线程是不需要访问的,当然也访问不到。

在前面的Handler的构造方法中不知大伙有没有注意到这一句:

mQueue = mLooper.mQueue;
           

(⊙o⊙)? Looper中持有MessageQueue的引用,那么MessageQueue这个对象是在什么时候被创建的呢,我们跟着上面的步伐继续看一下Looper的构造方法:

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

好了,是不是很清楚了!MessageQueue对象是在Looper被创建的时候就也跟着被创建了。

看到这里Handler = new Handler()这一句代码所涉及到的源码已经分析的差不多了。

我们下面接着分析,线程中进行通过Handler发送消息的这几行代码:

获取一个Message对象:

Message message = mHandler.obtain();
           

下面通过一张图来看一下mHandler.obtain()都做了那些事情:

Handler消息机制源码学习记录

接着到了 mHandler.sendMessage(message):

sendMessage又做了什么呢:

(这里忽略了两个方法)

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;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
           

在这里又调用了一次msg.target = this进行Message和Handler的绑定,因为通过new Message()创建Message的时候其内部就不会进行绑定(建议使用Handler.obtain()的方式创建Message对象),此处就是为了防止出现前面所提的到的情况,以确保万无一失。

最后调用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("MessageQueue", e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when ==  || 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;
    }
           

在前面我们知道ActivityThread中已经调用过Looper.loop()进行不断的从MessageQueue中轮询消息,只不过在队列为空的时候会处于阻塞状态,那么loop()轮询到消息之后是怎么把Message交给Handler处理的呢?

下面我们从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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

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

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

            final long traceTag = me.mTraceTag;
            if (traceTag !=  && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != ) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }
           

在代码走到第14行的时候有可能会被阻塞住,继续往下看到第32行,当从消息队列中获取到Message对象时调用了msg.target.dispatchMessage(msg)方法,而msg.target就是前面代码中通过Handler.obtain()获取Message对象时和handler.sendMessage()内部Message和Handler的绑定,绑定之后Message内部就持有了能够处理当前Message的Handler的引用,这样Message就能找到处理事件的Handler并将消息回调给Handler进行处理。

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

到这里Handler的消息机制的源码就看的差不多了,下面进行一下总结:

1.首先Looper.prepare()在当前调用的线程中使用ThreadLocal保存一个Looper实例,然后该实例持有一个个MessageQueue对象,Looper.prepare()方法只能调用一次,所以MessageQueue在一个线程中只会有一个存在。

2.Looper.loop()方法会不端地从当前线程中的MessageQueue的实例中轮询消息,然后进行回调msg.target.dispatchMessage(msg)方法,message之所以能够将消息交给指定的Handler处理,是因为内部进行了message对象和Handler之间的绑定。

3.Handler的构造方法,也就是当我们new一个Handler的时候Handler会依次从当前的线程中取出已经保存在ThreadLocal中的Looper对象,并从Looper对象中取出MessageQueue。

4.Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。这里也进行了一次Message对象和当前发送消息的Handler之间的绑定。当使用new Message()获取Message的时候,上面提到的在Handler.obtian()方法中的绑定就没办法执行到了,因此需要在发送消息的时候再进行一次的绑定,以确保万无一失。

5.为什么我们可以不调用Looper.prepare()和Looper.loop()方法就能直接使用呢?原因是再程序启动的时候在ActiivtyThread的main方法中就已经帮我们调用过这两个方法了,因此可以直接使用,如果在子线程中使用的话,还是需要老老实实的调用上面的那两个方法了。

6.是Message指定的到底应该是那个Handler来处理消息,依赖于前面所提到的绑定

7.其实Handler不仅可以更新UI,你完全可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。

今天5.20一个悲伤的日子……

Handler消息机制源码学习记录