2021-08-18 消息传递机制(Handler) 01

Handler 自我学习笔记


以下内容原文链接:https://blog.csdn.net/luoyingxing/article/details/86500542


Handler的通俗理解:

首先,我将Handler相关的原理机制形象的描述为以下情景:

  • Handler:快递员(属于某个快递公司的职员)
  • Message:包裹(可以放置很多东西的箱子)
  • MessageQueue:快递分拣中心(分拣快递的传送带)
  • Looper:快递公司(具有处理包裹去向的管理中心)

情景分析:在子线程中更新主线程的UI

其中的原理机制可以形象的理解为:

某天,你想给朋友寄一件礼物,首先你拿个箱子装好礼物并包裹好,下单叫了某家的快递员上门取件,快递员揽收你的包裹后,会将包裹送往快递分拣中心,等待配送车送出你的包裹。等配送车来了,就按照你的包裹地址信息,送到指定地方站点,然后分派给相应的快递员,将你的包裹送到你的朋友手里。

这整个邮寄包裹的过程可以形象的理解为Handler的工作机制原理,下面还原一下实际工作过程:
某时,你想刷新主界面的TextView,无奈你不在主线程,此时你就会包装好Message,然后声明一个Handler,让Handler将你的Message送往主线程(Looper),Handler将你的Message送到主线程后,还需要排队等待,等轮到你的时候,主线程就会告诉Handler,这个Message可以处理了,你负责分发一下,于是,Handler将该Message分发到相应的回调或者handleMessage( ) 方法中,于是,你就在该方法中更新了UI。


以下是对Handler源码进行了一个大概逻辑的分析:

MessageQueue的消息队列

//MessageQueue.java
Message mMessages;

MessageQueue中存在一个Message类型的变量mMessages;

//Message.java
Message next;

Message类里面又存在一个Message类型的变量next;

这说明每一个message都会有一个指向下一个节点的next指针,也就是说我们的MessageQueue所持有的mMessages只是一个链表的表头,它的后面有无数个message,这就是我们所说的MessageQueue的消息队列。

MessageQueue初始化

//handler.java
mQueue = mLooper.mQueue

由代码可以看出,mQueue并不是通过new的方式来初始化的,而是通过获取mLooper中的mQueue变量来进行初始化的。

mLooper

//ActivityThread.java
public static void main(String[] args){
...
Looper.prepareMainLooper(); //1
...
ActivityThread thread = new ActivityThread();

Looper.loop();   //2
...
}

//Looper.java
public static void prepareMainLooper(){
   prepare(false);           //3
   synchronized(Looper.class){
      if(sMainLooper != null){
          throw new IllegalStateException("The main Looper has already");
      }
      sMainLooper = myLooper(); //4
   }
}

//3处调动源码
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)); //5
}

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(;;){    //6
  Message msg = queue.next();    //might block //7
  ...
  try{
     msg.target.dispatchMessage(msg); //8
     dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
  }
  ...
  msg.recycleUnchecked();    //9
  }
}

代码1会调用代码3和代码4,代码3的函数关键就在代码5的位置,代码5的位置new了一个Looper,所以Looper的初始化在prepare函数里面。代码5的意思是将初始化的Looper和sThreadLocal进行了绑定,也就是说有且只有一个Looper和主线程进行了绑定。

代码2调用了Looper.loop(),这个Looper.loop()函数里面就是维持了一个for的死循环,也就是代码6。代码6每次轮询都会去调用queue.next(),就是代码7,代码7的意思是去MessageQueue消息队列里面取出一个消息出来。取出消息后交由代码8处理,代码8处的消息调用了target.dispatchMessage(),dispatchMessage就是负责分发这个message的,这个target就是Message创建的时候它对应的Handler。这保证了从哪里发送的这个消息同时就会从哪一个同样的Handler里面去进行一个处理。

Looper就是Handler机制里的一个轮询机制,它会不断地去轮询MessageQueue里面是否有消息,如果有消息它就拿出来进行一个处理。Handler的sendMessage就是往消息队列里面添加消息。

代码9做的事情是消息处理完后进行回收再利用。如果不进行这个回收再利用操作,就会发生内存抖动。因为不回收利用它就会不断地去new message,在不断new的过程中它又会不断地释放message。这个时候就会带来内存抖动,内存抖动又会带来频繁的GC,这样就会造成我们系统的卡顿。

内存抖动是指内存频繁地分配和回收,而频繁的gc会导致卡顿,严重时和内存泄漏一样会导致OOM。

抄录别人的小demo:

public class TestActivity extends AppCompatActivity {
    
    private Handler mHandler;

    private static final String TAG = "TestActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        final TextView textView;
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        textView = findViewById (R.id.textView);
        final Handler handler = new Handler (){   //创建Handler
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage (msg);
                Log.d (TAG, "handleMessage: "+msg.what);
                if(msg.what == 1001){    //判断哪个线程来的消息,what是线程标识
                    textView.setText ("你好!");     //更新UI
                }
            }
        };

        findViewById (R.id.btn_1).setOnClickListener (
                new View.OnClickListener () {
                    @Override
                    public void onClick(View v) {
                        new Thread(new Runnable() {       //子线程
                            @Override
                            public void run() {
                                handler.sendEmptyMessage(1001);  //传递消息,附带线程标识what
                            }
                        }).start();   //启动线程
                    }
                });
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值