Android消息机制中Handler切换线程的思考记录

ThreadLocal作用

在当前线程中存放属于该线程的数据

ThreadLocal存储算法记录

将当前线程的弱引用作为key,将存放的值作为value,使用一个数组table存放,key的index为当前LocalThread的hash和其value对象的mask(mask:用于将hash转化为指数(indices))相与的结果,value的index为key的index+1

Handler与Looper

在子线程中new一个Handler时,如果不在构造参数中传入一个Looper,则必须在new之前调用Looper#prepare()方法为该子线程创建一个Looper,该方法内部使用ThreadLocal存储Looper,这样可以让每个线程拥有自己的Looper,Handler在构造器初始化时会调用Looper#myLooper()方法,该方法会从ThreadLocal中获取一个Looper对象,你也可以在构造器中传入一个looper对象,如果是通过Looper#getMainLooper()得到的looper对象,不需要再调用looper#loop(),因为系统已经调用过了,系统的looper已经开始不断遍历MessageQueue了,只要有新的Message到来,looper就会调用该Message持有的(Handler发送的Message,Message会持有Handler引用)Handler#handleMessage(Message msg)方法

切换线程

切换线程实际上就是调用位置的切换而已,Handler切换线程的机制最终原因是handleMessage方法的调用位置的切换,比如主线程。

handler将线程切换到主线程

handler将自己的引用间接被Looper持有,当Looper在主线程调用loop()方法时,该方法会取出handler并调用其handleMessage()方法,相当于切换到主线程


handler--Looper--MessageQueue的关系

这三者构成Handler通信机制

MessageQueue内部使用单链表来存储信息Message,Handler发送的Message全部都添加到了MessageQueue中,Looper#loop()方法通过调用MessageQueue#next()方法不断遍历链表中的Message,当取得符合的Message后,通过Message持有的Handler对象引用调用Handler#handleMessage方法,如此,Looper#loop()在哪个线程调用的,handleMessage方法就切换到哪个线程了。

简单代码模拟实现handler切换线程到主线程

Handler:

public class MHandler {

   MLooper mLooper;

   public MHandler(MLooper mLooper) {
       this.mLooper = mLooper;
   }
    public void sendMessage(MMessage msg) {
       //让msg保持MHandler的引用,同时让mLooper保持msg的引用,并在mLooper获取msg中的handler回调handleMessage,
       // 这样就能在mLooper的线程中调用handleMessage,相当于切换了线程
       
msg.mHandler = this;
       mLooper.msg = msg;
       //主线程中的looper调用loop()后会处于阻塞状态,获取到msg同时间接获取到handler,此时msg不为null,
       // looper#loop()程序继续进行调用handlerMessage
   
}

   public void handleMessage(MMessage msg) {

   }

}

Message(这里只模拟一个消息,所以就用Message代替MessageQueue)

/**
* Created by zz on 16/3/6.
* 如果只是切换线程的话,只需要让Looper等待Handler的引用到来然后在ui线程调用就可以
* ,这里是为了模拟Handler发送信息,所以添加了一个Message
*/
public class MMessage {
   MHandler mHandler;
   String msg;
}
Looper:
public class MLooper {
   //相当于把MessageQueue简化成一个msg了
   
MMessage msg;


   public void loop() {
       while(msg == null) {
           //如果looper没有获取到msg,就让它一直等待
       
}
       msg.mHandler.handleMessage(msg);
   }

}

调用代码

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_2);
   tv = (TextView) findViewById(R.id.activity2_text);
   final MLooper looper = new MLooper();

   tv.setClickable(true);
   tv.setOnClickListener(new View.OnClickListener() {
       @Override
       
public void onClick(View v) {
           new Thread() {
               @Override
               
public void run() {
                   MHandler handler = new MHandler(looper) {
                       @Override
                       
public void handleMessage(MMessage msg) {
                           super.handleMessage(msg);
                           //主线程回调
                           
tv.setTextSize(64);
                       }
                   };
                   MMessage msg = new MMessage();
                   msg.msg = "msg";
                   //子线程发送
                   
handler.sendMessage(msg);
               }
           }.start();
           //在主线程调用,切换了线程
           
looper.loop();
       }


   });

}

效果就是将一个TextView的字体大小由32sp变大到64sp,就不演示了

持续更新中  

代码地址 https://github.com/franos/androidLearn2




你可能感兴趣的:(android,线程,handler,threadLocal,message,消息机制,messagequeue,切换线程)