从源码的角度分析Android消息处理机制
2016-03-08 11:41
507 查看
我们都知道Android的UI线程是非安全的,如果要在子线程中更新UI界面,出现会报错”Only the original thread that created a view hierarchy can touch its views.“。我们通常的做法是在UI线程中创建一个Handler,然后在子线程中通过sendMessage来更新UI。为什么这样做就可以呢?今天我们就通过源码来解释下。
Android 的消息机制主要是通过Handler,Looper,Message,MessageQueue这几个类来实现的。Handler是发送消息(Message)和处理消息的。MessageQueue表示一个消息队列,负责消息入队和出队;Looper类用于创建消息循环。
下面我们通过创建一个Handler来开始我们今天的主题。
我们在onCreate方法中创建一个线程,并在该线程中new 了一个Handler。运行结果如下:
提示的错误信息为 Can’t create handler inside thread that has not called Looper.prepare() 。这句话是什么意思呢?就是说不能在子线程中创建Handler而不调用Looper.prepare()。我们进Handler的构造函数中看看为什么会报这样的错误。
在第13行抛出了异常。是因为mLooper 为null导致的。我们去Looper.myLooper()方法中看看,
这个方法很简单,就是从sThreadLocal中取出一个Looper对象。那么我们可以猜到应该是在Looper.prepare()中为sThreadLocal设置了Looper对象。看下Looper.prepare()方法:
可以看到,如果已经有一个Looper对象,你再调用prepare()方法会抛出异常,这表明一个线程只能有一个Looper对象。如果没有则new 一个Looper对象。
看到这里大家一定觉得很奇怪,为什么我平时写的更新UI界面的Handler时不需要调用Looper.prepare()呢?其实不然,那是因为在主线程ActivityThread中已经做好了。
我们看第22行调用了Looper.prepareMainLooper(),再看一下prepareMainLooper具体做了哪些工作:
可以看到在第2行调用了prepare(),不难推测在该方法中会创建Looper对象。
这就保证了,我们在UI线程中创建Handler不需要再调用Looper.prepare()了。
下面我们看看Handler是怎么发生消息的。我们平时都类似这样写的:
在子线程中发送Message,在Handler中通过handleMessage来更新UI。现在我们从源码看看Handler是怎么发送消息,消息又是如何回到Handler中的。
Handler中有很多发送消息的方法,其中除了sendMessageAtFrontOfQueue()方法之外,其它的发送消息方法最终都会辗转调用到sendMessageAtTime()方法中,这个方法的源码如下所示:
其中的两个参数分别是待发送的消息以及发送消息的时间。queue就是消息队列。如果 queue 不为空就进行消息的入队操作,enqueueMessage源码如下:
这个方法很简单,将msg.target表示未Handler,然后调用queue.enqueueMessage(msg, uptimeMillis)。源码如下:
入队操作中使用mMessages表示当前待处理的消息,29-35行表示根据消息的处理时间Message.when来确定消息插入到队列中的具体位置,从而保证队首的消息是下一个要处理的。
入队讲完了,我们接着讲消息的出队。出队是通过Looper.loop()来循环实现的。源码如下:
从代码的第13行就进入了一个死循环,通过queue.next()来出去出队的消息。我们进到MessageQueue.next()方法看看消息时怎么出队的:
代码14-29解读:
首先 , 定义一个Message 变量(msg)指向当前待处理的消息(mMessages)。
如果当前待处理的消息mMessages不为空,那么进入第17行,判断是否已到发送时间,如果时间到了,那么让待处理的消息(mMessages)指向下一个msg。
msg.next = null表示将当前的消息从消息队列中移除,因为msg.next = null表示该消息不再指向其他的消息。
最后返回待处理的消息。
接着看Looper.loop()中第27行代码,每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,那么msg.target指的是什么呢?msg.target就是Handler,在之前的入队操作中可以看出来( msg.target = this)。
dispatchMessage源码如下:
第2行中的msg.callback其实是一个Runnable对象。一般我们调用Handler.post(runnable)时会赋值的。源码如下:
看看getPostMessage(r)都做了什么?
第3行为m.callback 赋值为一个Runnable 对象。
回到dispatchMessage方法中。其中第5行的mCallback,是在Handler的内部接口CallBack中实现的。最后执行 handleMessage(msg);
源码:
可以看出这个函数是空的,这就要求我们自己在创建Handler的时候来实现它。这就是为什么handleMessage()方法中可以获取到之前发送的消息。
Android 的消息机制主要是通过Handler,Looper,Message,MessageQueue这几个类来实现的。Handler是发送消息(Message)和处理消息的。MessageQueue表示一个消息队列,负责消息入队和出队;Looper类用于创建消息循环。
下面我们通过创建一个Handler来开始我们今天的主题。
protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.test_handler_layout); new Thread(){ public void run() { Handler mHandler = new Handler(); }; }.start(); }
我们在onCreate方法中创建一个线程,并在该线程中new 了一个Handler。运行结果如下:
提示的错误信息为 Can’t create handler inside thread that has not called Looper.prepare() 。这句话是什么意思呢?就是说不能在子线程中创建Handler而不调用Looper.prepare()。我们进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) == 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 that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
在第13行抛出了异常。是因为mLooper 为null导致的。我们去Looper.myLooper()方法中看看,
public static Looper myLooper() { return sThreadLocal.get(); }
这个方法很简单,就是从sThreadLocal中取出一个Looper对象。那么我们可以猜到应该是在Looper.prepare()中为sThreadLocal设置了Looper对象。看下Looper.prepare()方法:
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对象,你再调用prepare()方法会抛出异常,这表明一个线程只能有一个Looper对象。如果没有则new 一个Looper对象。
看到这里大家一定觉得很奇怪,为什么我平时写的更新UI界面的Handler时不需要调用Looper.prepare()呢?其实不然,那是因为在主线程ActivityThread中已经做好了。
public static void main(String[] args) { SamplingProfilerIntegration.start(); // 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()); Security.addProvider(new AndroidKeyStoreProvider()); // 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(); 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"); }
我们看第22行调用了Looper.prepareMainLooper(),再看一下prepareMainLooper具体做了哪些工作:
public static void prepareMainLooper() { prepare(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; }
可以看到在第2行调用了prepare(),不难推测在该方法中会创建Looper对象。
public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }
这就保证了,我们在UI线程中创建Handler不需要再调用Looper.prepare()了。
下面我们看看Handler是怎么发生消息的。我们平时都类似这样写的:
private Handler mHandler = new Handler(){ public void handleMessage(Message msg) { if(msg.what == 11){ bm = (Bitmap)msg.obj; mImageView.setImageBitmap(bm); } }; }; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.test_handler_layout); new Thread(){ public void run() { Message msg = Message.obtain(); msg.what = 11; msg.obj = getBitMapFromURL(url); mHandler.sendMessage(msg); }; }.start();
在子线程中发送Message,在Handler中通过handleMessage来更新UI。现在我们从源码看看Handler是怎么发送消息,消息又是如何回到Handler中的。
Handler中有很多发送消息的方法,其中除了sendMessageAtFrontOfQueue()方法之外,其它的发送消息方法最终都会辗转调用到sendMessageAtTime()方法中,这个方法的源码如下所示:
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); }
其中的两个参数分别是待发送的消息以及发送消息的时间。queue就是消息队列。如果 queue 不为空就进行消息的入队操作,enqueueMessage源码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
这个方法很简单,将msg.target表示未Handler,然后调用queue.enqueueMessage(msg, uptimeMillis)。源码如下:
final boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } if (msg.target == null && !mQuitAllowed) { throw new RuntimeException("Main thread not allowed to quit"); } final boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; } else if (msg.target == null) { mQuiting = true; } msg.when = when; Message p = mMessages; if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; // new head, might need to wake up } else { Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; needWake = false; // still waiting on head, no need to wake up } } if (needWake) { nativeWake(mPtr); } return true; }
入队操作中使用mMessages表示当前待处理的消息,29-35行表示根据消息的处理时间Message.when来确定消息插入到队列中的具体位置,从而保证队首的消息是下一个要处理的。
入队讲完了,我们接着讲消息的出队。出队是通过Looper.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 Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); 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.recycle(); } }
从代码的第13行就进入了一个死循环,通过queue.next()来出去出队的消息。我们进到MessageQueue.next()方法看看消息时怎么出队的:
final Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); msg.markInUse(); return msg; } else { nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; } // If first time, then get the number of idlers to run. if (pendingIdleHandlerCount < 0) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount == 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
代码14-29解读:
首先 , 定义一个Message 变量(msg)指向当前待处理的消息(mMessages)。
如果当前待处理的消息mMessages不为空,那么进入第17行,判断是否已到发送时间,如果时间到了,那么让待处理的消息(mMessages)指向下一个msg。
msg.next = null表示将当前的消息从消息队列中移除,因为msg.next = null表示该消息不再指向其他的消息。
最后返回待处理的消息。
接着看Looper.loop()中第27行代码,每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,那么msg.target指的是什么呢?msg.target就是Handler,在之前的入队操作中可以看出来( msg.target = this)。
dispatchMessage源码如下:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
第2行中的msg.callback其实是一个Runnable对象。一般我们调用Handler.post(runnable)时会赋值的。源码如下:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
看看getPostMessage(r)都做了什么?
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
第3行为m.callback 赋值为一个Runnable 对象。
回到dispatchMessage方法中。其中第5行的mCallback,是在Handler的内部接口CallBack中实现的。最后执行 handleMessage(msg);
源码:
public void handleMessage(Message msg) { }
可以看出这个函数是空的,这就要求我们自己在创建Handler的时候来实现它。这就是为什么handleMessage()方法中可以获取到之前发送的消息。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories