赞
踩
因为Android UI
线程是线程不安全的,在子线程中更新UI会直接程序崩溃,另外当UI线程需要执行一个比较耗时的操作的话(IO
操作,网络通信等),若是执行时间超过5s,程序会直接ANR
,为了解决上述问题,可以使用异步消息处理机制 Handler,Handler
有两大用处:
Message
或者Runnable
对象通常在主线程中创建Handler
,在子线程中执行耗时操作,并在子线程中将执行结果通过handler
传递到主线程中刷新UI,首先在Activity
中创建handler
:
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
tv_text.setText("更新UI");
break;
default:
break;
}
}
};
新起一个子线程:
new Thread(new Runnable() {
@Override
public void run() {
try {
//模拟耗时操作
Thread.sleep(2000);
//模拟子线程处理完数据,通过handler将结果传到主线程
handler.sendEmptyMessage(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
整个流程:首先在子线程中处理耗时操作,当子线程处理完后,通过handler
将处理结果传到主线程用来刷新UI
,通过handler
就完成了线程间的通信,那么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; }
可以看到首先通过Looper.myLooper()
来创建了一个Looper
对象,如果Looper
为空,则抛出异常Can't create handler inside thread that has not called Looper.prepare()
,来看Looper.myLooper()
:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
从sThreadLocal
对象中取出Looper
,如果sThreadLocal
中有Looper
存在就返回Looper
,如果没有则返回null
了,sThreadLocal
对象什么时候set
的呢?答案是Looper.prepare()
:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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.prepare()
只能被调用一次,即一个handler
只能对应一个Looper
,如果被多次调用,则会抛出异常:Only one Looper may be created per thread
,如果在子线程使用handler
,必须首先调用Looper.prepare()
来创建Looper
;但是我们在主线程中并没有调用Looper.prepare()
,也没有崩溃呀!这是因为系统已经为我们创建好了:
public final class ActivityThread {
public static void main(String[] args) {
......//省略其他代码
Looper.prepareMainLooper();
......//省略其他代码
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
来看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();
}
}
因此我们的主线程一直有Looper
对象,再来看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; ......//省略其他代码 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(); } }
ActivityThread
的main()
方法执行 Looper.loop()
后,主线程就开始无限循环处理消息,没有消息时阻塞等待,既然无限循环为什么主线程没有因为死循环而卡死呢?原因可以参考下这篇文章:Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
handler
创建完成,就可以发送Message
了,来看Message
都有哪些成员变量:
//Message的识别码,用来区别不同的Message
public int what;
//arg1 arg2都是用来传递整形数据
public int arg1;
public int arg2;
//可以传递任意数据
public Object obj;
创建一个新消息,如:
Message message = Message.obtain();//建议使用Message.obtain()而不是new Message(),Message.obtain()可以从消息池中取消息
message.what = 1; //识别码为1
message.arg1 = 100; //携带int类型数 100和101
message.arg2 = 101;
message.obj = MyObject;//携带MyObject对象
Bundle bundle = new Bundle(); //封装bundle
bundle.putString("key", "value");
message.setData(bundle);
handler.sendMessage(message); //通过handler将Message送到消息队列中
来看handler
都有哪些处理Message
的常用方法:
方法 | 备注 |
---|---|
sendEmptyMessage( int what) | 只包含what的Message |
sendEmptyMessageDelayed( int what, long delayMillis) | 只包含what的Message,延迟delayMillis之后发送 |
sendEmptyMessageAtTime( int what, long uptimeMillis) | 发送空消息最终会调用的方法,uptimeMillis=SystemClock.uptimeMillis() + delayMillis,其中SystemClock.uptimeMillis()表示从开机到现在的毫秒数 |
sendMessage(Message msg) | 发送消息 |
sendMessageDelayed(Message msg, long delayMillis) | 延迟发送消息,delayMillis为延迟时间 |
sendMessageAtTime(Message msg, long uptimeMillis) | 发送消息最终会调用的方法,uptimeMillis=SystemClock.uptimeMillis() + delayMillis,其中SystemClock.uptimeMillis()表示从开机到现在的毫秒数 |
dispatchMessage(Message msg) | 分发消息 |
handleMessage(Message msg) | 处理消息 |
removeMessages( int what) | 移除在MessageQueue里面识别码为what的消息 |
post(Runnable r) | 将Runnable转换成一条消息,见下面 |
postAtTime(Runnable r, long uptimeMillis) | 将Runnable转换成一条消息定时发送,见下面 |
postDelayed(Runnable r, long delayMillis) | 将Runnable转换成一条消息延时发送,见下面 |
removeCallbacks(Runnable r) | 移除runnable |
上面表格中除了直接发送Message
外,还可以用post
一个runnable
,看post()
方法:
public final boolean post(Runnable r)
{
//通过getPostMessage()方法将Runnable 转换成消息
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
//新建一个Message
Message m = Message.obtain();
//Runnable 作为Message的一个变量callback
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
可见post(runnable)
最终也是调用sendMessageAtTime()
来发送消息MessageQueue
的。通过上面的代码我们知道runnable
作为Message
的一个变量callback
封装到Message
中了,那么什么时候回调这个runnable
呢?答案是当我们new Handler()
时执行Looper.loop()
的时候,会执行一句msg.target.dispatchMessage(msg)
,其中msg.target
即是发送Message
的handler
,最终处理Message
还是这个handler
,来看dispatchMessage
的源码:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
如果callback
不为null
,则执行handleCallback(msg)
,直接回调runnable
里的run()
方法;如果callback
为null
,则执行Handler
的handleMessage
来处理之前传递到MessageQueue
的消息,整个过程:
总结:
1、在子线程中更新UI有多种: Handler发送Message、Handler的post(runnable)方法、View.post()方法、Activity的runOnUiThread()方法
2.Handler的初始化会获取到当前线程的Looper对象,并通过Looper获得对应的MessageQueue对象
3.如果在子线程中使用Handler,必须首先调用Looper.prepare(),如:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
//初始化Handler之前必须先调用Looper.prepare()
Handler handler = new Handler();
Looper.loop();
}
}).start();
4.Handler在当前线程初始化时创建了对应的Looper,Looper初始化时又会创建对应的MessageQueue,一个线程中只有一个Looper和一个MessageQueue,但可以有多个Handler,他们往同一个MessageQueue中发送消息并且Looper从MessageQueue中取出消息后,再交给发送这个Message的Handler去处理。
PS:关于Handler
使用可能会内存泄露问题,可以参考这篇文章:Android中Handler引起的内存泄露
参考:
1、Android 中线程间通信原理分析:Looper, MessageQueue, Handler
2、Android异步消息处理机制完全解析,带你从源码的角度彻底理解
3、Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。