- 说在前面
作为一个android开发工程师,我们最常用到得方法之一就是handler ,使用它得原因相信大家也很清楚啦,因为android本身如果在UI线程(UIThread)中使用耗时操作比如网络请求得时候就会出现异常,而为了解决这个问题就需要使用子线程先进行数据获取然后在对UI进行操作,但是在Android中所有得UI操作只可以在UI线程里进行,所以也就出现了一个情况 :获取得数据如何传输给UI线程使用?因此android提供了handler跨线程传输得机制,在初学Android得时候我们都是在子线程获取完数据以后直接调用handler得sendMessage ,就可以将数据传回到UI线程使用,如果我们了解了handler得运行机制我们就可以直接使用handler在子线程与子线程通信。下面我们将逐一介绍handler得运行机制(只会把关键代码贴出) - 平常得写法
//创建一个handler实例
private Handler handler=new Handler(){
//当子线程调用sendMessage得时候会回调这里得方法
@Override
public void handleMessage(Message msg) {
textView.setText(msg.obj.toString());
}
};
Thread thread2=new Thread(){
@Override
public void run() {
//此处进行网络请求等耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//创建消息对象
Message message=handler.obtainMessage();
message.obj="hi 1";
//发送消息
handler.sendMessage(message);
以上是我们一般得使用方法也就是创建一个子线程然后在子线程进行耗时操作最后在把数据回传给handleMessage 因为此方法在UI线程里所以可以直接进行UI得操作那么他是怎么实现得呢 ?下面我们从Handler入手追一下它得源码。
- handler得构造方法(只留有关键代码)
public Handler(Callback callback, boolean async) {
首先他会获取looper如果looper获取不到就会抛出异常
那么looper是怎么来得呢我们在看下mylooper里做了什么
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//此为myLooper得方法里得代码 可以看到他从中sThreadLocal get出一个Looper 那么既然有get那他肯定就放过什么东西跟着这个思路走我们找到了放置looper对象得方法
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//放置looper得方法 从下所见它是直接new了一个looper所以我们看下looper得构造方法
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得构造方法代码
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
从上面可以看到looper在实例化得时候给我们创建了一个MessageQueue消息队列和一个并使用mThread 变量维护当前线程。那么他是如何在调用sendMessage后去调用handleMessage呢我们先看下handler是如何发送消息得。
//我们跟了一下sendMessage还有其他发送方法发现 调用Handler得发送消息方法不管是什么方式最后都会跑到这个方法里面来
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);
}
后面会发现sendMessageAtTime最后关键会调用得方法是enqueueMessage,我们来看下enqueueMessage干了什么。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
你会发现他设置了一个target然后就调用了queue.enqueueMessage 我们在往下看
boolean enqueueMessage(Message msg, long when) {
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 {
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 放入了MessageQueue中,并且在放入时是将Message存到了上一个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.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();
}
}
/*
这里我们看到,mLooper()方法里我们取出了,当前线程的looper对象,然后从looper对象开启了一个死循环
不断地从looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用
dispatchMessage去分发消息,通过代码可以看出target就是我们创建的handler。我们在继续往下分析Message的分发
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/*好了,到这里已经能看清晰了
可以看到,如果我们设置了callback(Runnable对象)的话,则会直接调用handleCallback方法
*/
private static void handleCallback(Message message) {
message.callback.run();
}
//即,如果我们在初始化Handler的时候设置了callback(Runnable)对象,则直接调用run方法。比如我们经常写的runOnUiThread方法:
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
/*
而如果msg.callback为空的话,会直接调用我们的mCallback.handleMessage(msg),即handler的handlerMessage方法。由于Handler对象是在主线程中创建的,
所以handler的handlerMessage方法的执行也会在主线程中。
*/
在上面我们也看见了如果没有创建looper对象将会直接抛出异常,而创建loop得方式在我们查看得我们并没有看到在任何时候去创建过那么主线程是如何传递得呢?其实是在activity创建得时候他就已经创建了looper对象。那么他是什么时候创建的呢,其实在app启动的时候都会去执行一个类的方法 ,那就是ActivityThread 的main方法
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();
//上面就是ActivityThread main方法的部分代码 可以看到他创建了一个Looper对象然后就已经调用了looper的loop方法进行接收读取,这样看显然是比较耗资源其实消息队列中的next()里有个C++库的方法nativePollOnce 这个方法在没有消息的时候会出现挂起这样就不会浪费资源了
最后我们发现如果需要使用handler去让两个子线程去通信那么只需要在接收线程里创建一个looper就然后在调用loop方法就可以正常使用了。
- 总结
最后发现handler的基本运行机制是这样的 :当activity创建的时候将会创建一个looper对象 ,然后looper对象会存住当前线程并创建一个专属于它的消息队列,当我们在子线程调用sendMessage 后它将会往消息队列里面按时序插入消息,并在Message中设置所属的handler标签 looper发现有消息就会启动然后通过消息对象里的标签handler去调用dispatchMessage 最后由dispatchMessage 调用我们的指定handleMessage 这样回到了我们的主线程。
网友评论