Android消息机制分析
来源:互联网 发布:htc g16软件下载 编辑:程序博客网 时间:2024/06/05 08:09
转载请注明出处:http://blog.csdn.net/fishle123/article/details/47864923
对Android开发有一定了解的人都知道,Android应用程序是消息驱动的。Android应用程序的消息机制是围绕消息队列来实现的,具体的,主要通过Handler、MessageQueue、Looper三个类来实现。其中Handler用来发送和处理消息;MessageQueue表示一个消息队列,负责入队和出队消息;Looper类用于创建消息循环。本文结合源码来介绍Android的消息机制包括:消息循环的创建、消息的发送和处理。
每个Android应用程序都一个主线程ActivityThread,每个主线程都有一个消息循环。先看一下ActivityThread的main方法:
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; }
可以看到在第调用prepare,然后调用setMainLooper设置主线程的Looper。不难推测,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()); }
prepare()里面先检查是否已经创建了Looper,如果没有,则创建一个并保存到sThreadLocal中,反之,如果已经创建了一个,则会抛出异常。通过这里,我们也可以发现,prepare只能调用一次,每个线程只有一个Looper。那这里已经创建了Looper,那么第36行的Looper.loop()又是做什么的呢?继续看源码:
public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } 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(); while (true) { Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } long wallStart = 0; long threadStart = 0; // 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); wallStart = SystemClock.currentTimeMicro(); threadStart = SystemClock.currentThreadTimeMicro(); } msg.target.dispatchMessage(msg); if (logging != null) { long wallTime = SystemClock.currentTimeMicro() - wallStart; long threadTime = SystemClock.currentThreadTimeMicro() - threadStart; logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); if (logging instanceof Profiler) { ((Profiler) logging).profile(msg, wallStart, wallTime, threadStart, threadTime); } } // 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(); } } }
可以看到,在loop中会进入一个死循环,每次循环的开始调用MessageQueu.next()方法从消息队列中取出一个Message,然后调用 msg.target.dispatchMessage(msg)交给message的接受者处理。这里的msg.target就是message的接受者,其实就是一个Handler。如果消息队列为空,那么MessageQueue .next()会阻塞,直到有新的消息达到。到这里,就可以明白调用Looper.loop后,线程就进入了消息循环等待message到来并交给message对应的Handler来处理消息。正常情况下,一旦进入消息循环就不会退出,除非MessageMessage.target=null,见第16行代码。
到此为止,我们就知道如果要给线程创建一个消息循环,可以按如下方式来定义:
class LooperThread extends Thread { public void run() { Looper.prepare(); ...... Looper.loop(); } }
上面提到在Looper中会通过msg.target.dispatchMessage(msg)将msg分派到对应的Handler来出来,这里看一下dispatchMessage的实现:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
在dispatchMessage中先检查在Message中是否定义了消息的callback,如果有,优先调用Message的callback来处理消息;反之,如果Handler的mCallBack不为null,则调用mCallBack的handleMessage来处理,当MCallBack也为null的时候,才调用Handdler的handleMessage来处理。这里可以看出,Message有三种方式来实现它的处理逻辑:
1)在Message.callback中实现,这里的callback是一个Runnable对象;
2)在Handler的内部接口CallBack中实现;
3)重写Handler的handleMessage来实现。
阅读Handler的源码,可以发现当调用handler.post(Runnable)的时候,就是通过第1种方式来实现:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private final Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
通常我们会通过第3种方式来定义自己的Handler,如在主线程:
Handler mHandler=new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); ......//你的处理逻辑 } };
这里看一下Handler的构造函数(还有其他几种构造函数):
public Handler() { 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 = null; }
这里可以看到,创建Handler的时候,会在保存当前线程的Looper及Looper对应的MessageQueue。这样发送消息时Handler就能将Message发送到当前线程的消息循环中。当然,我们也可以指定Looper,这样就可以给另一个线程发送消息,如在另一个线程中给主线程发送消息。这里也可以看出,定义Handler之前一定要先调用Looper.prepare()来创建一个Looper,主线程中会在ActivityThread的main方法中创建,不需要我们手动调用。但是在子线程中,一定要先调用Looper.prepare()才能创建 Handler,否则会抛出一个异常,如第78行所示。
Handler常见的用法之一就是在子线程中更新UI,我们可以通过handler.post(Runnable)让runnable在主线程中执行。其中Activity的runOnUIThread就是利用handler来实现的,view.post(Runnable)也是相似的实现。
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
当调用handler.sendMessage()发送消息时,最终会调用Handler.sendMessageAtTime,源码如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }
在这里可以看出,发送消息的时候,将Message添加到队列中,并且会将Message的接受者设置为发送Message的handler,如代码第6行所示。前面有提到,在loop中会调用MessageQueue.next()来取消息。这里先看一下消息的入队操作:
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; //Log.d("MessageQueue", "Enqueing: " + msg); 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; }
看代码的第29到35行代码,这里会根据消息的处理时间Message.when来确定消息插入到队列中的具体位置,从而保证队首的消息是下一个要处理的。在这里也可以发现,主线程的消息循环是不能退出的,如第6行所示。
前面有提到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; } }
在第3行,定义变量nextPollTimeoutMills,用来描述当消息队列中没有新的消息时,当前线程需要睡眠多长时间。当nextPollTimeoutMills等于-1时,表示无限制等待,直到有新的消息到达。在没有消息到达的时候会调用nativePollOnce来检查是否有新消息需要处理,nativePollOnce是一个native函数,主要是通过Linux中的poll函数来实现的。
当队列不为空时,在第17行先判断当前时间是否大于等于message的处理时间,如果是,则返回该message,反之,计算需要等待的时间,并更新nextPollTimeoutMills。
如果队列为空,则nextPollTimeoutMills的值设置为-1。在进入睡眠之前,先调用注册到消息队列中的空闲消息处理器IdleHandler对象的queueIdle函数,以便有机会在线程空闲时执行一些操作。可以通过MessageQueue的addIdleHandler和removeIdleHandler来注册和注销一个空闲消息处理器。
到此为止,已经分析完消息循环的创建,发送消息,以及消息的处理。
- Android消息机制分析
- Android消息机制分析
- android消息机制源码分析
- Android 消息机制源码分析
- Android消息机制源码分析
- Android 消息传递机制分析
- Android消息机制初步分析
- Android的消息机制分析
- Android消息机制源码分析
- Android 消息机制源码分析
- Android的消息机制分析
- Android消息循环机制分析
- Android消息机制源码分析
- Android消息机制底层分析
- Android消息机制源码分析
- Android消息机制源码分析
- Android 消息传递机制分析
- Android应用程序消息处理机制Handler分析
- 社交网络中潜在好友推荐算法研宄
- 黑马程序员11模拟实现银行业务调度系统
- 基于Linux C 的简单聊天程序
- poj185 炮兵阵地 状压DP
- HDU 3513 Human or Pig
- Android消息机制分析
- 人生需要拆穿(读书笔记)
- Minimum Depth of Binary Tree
- find命令的用法
- Netty5中使用LineBasedFrameDecoder解决TCP粘包问题
- 在Cocos2dx项目中嵌入百度广告Android SDK
- ECMAScript6学习笔记《三》-----“包含字符串的三种新方法”
- 完整的图片去噪代码(python)
- Go1.5正式版程序性能分析小积累,实验环境windows64