android异步消息处理机制
如果尝试在程序里创建两个Handler对象,一个在主线程中创建,一个在子线程创建.
public class MainActivity extends ActionBarActivity {
private Handler mh1;
private Handler mh2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mh1=new Handler();
new Thread(new Runnable() {
@Override
public void run() {
mh2=new Handler();
}
}).start();
}
} 运行程序会发现,在子线程中创建的Handler的对象会导致程序崩溃,报错如下:
说的是不能再没有Looper.prepare()的线程中创建Handler对象.
如果在子线程中调用了Looper.prepare().
public class MainActivity extends ActionBarActivity {
private Handler mh1;
private Handler mh2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mh1=new Handler();
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
mh2=new Handler();
}
}).start();
} 再运行.就不会出现这个问题了.
看看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;
}
可以看到 ,mLooper是Looper的对象,如果mLooper为空,就会抛出一个运行异常.提示的错误正是刚才运行的错误.
再看一下,什么时候Looper对象才为空
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
方法很简单,就是从sThreadLocal取出Looper,如果sThreadLocal存在Looper则返回looper对象,否则返回空.
结合上面运行,第一遍因为没有Looper.prepare()方法而报Looper对象为空, 第二遍因为有了Looper.prepare()而不会出现这种情况.
所以sThreadLocal里面的Looper对象肯定是Looper.prepare()方法传进去的.
Looper.prepare()方法源码:
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
在这里,可以看到sThreadLocal首先判断是否有Looper对象,如果已经有了,则抛出异常,否则创建一个Looper对象set进去.
看出,每个线程中最多只能有一个Looper对象.
看看刚才的代码,发现只有子线程中调用了Looper.prepare()方法,主线程并没有调用.
这是因为在程序启动时,系统已经自动调用了Looper.prepare()方法.
查看ActivityThread中的main()方法
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到这里有一个Looper.prepareMainLooper()方法.再看到这个方法内部
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
到这里,已经明白:主线程中始终会存在一个Looper对象,而在子线程中需要调用Looper.prepare()去创建.
再看Handler是如何发送消息
public class MainActivity extends ActionBarActivity {
private Handler mh1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mh1=new Handler();
new Thread(new Runnable() {
@Override
public void run() {
Message meg=Message.obtain();
meg.obj="data";
mh1.sendMessage(meg);
}
}).start();
}
} 在线程中,用Handler对象发送一个Message.
可是,Handler到底是把Message发送到哪里?为什么又会在Handler的handleMessage()方法中可以获取这条Message.
其实Handler提供了多个发送消息的方法,除了sendMessageAtFrontOfQueue()外,所有方法都到经过sendMessageAtTime()方法.
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对象,第二个则是需要延迟发送的毫秒数.如果不是调用的sendMessageDelayed(),延迟数都为0.
MessageQueue,是一个消息队列.提供了出队与入队的方法.
而这里面的enqueueMessage就是入队方法.
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
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;
this.notify();
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}
MessageQueue并没有使用一个集合将所有的消息保存起来,它只使用了mMessages对象表示未处理的消息.
其实入队的方法就是将所有的消息按uptimeMillis时间排序,具体操作方法根据时间顺序调用msg.next.
而从每一个消息指定它的下一个消息是什么.
而出队的方法,就在Looper.loop()方法.
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
从代码的 while(true) 开始就是一个死循环,然后不停的调用MessageQueue的next方法,next()就是消息队列出队的方法.
它的简单逻辑就是如果当前MessageQueue中存在mMessages(待处理消息) ,就将这个消息出队,然后让下一个消息成为mMessages,否则就形成一个堵塞状态,一直等到有新消息入队.
每当一个消息出队,就将它传递到msg.target的dispatchMessage()方法中.
而msg.target就是Handler对象.
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果mCallback不为空,则调用mCallBack的handleMessage()方法,否则调用Handler的handleMessage()方法.
除了发送消息,还有几种方法可以在子线程中更新UI
1.Handler的post()方法
2.View的post()方法
3.Activity的runOnUiThread()方法.
qq3061280@163.com