Handler的sendMessage,post和obtainMessage的不同

我们常用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?

首先看Handler1.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的缓存池

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值