Android异步消息机制
Android的异步消息机制在Android系统中的重要性读者应该都很清楚,无论是在平时开发中,还是笔试、面试中,这方面的内容都是无法避免的。Android提供了Handler和Looper来满足线程间通信,而MessageQueue则是用来存放线程放入的消息。下面我们就结合源码分析一下这三者的关系。
(1)Handler的基本使用方式:
public class MainActivity extends AppCompatActivity { private MyTextView title; private static final String TAG = MainActivity.class.getSimpleName(); private static final int HANDLER1 = 1; /** * 在主线程中定义Handler,并实现对应的handleMessage方法 */ public static Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == HANDLER1) { Log.i(TAG, "接收到handler消息..."); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); title = (MyTextView) findViewById(R.id.title); title.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread() { @Override public void run() { // 在子线程中发送异步消息 mHandler.sendEmptyMessage(HANDLER1); } }.start(); } }); }}
相信上面的用法大家都很熟悉,但现在有个问题,如果想把Handler放到子线程中使用,是怎么样子的呢?下面我们看一段代码。
public class MainActivity extends AppCompatActivity { private MyTextView title; private static final String TAG = MainActivity.class.getSimpleName(); private static final int HANDLER1 = 1; /** * 在主线程中定义Handler,并实现对应的handleMessage方法 */ Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); title = (MyTextView) findViewById(R.id.title); title.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mHandler.sendEmptyMessage(HANDLER1); } }); new Thread() { @Override public void run() { // 在子线程中发送异步消息 mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == HANDLER1) { Log.i(TAG, "接收到handler消息..."); } } }; } }.start(); }}
点击按钮运行后会报下面的错。
E/AndroidRuntime: FATAL EXCEPTION: Thread-95 Process: com.eyck.androidsample, PID: 2552 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.(Handler.java:200) at android.os.Handler.(Handler.java:114) at com.eyck.androidsample.MainActivity$2$1.(MainActivity.java:0) at com.eyck.androidsample.MainActivity$2.run(MainActivity.java:41)
根据提示信息我们可以很清晰的看到,在初始化Handler对象之前需要先调用Looper.prepare()方法,那么我们就加上这句代码再执行一次。
new Thread() { @Override public void run() { // 在子线程中发送异步消息 Looper.prepare(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == HANDLER1) { Log.i(TAG, "接收到handler消息..."); } } }; Looper.loop(); } }.start();
这次运行就不会报错了,说明程序在初始化Handler之前需要调用Looper.prepare(),那么主线程为什么能直接初始化Handler?相信有的读者已经想到,是不是在初始化主线程时系统已经帮我们,我们来到应用程序的入口,在APP初始化时会执行ActivityThread的main()方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “ActivityThreadMain”);
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()); AndroidKeyStoreProvider.install(); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0(""); 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")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}
可以看到这里调用了Looper.prepareMainLooper();和Looper.loop();两个方法。接下来我们看一下Handler的源码。
首先就是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(); }}
可以看到在prepareMainLooper()中调用了prepare(false);方法。
接下来看一下Looper.prepare();
public static void prepare() {
prepare(true);
}
可以看到,上面两个方法都调用了prepare(boolean quitAllowed);那接下来我们就来看一下这个方法。
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中有一个ThreadLocal的成员变量,关于ThreadLocal在这里不做详细说明,它的主要作用是为每个使用该变量的线程提供一个独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不会影响其他线程所对应的副本。而且从中我们也可以看出在每个线程中Looper.prepare()只能调用一次,不然就会抛出”Only one Looper may be created per thread”异常。
接下来我们继续看new Looper(quitAllowed);
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
可以清楚的看到在构造方法中,初始化了一个MessageQueue对象,以及获取了当前的线程。
按着代码的顺序,接下来我们看一下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;}
在Handler的构造方法中,主要对一些相关的变量进行初始化。我们再看一下mLooper = Looper.myLooper();这句代码
public static @Nullable Looper myLooper() { return sThreadLocal.get();}
通过上面的代码我们可以清晰看到Looper.myLooper();就是从sThreadLocal中得到我们之前保存的Looper对象,这样就和前面的sThreadLocal.set(new Looper(quitAllowed));联系起来了。
接下来我们再看一下sendEmptyMessage();
在Handler中有这么几个方法:
sendMessage(Message msg);sendEmptyMessage(int what);sendMessageDelayed(msg, 0);sendEmptyMessageDelayed(what, 0);
最后都调用了sendMessageAtTime(Message msg, long uptimeMillis)这个方法
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);}
接下来来到enqueueMessage(queue, msg, uptimeMillis);
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;//this==handler==msg.target if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}
可以看到这里的msg.target就是Handler对象,queue对象就是我们的Handler内部维护的Looper对象关联的MessageQueue对象。
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.next按照时间将所有的Message连接起来。
接下来继续看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 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 != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { 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(); }}
这一段代码中有一个for的死循环,在死循环中不断获取MessageQueue中的消息,Message msg = queue.next(); // might block,接下来如果获取到不为空的消息,就会调用msg.target.dispatchMessage(msg);进行消息的分发,我们在前面已经介绍过msg.target就是一个Handler对象,也就是接下来就会调用Handler中的dispatchMessage(msg);
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}
可以清晰的看到,这里调用了handleMessage(msg);,这个方法不正是我们在MainActivity里面调用的回调接口吗?
关于Android异步消息相关的就分析到这里,如果有什么不足的地方,希望读者指出,谢谢。
更多相关文章
- android媒体--stagefright概述【一】
- android 登陆、提交数据或加载数据时提示页面
- Visual Studio跨平台开发实战(5) - Xamarin Android多页面应用程
- 第一行代码 Android读书笔记(一)
- Android的MediaPlayer架构介绍
- Testing和Instrumentation
- Android(安卓)触控事件解析(Mastering the Android(安卓)Touch Sy
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用