我们常用Handler来进程线程间通信,但是调用方法有sendMessage,post和obtainMessage三种,这三种到底有什么区别呢?下面就来一一分析下:
(一)sendMessage
最基本用法
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private String new_str = "";
//实例化Handler,重写回调方法
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == 0) {
/*sendMessage方法更新UI的操作必须在handler的handleMessage回调中完成*/
mTextView.setText(new_str);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
new_str = "sendMessage更新UI";
/*sendMessage方法解决UI更新发送消息给handler(主线程中的handler)*/
mHandler.sendEmptyMessage(0);
}
}).start();
}
(二)Post
用法:
此用法并没有开启新的线程(关于子线程的开启本节最后会补充)
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private String new_str = "";
//实例化Handler
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里调用了post方法,和sendMessage一样达到了更新UI的目的
mHandler.post(new Runnable() {
@Override
public void run() {
mTextView.setText(new_str);
}
});
}
}).start();
}
先看Handler中post的源码
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//获得了message实例,将r赋给callback,接下来还是和sendMessage一致的操作,进入sendMessageDelayed
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
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) {
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);
}
handler.post和handler.sendMessage本质上是没有区别的,都是发送一个消息到消息队列中,只不过post使用方式更简单。
在handler的出队列方式中,可以看到如何进入不同的回调
消息出队列源码
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//如果是post,callback不为空,直接进入handleCallback
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果是sendMessage,且创建handler时没有传入callback,则callback为空,直接进入handleMessage,也就是我们自己复写的处理Message的方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//直接run并不会启动新线程,所以这就是post的runnable里面可以直接更新UI的原因
private static void handleCallback(Message message) {
message.callback.run();
}
子线程里面使用post
定义一个线程。
class MyThread extends Thread{
Handler mHandler;
Boolean boo;
public MyThread(Handler handler){
mHandler = handler;
}
public void setBoo(boolean b) {boo = b; }
publid void run(){
。。。//耗时操作
。。。//耗时操作
mHandler.post(new Runnable() {
public void run() {
。。。。//更新UI
}
);//更新UI
}
}
在处理单击事件时:
sureButton.setOnClickListener(new Button.OnClickListener(){
public void onClick(View view){
myThread.start();
}
});
在activity中:
Handler mHandler = new Handler();
MyThread myThread = new MyThread(mHandler);
(三)obtainMessage
public final Message obtainMessage(); //返回值是一个Message,一般搭配sendToTarget使用
有多个重载版本,就是构建传入参数的不同产出不同的Message
public final Message obtainMessage(int what); //带指定what的Message
public final Message obtainMessage(int what, @Nullable Object obj);//带指定what和obj的Message
public final Message obtainMessage(int what, int arg1, int arg2);//带指定what arg1 arg2的Message
public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj);带指定what arg1 arg2 obj的Message
搭配使用就是 obtainMessage(xx).sendToTarget(); //实现和sendMessage相同的功能
obtainMessage会利用内部的message池,如果池中有可用message,就不重新new分配,直接复用,达到节省开销的目的
看源码发现执行sendToTarget内部是和sendMessage相同的
所以平常使用推荐obtainMessage
obtainMessage的实现
.@NonNull
public final Message obtainMessage()
{
return Message.obtain(this);
}
源码调用Message的obtain函数,然后再进入
2.public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
obtain()函数返回一个Message, 然后将参数的hander保存在message中
3.public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
看一下首先对sPoolSync上锁, 然后判断sPool是否有数值,sPool的原型是
private static Message sPool;
如果有数值,做了一个链表的操作
就是这样,sPool又指向了下一个空闲,直到等于NULL, 就会重新分配空间
那么message返回了之后呢,调用的sendToTarget是啥?
看message源码
1.public void sendToTarget() {
target.sendMessage(this);
}
target就是我们obtain保存的我们的Hander
到这里就已经和直接调用sendMessage相同了,只不过message可能使用池中的而已
那这个池是怎么形成的?
首先看message的源代码的recycleUnchecked函数
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
在这里我们看到了首先是对标志和成员变量的复位
把当前message的next赋值为pool的值,如果pool是null也就是第一次分配new得到的message,那么这里next就为null
然后把当前messgae this赋值为sPool,内存增加
这仅仅是一个message调用recycleUnchecked发生的情况,如果有很多个sendmessage都发送,recycleUnchecked就会被多次调用,那么就会形成第一张图的message缓存池
在哪里调用的recycleUnchecked?
首先看Handler的
1.public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
调用了sendMessageDelayed
2.进入
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
调用sendMessageAtTime,将msg一直传入
3.进入
public boolean sendMessageAtTime(@NonNull 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);
}
MessageQueue就是消息队列
4.进入
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
把当前的hander对象保存,和之前是一样的
enqueueMessage函数就不看了,只要知道在enqueueMessage里面会把这个发送的msg进行保存,等待使用
enqueueMessage里面成员变量mMessages存储的永远是最先进入的message,然后根据next一个一个找
就是一个链表,如果是第一个直接赋值
如果不是,那么不断遍历找到最后一个,把msg插入到最后一个
谁使用enqueueMessage的内容?
就是Looper了
进入Looper的loop()函数
1.发现里面的有一个for循环,在这个for循环里面不断把enqueueMessage的数据拿出来进行handermessage的处理
Message msg = queue.next(); // 就是从enqueueMessage里面拿数据
2.下面的
msg.target.dispatchMessage(msg);
target就是我们之前传入的hander,这里就执行了dispatchMessage函数,这个函数先不说,当它执行了handermessage
3.最后有一个
msg.recycleUnchecked();
重点来了,创建出的msg没有被销毁,调用了recycleUnchecked进行保存,spool指向这个message,然后不断recycleUnchecked被调用,汇聚成了message的缓存池