江南带你从源码看handler内部实现的机制

4 篇文章 0 订阅
1 篇文章 0 订阅

介绍handler之前首先我们来看下ThreadLocal类的用法

private static ThreadLocal<String> sthread;
    public static void main(String[] args) {
      sthread = new ThreadLocal<>();
      sthread.set("这是我的第一个项目");
      show();
      new Thread(new Runnable() {

        @Override
        public void run() {
            show();

        }
    });
    }

    public static void show(){
        System.out.println(sthread.get());
    }

ThreadLocal就是相当于是一个map集合,他的键就是当前的线程,值就是自己设置的值,如果ThreadLocal在主线程中set了值,在子线程中是没法获得的,这就是ThreadLocal的一个重要特点,可以将数据和当前线程绑定。
当我们应用开启的时候系统会调用他的ActivityThread类的Loop.PrepareXXX方法,然后这个方法会调用Loop.prepare()方法。
在这个方法里面我们把ThreadLocal类的对象所谓Loop的一个成员变量记录下来,这样有个好处在于下文handler。或者调用loop.loop()的时候很快的通过ThreadLocal获取到当前的looper对象。进而获得MessageQueen。

public final class Looper {
    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;
    volatile boolean mRun;

    private Printer mLogging;

这里写图片描述

这里写图片描述

上面的方法同时是将looper对象绑定到当前线程上面(为下面的handler的时候回调用sThreadlocal.get()获得looper)

这里写图片描述

同时这个方法还把当前looper类的对象与相前线程绑定一起

其实内部就是系统在创建looper对象的prepare()方法的时候同时创建消息队列MessageQueen,然后把looper对象与当前线程绑定在一起

查看Handler源码可知
创建Handler对象的时候我们

 public Handler() {
        this(null, false);
    }

然后调用到了

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

这里写图片描述

这里写图片描述
你看这个机制多好,首先系统自动的在主线程中创建了looper对象,调用了prepare()方法创建了消息队列,然后当handler对象一创建的时候调用构造函数,通过ThreadLocal的get()方法获得当前线程在ThreadLocal中设置的looper引用,然后通过looper对象的引用获得MessageQueen消息队列。当Handler对象获得了消息队列的引用的时候,

hanlder.sendMessage(msg)底层源码可知
  */
    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) {
        MessageQueue queue = mQueue;//因为当前的是handler对象获得了looper引用,进而获得了MessageQueen对象的引用,自然能执行这个方法
        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);
    }

再然后
这里写图片描述
注意这里面还有一个注意点就是msg.target=this,也就是将当前的Handler对象引用赋给msg的target属性、

下面我们看看looper.loop()方法执行了什么?
这里写图片描述
接着追了myLooper()
这里写图片描述

这里写图片描述

这里写图片描述
for (;;) {//死循环,会不会把主线程卡死???
不会被卡死。
涉及到Linux下的通讯机制,管道通讯的概念,管道就是内存中的一个特殊的一个文件,这个文件有两个句柄(其实就是java中的引用),一个是读的引用,一个是写的引用,当写的一端写入内容时,读的一端就会被唤醒,读的一端在没有读到消息时会休眠

总结looper.loop()做了什么?
其实就是首先通过myLooper()方法获得looper对象引用,然后通过looper对象的引用获得消息队列的引用,然后通过一个死循环不断地从消息队列中获取到消息,通过msg.this获得handler,然后调用handler对象的disPatchmessage()方法执行handMessage()【由我们外部重写】,这也就消息就循环执行起来了。。。

这里写图片描述

这里写图片描述

其实作为一个对技术热爱的程序员我们不可能仅仅停留在这里,试问假如主线程给子线程发消息那我们该如何去做?
由于安卓的主线程比较特殊在应用被开启的时候就会调用Looper.prepare()方法创建了Looper.MessageQueen,那我们我们子线程如果也想实现主线程给其发消息,那也得手动创建Looper对象,进而获取到MessageQueen队列,然后不断地往里面发消息。然后通过一个Looper.loop()方法不断地从里面取消息,但是这里有个弊端就是由于Looper.loop()会堵塞住子线程导致子线程没法结束,所以一般这种方式仅仅用于测试,很少这么去用。

public class MainActivity extends Activity {
    private Handler subHandler;

    //1
    private Handler mainHandler = new Handler(){
        //2
        @Override
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 1:
                Toast.makeText(MainActivity.this, msg.toString(), Toast.LENGTH_SHORT).show();
                break;

            default:
                break;
            }
        };
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //创建一个子线程
        Thread subThread = new Thread(new Runnable() {

            @Override
            public void run() {
                //(1)
                Looper.prepare();
                //(2)
                subHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        Toast.makeText(MainActivity.this, msg.toString(), Toast.LENGTH_LONG).show();
                    }
                };
                Log.d("tag", "我是loop前面的代码");
                //(3)
                Looper.loop();//死循环,这样子线程就一直存在
                Log.d("tag", "我是loop后面的代码");

            }
        });
        subThread.start();


    }

    //开启子线程,模拟耗时任务
    public void start(View view){

        new Thread(new Runnable() {

            @Override
            public void run() {
                //模拟耗时操作
                SystemClock.sleep(5000);

                Message msg = new Message();
                msg.obj = Thread.currentThread().getName();
                msg.what = 1;
                //3
                mainHandler.sendMessage(msg);

            }
        }).start();


    }
    //主线程给子线程发送消息
    public void send2SubThread(View view){
        //发送消息
        subHandler.obtainMessage(2, "我是主线程给你发送的数据").sendToTarget();
    }

}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值