在安卓中,在不同的线程间通信,可是使用Handler来轻松实现,本文就详细的介绍Handler处理消息的原理。
讲Handler机制,我们就不得不涉及到一个很重要的对象Message,Handler使用Message对象作为传输对象,所以Message对象中,可以携带一些对象,直接在另一个线程(handler对象所绑定的线程)中去处理。一个典型的案例就是我们在获取网络图片时必须单开线程,而后通过Handler发送一个Message通知UI线程去更新图片,这里就可以将得到的bitmap作为对象放在Message中,在UI线程中去得到这个bitmap,然后显示在ImageView控件上。
public class MainActivity extends Activity {
String m_image_url = "https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superplus/img/logo_white_ee663702.png";
private Handler m_handler;
private ImageView m_imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m_imageView = (ImageView) findViewById(R.id.imageview_temp);
//在UI线程中new Handler()这样,处理消息时,也会在主线程中
m_handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 0x11) {
//获得非UI线程传递过来的Bitmap
Bitmap bitmap = (Bitmap) msg.obj;
if (m_image_url != null) {
m_imageView.setImageBitmap(bitmap);
}
}
return true;//表示拦截消息事件,不会下发到handler的handerMessage()了
}
});
}
//按钮的触发事件
public void click_fetch_image(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 请求网络,注意添加网络权限
URL url = new URL(m_image_url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream inputStream = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
if (bitmap != null) {
// 得到一个Message对象的方法,官方推荐1,2方法
// 1 new Message();2 Message.obtain();3 handler.obtainMessage();
// Message message = Message.obtain();
Message message = m_handler.obtainMessage();
// 设置message传递的信息
// 2个int类型 message.arg1 .arg2 一个obj类型 message.obj
message.what = 0x11;
message.obj = bitmap;
// 发送消息给指定的handler
// 1 通过Message自己发送
message.setTarget(m_handler);
message.sendToTarget();
// 2 通过Handler发送
// m_handler.sendMessage(message);
Log.d("", "子线程消息发送成功!");
}else {
Log.d("", "网络图片返回为null");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
代码中已经讲得很清楚怎么去获取一个Message对象,如果给利用message传递参数,如何将一个通过handler发送message.当然messge还有一些其他知识点,这里就先不讲。
第二个重要的类,当然是Looper了。
Looper其实就是一个在线程的处理MessageQueue的一个类,注意这里的线程是任意线程,不要被UI线程中存在looper就以为它只能在UI线程中存在,只是当运行应用时,系统会默认的在UI线程中去创建Looper而已。一个线程中只能有一个looper去处理消息(其实是分发消息给handler去处理)。想要在子线程中使用handler机制,就必须在子线程中创建looper对象。
一个典型的实例如下:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
跟进Looper源码中看到Lopper.prapare()
最终调用了
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
它的私有构造方法,去创建一个MessageQueue,并绑定当前线程,此时我们线程中就有了looper对象了(或许应该反过来)。
看下looper.loop()
做了哪些事情:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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();
}
}
在第6行,得到当前线程的looper
第10行,得到消息队列,
从第17行开始是一个执行死循环,
18行从消息队列里获得消息,
31行调用handler(message.target就是message绑定的handler)的dispatchMessage()方法,对msg处理message消息
终于言归正传了,因为一般我们都是在UI线程中使用Handler
所以这里只讲UI线程中的Handler
的用法。
先创建一个Handler
,我们一般声明的时候就创建,或者在Activity的onCreate()中创建:
Handler handler = new Handler();//这个handler就不具有处理消息的能力
Handler handler= new Handler(new Handler.Callback() {//这个handler可以处理消息,callback方式
@Override
public boolean handleMessage(Message msg) {
//use msg code
return true;
}
});
Handler handler= new Handler(){//这个handler可以处理消息,匿名内部类方式
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Handler的两个最大的用处就是1,在另一个线程中发送message,或者runnable将他们入队(MessageQueue);2,处理message或者runnable。
1,Message方式
前面讲Message时,讲过了,就是调用m_handler.sendMessage()
方法发送消息
2,Runnable方式
如果不需要传递数据给另一个线程,只需要通知handler线程执行某个方法,比如切换一个Button的状态,这时候,就应该用Runnable方式:
new Handler().post(new Runnable() {
@Override
public void run() {
//handler所在线程执行的代码
//code
}
});
前面去面试一家公司,面试官问我:handler发送消息的Message和Runnable方式有什么区别?苦逼的我当时没研究过handler源码,没答上来。
现在分析一下,就应该去看一下这个post()
方法怎么去处理这个Runnable的,先看一下handler的post()方法
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
看到这个方法名,就有点懂了,其实还是去发送了个Message消息,因为这个sendMessageDelayed()
的第一个参数是一个Message对象,下面跟进getPostMessage()
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
看到这里就明白了,当使用Runnable形式发送消息时,也是发送一个Message对象,只是记录在了message.callback中。
在处理之前,先看一下,handler把是如何把消息发送出去,然后消息又回到handler中去处理的
Class Handler
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
/** mQueue为Looper中的MessageQueue */
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
/** 设置当前消息的handler对象 */
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
/** messageQueue将msg加入队列 */
return queue.enqueueMessage(msg, uptimeMillis);
}
Class MessageQueue
boolean enqueueMessage(Message msg, long when) {
/** 没有找到发送者(handler) */
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("MessageQueue", 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加入队列 */
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;
}
通过分析Handler和MessageQueue的源码,可以发现,Handler发送一条消息之后,是加入到Looper的MessageQueue中的,这样Looper.loop()就能实时的从MessageQueue中去获得消息了,通过调用msg.target.dispatchMessage(msg);将当前的msg消息发送给原来的发送者target来处理。
现在message又回到了Handler中,分析一下handler.dispatchMessage():
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
/** 表明使用Runnable方式发送 */
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
/** 如果存在callback拦截消息 ,并返回true */
return;
}
}
/** 这就是我们常重写的handlerMessage() */
handleMessage(msg);
}
}
到这里,就恍然大悟了,msg已经到我们的handlerMessage()碗里来了。不过还是要看一下handlerCallBack()是怎么处理Runnable的
private static void handleCallback(Message message) {
message.callback.run();
}
很简单的一行代码,就是去跑一个run()方法,而不是start()。
这里分析一下,线程中绑定的是Looper对象,也就是说当前线程一直有一个looper在轮询消息,当looper轮询到一条消息时,就通知handler去处理,注意这是在同一个线程中,也就是looper所在的线程中。handler如果去调用一个实现runnable接口的start()方法岂不是又新开启了一个线程了,所以这里直接调用了run()方法,在当前线程就执行就行了。
Handler除了上面提到的基本用法外,还有一个常用功能就是在UI线程执行一个定时的任务,比如让一个TextView自增长。
private Handler m_handler;
public void click_start(View view) {
m_handler = new Handler();
//发送一个runnable,延迟一秒执行
m_handler.postDelayed(m_runnable, 1000);
}
public void click_cancel(View view){
//从消息队列中移除runnable
m_handler.removeCallbacks(m_runnable);
}
Runnable m_runnable = new Runnable() {
int i = 1;
@Override
public void run() {
m_increment_textview.setText("" + i++);
//在run()中又去发送一个runnable,就是一个循环调用的过程
m_handler.postDelayed(this, 1000);
}
};