从4个问题出发,解析Handler源码

1.问题

   1.handler.post()和view.post()里面Runnable,是执行在哪个线程? 为什么?

   2.handler,looper,messagequeue,thread之间的对应关系?

   3.如何在子线程里面构建handler?

   4.HandlerThread如何实现的?

  2.日常用法

       Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        new Thread(()->{
            handler.sendEmptyMessage(1);
        }).start();

  子线程向主线程发送数据,达到更新UI界面的作用,这是我第一次接触Handler的用法。

  这里面最关键的问题是handler发送的message是如何到达handleMessage。

  然后把这个问题分解成两个角度去分析,1.如何入队 2.如何出队

2.1  入队

从源码分析:

第一步:封装成Message

不论是handler.post(),或者handler.sendEmptyMessage()等,本质上都是和handler.sendMessage()一样,只是他们先要把输入的数据包装成一个Message,然后把message入队,而handler.sendMessage()直接输入的就是一个Message,故不需要再封装。

 handler.post()源码分析:

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

 getPostMessage()把输入r即Runnable,赋值给message.callback,接着通过sendMessageDelayed将message发送出去。

第二步:将message加入MessageQueue

 从handle.sendMessage()源码分析入列流程:

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;
        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);
    }

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

从sendMessage()和post()可以看出,两个方法其实都是通过sendMessageDelayed将Message发送出去,到最后运行enqueueMessage()函数,将message入队。其实handler的所有入队操作都相差不大,最终都是通过enqueueMessage方法将message加入到MessageQueue

2.2 出队

在介绍出队之前,先看下在子线程中是如何创建Handler的。

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyHandlerThread myHandlerThread = new MyHandlerThread();
        myHandlerThread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myHandlerThread.handler.sendEmptyMessage(1234);
    }
    

    public class MyHandlerThread extends Thread{
        Handler handler;
        @Override
        public void run() {
            super.run();
            Looper.prepare();
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    Log.e(TAG,msg.what+"");
                }
            };
            Looper.loop();
        }

    }

上面的代码是简单的子线程中handler的实现,在MyHandlerThread中,首先要调用Loop.perpare(),为该线程创建一个Loop,接着调用Looper.loop()进入循环中,在该循环中不断的取出Message,也就是所谓的出队操作。上述代码中的延时1秒,是为了确保在发送message之前loop已经创建完毕,毕竟这个loop是在子线程中创建的。

sLooper.loop()关键点的源码分析:

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for(;;){
    Message msg = queue.next(); // might block
    msg.target.dispatchMessage(msg);
}

从Threadlocal中取出Looper即myLooper()。ThreadLocal的特性,使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。

接着在for循环不断取出message,queue.next是阻塞的,当没有消息时,会阻塞在这。

msg.target指的是handler,msg.target.dispatchMessage也就是调用handler处理message,从这里也可以看出来,由那个handler发送的信息也就由那个handler进行处理

dispatchMessage()源码分析:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

先判断msg.callback是否为空,如果不为空,则进入handleCallback()进行处理,message的callback指的是Runnable,在前文也指到handler.post(),是将Post中的Runnable赋值给message的callback,于是可以得知当使用handler.post()时,是在handleCallbak()里面对message进行处理。

handlerCallback()源码分析:

private static void handleCallback(Message message) {
    message.callback.run();
}

 message.callback.run()也就是直接调用runnable中的run方法,(PS:开辟一个线程必须调用thread.start(),直接调用thread.run()是无法开辟一个新的线程)。于是,要看handler.post()中的Runable运行在那个线程,要看 msg.target.dispatchMessage(msg)方法运行在那个线程,而 msg.target.dispatchMessage(msg)方法是在Loop中调用,于是要看loop对应的是那个线程。view.post()中的Run able使用的handler中的Loop是UI线程的,于是view.post方法是运行在UI线程,无论view.post方法是在那个线程调用。

下面看如果msg.callback为空,进行else中,再判断mCallback是否为空,也就是handler中mCallback是否赋值。

Callback源码如下:

public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }

可以看出,handler的Callback是一个接口,如果自己实现handler的callback,然后再赋值给handler,则会执行msg.callback逻辑。

如果handler的mcallback也为空,则会执行handlerMessage方法,也就是我们最常用这个方法,handlerMessage是一个空方法,所以需要重写这个方法对msg进行处理,即上文的第一个例子。

总结一下:首先判断message中的runnable是否为空,如果为空再判断handler中的mcallback是否为空,如果mCallback也为空,则调用handlermessage()进行处理。(不想画图 TOT!)

其实从上文的分析,我们也可以看出,一个线程可以有多个handler,但一个线程只能有一个loop,也只能有一个messageQueue,所以多个handler对应一个loop,对应一个thread,对应一个messageQueue,即n:1:1:1

3.HandlerThread的实现

HandlerThread handlerThread = new HandlerThread("handlerThread");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());
        handler.post(()->{
            Log.e(TAG,Thread.currentThread().getName());
        });
09-21 14:55:36.557 10068-10143/com.example.asus.spitest E/MainActivity: handlerThread

上述代码为HandlerThread的简单使用,我们可以看出handler.post中的runnable在handlerThread中运行,从此也可以看出handler中的runnable在那个线程运行与handler在那个线程创建无关,而是看handler的looper是在那个线程中创建

handlerThread源码分析:

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }


public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

handlerThread也就是在子线程中实现Handler,我们重点比较这个与上文实现的在子线程创建hander的区别。

之前的,在thread创建handler,为了确保在getLooper时,Looper已经创建完毕,笔者采用了thread.sleep的方法,不得不说,这种方法纯粹只是取巧。

我们看handlerThread中的getLooper方法(此方法在上述例子中在主线程中运行),先取得同步锁,然后判断mLooper是否为空,如果为空则使线程等待,等到mLooper不为空时,通知此线程重新启动运行。

在run方法中,Looper.myLoop(),返回此线程中的Looper,赋值给mLooper,运行到此时,getLooper方法中的mLooper不为空,

于是notifyAll(),通知所有进行wait的线程重新进入就绪状态。此线程调度,不得不说完美的解决了,在主线程获取Looper时,此时在子线程中Looper或许还未创建的问题。

 

本文若有错误之处,麻烦指出。如果觉得有可取之处,麻烦点赞支持一下。 --箫洛

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值