Handler的作用
大家都知道,Handler是用来做子线程和UI线程之间的通信的,在子线程进行耗时的操作,然后通过Handler将消息传递到主线程中进行UI操作。大家有没有一个疑问,为什么不能在子线程更新UI?这是因为ViewRootImpl对UI操作做了验证
void checkThread(){
if(mThread!=Thread.currentThread()){
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can
touch its views."
)
}
}
另外,题外话:大家有没有遇到过在onCreate方法中new一个Thread去进行UI操作是不会报错的,那是因为什么原因呢?那是因为在onCreate()方法中进行UI的修改,ViewRootImpl的checkThread()还没有来得及工作的原因
再另外,系统为什么限制不能在子线程中更新UI呢?因为Android的UI 控件不是线程安全的,多线程并发访问会导致线程安全问题,
那么为什么不加锁呢?加锁会让UI访问的逻辑变得复杂,并且阻塞某些线程的执行降低对UI访问的效率
Handler的原理
Android的消息机制主要指Handler的消息机制,Handler的运行主要依靠底层的Looper和Handler支撑
MessageQueue:消息队列,并不是真正的队列,而是以单链表的数据结构来存储Handler发送过来的Message;
Looper:Looper以无限循环的方式去查看消息队列中是否有新消息,线程默认是没有Looper的,如果需要使用就必须为线程创建Looper,主线程创建时会初始化Looper,这就是主线程可以使用Handler的的原因
Handler:通过Handler从子线程发送消息到主线程的MessageQueue中,Handler创建的时候会采用当前线程的Looper来构造消息循环系统,另外Handler通过ThreadLocal可以轻松的获取每一个线程的Looper,从而获取消息队列中的message
源码
new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler();
}
}).start();
如果现在运行一下程序,你会发现,在子线程中创建的Handler是会导致程序崩溃的,提示的错误信息为 Can't create handler inside thread that has not called Looper.prepare() 。说是不能在没有调用Looper.prepare() 的线程中创建Handler,那我们尝试在子线程中先调用一下Looper.prepare()呢,代码如下所示:
<span style="font-size:14px;"> new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler2 = new Handler();
}
}).start(); </span>
这样的方法不会奔溃,我们来看一下Handler的源码,为什么创建Handler需要Looper.prepare()?
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;
}
我们看到通过Looper,myLooper()获取到一个Looper对象,如果Looper对象为null就会抛出Can't create handler inside thread that has not called Looper.prepare()异常,我们来看一下Looper.myLooper()中的源码
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
就是从ThreadLocal对象中get一个Looper,有get就有set,我们看一下在哪里set了?
public static void prepare() {
prepare(true);
}
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()方法,这个方法会判断ThreadLocal中是否存在Looper,存在则报错,一个线程只能存在一个Looper;
这样就找到了为什么创建Handler对象需要Looper.prepare();我们可能会有疑问主线程创建Handler也没有也调用Looper.prep[are()?
这是因为在ActivityThread的main方法中,系统以及帮我们自动调用了Looper.prepare()
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");
}
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
这样基本就将Handler的创建过程完全搞明白了,总结一下就是在主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象。
创建完Looper后,我们来看一下如何发生消息,就是通过handler的sendMessage方法发送一个Message
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.arg1 = 1;
Bundle bundle = new Bundle();
bundle.putString("data", "data");
message.setData(bundle);
handler.sendMessage(message);
}
}).start();
我们来看一下sendMessage(message)的源码,基本上大多数的发送信息的方法最终都会走到下面的方法
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()方法接收两个参数,其中msg参数就是我们发送的Message对象,而uptimeMillis参数则表示发送消息的时
间,它的值等于自系统开机到当前时间的毫秒数再加上延迟时间,如果你调用的不是sendMessageDelayed()方法,延迟时间就为
0,然后将这两个参数都传递到MessageQueue的enqueueMessage()方法中。MessageQueue中所有的Message都会以队列的形式
存放,提供入队和出对的方法,这个类是在Looper的构造函数中创建的,因此一个Looper也就对应了一个MessageQueue。
那么enqueueMessage()方法毫无疑问就是入队的方法了,我们来看下这个方法的源码:
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;
}
所谓的入队其实就是将所有的消息按时间来进行排序,这个时间当然就是我们刚才介绍的uptimeMillis参数,入队操作我们就已经看
明白了,那出队操作是在哪里进行的呢?这个就需要看一看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.recycleUnchecked();
}
}
我们可以看到,这个方法中存在一个死循环,会不同的调用MessageQueue.next()方法,其实这个next()方法就是消息队列的出队方法,它的简单逻辑就是如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。
从消息队列中获取消息后会调用msg.target.dispatchMessage(msg),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()方法,并将消息对象作
为参数传递过去。这样我相信大家就都明白了为什么handleMessage()方法中可以获取到之前发送的消息了吧!
那么我们还是要来继续分析一下,为什么使用异步消息处理的方式就可以对UI进行操作了呢?这是由于Handler总是依附于创建时所在的线程,比如我们的Handler是在主线程中创建的,而在子线程中又无法直接对UI进行操作,于是我们就通过一系列的发送消息、入队、出队等环节,最后调用到了Handler的handleMessage()方法中,这时的handleMessage()方法已经是在主线程中运行的,因而我们当然可以在这里进行UI操作了。整个异步消息处理流程的示意图如下图所示:
另外除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作:
1. Handler的post()方法
2. View的post()方法
3. Activity的runOnUiThread()方法
我们先来看下Handler中的post()方法,代码如下所示:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
原来这里还是调用了sendMessageDelayed()方法去发送一条消息啊,并且还使用了getPostMessage()方法将Runnable对象转换成了一条消息,我们来看下这个方法的源码:
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这个callback字段看起来有些眼熟啊,在Handler的dispatchMessage()方法中原来有做一个检查,如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法。那我们快来看下handleCallback()方法中的代码吧:
private final void handleCallback(Message message) {
message.callback.run();
}
竟然就是直接调用了一开始传入的Runnable对象的run()方法。因此在子线程中通过Handler的post()方法进行UI操作就可以这么写:
public class MainActivity extends Activity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
// 在这里进行UI操作
}
});
}
}).start();
}
}
虽然写法上相差很多,但是原理是完全一样的,我们在Runnable对象的run()方法里更新UI,效果完全等同于在handleMessage()方法中更新UI。
然后再来看一下View中的post()方法,代码如下所示:
public boolean post(Runnable action) {
Handler handler;
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
ViewRoot.getRunQueue().post(action);
return true;
}
return handler.post(action);
}
原来就是调用了Handler中的post()方法。
最后再来看一下Activity中的runOnUiThread()方法,代码如下所示:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,否则就直接调用Runnable对象的run()方法。