Android之Handler解析

众所周知,由于线程安全问题,Android中规定子线程不能更新UI,所以常常要借助Handler实现线程的切换。

比如这个实例,通过一个定时器在TextView中更新时间:

public class MainActivity extends AppCompatActivity {

    private TextView mTvShow;

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

        mTvShow = findViewById(R.id.tv_show);
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
				mTvShow.setText(System.currentTimeMillis() + "");
            }
        }, 100, 3000);
    }
}

不出意外,程序崩溃了,并报错:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
因为new TimerTask的本质就是开启一条新线程,而在新线程中试图更新TextView

下面是借助Handler修改后的程序:

public class MainActivity2 extends AppCompatActivity {

    private TextView mTvShow;

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

        mTvShow = findViewById(R.id.tv_show);

        final Handler myHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                if(msg.what == 0x123){
                    mTvShow.setText(msg.obj.toString());
                }
            }
        };

        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.what = 0x123;
                msg.obj = System.currentTimeMillis();
                myHandler.sendMessage(msg);
            }
        }, 100, 3000);
    }
}

使用Handler之后程序运行成功,每3s更新一次TextView。

可以看到,Handler在子线程中将消息通过 sendMessage() 发送了出去,然后在主线程使用了handleMessage(Message msg) 对消息进行了处理,更新了UI。但中间的过程就很迷,不知道把消息发送到哪儿去了,也不知道handleMessage()是什么时候被调用的,所以需要查看一下源码了解背后的运行机制。

创建Handler

先从Handler的构造函数说起,Handler有很多构造函数,如下:

    public Handler() 
    public Handler(Callback callback) 
    public Handler(Looper looper)
    public Handler(Looper looper, Callback callback) 
    public Handler(boolean async)

但不论调用以上哪个构造方法,最终调用的其实是以下的两个:

185    public Handler(Callback callback, boolean async) {

			//......省略一段不重要的源码......

195        mLooper = Looper.myLooper();
			//注意在非主线程中创建Handler之前,要先创建Looper,否则会抛出异常
196        if (mLooper == null) {
197            throw new RuntimeException(
198                "Can't create handler inside thread that has not called Looper.prepare()");
199        }
200        mQueue = mLooper.mQueue;//MessageQueue由Looper获取
201        mCallback = callback;
202        mAsynchronous = async;
203    }
224    public Handler(Looper looper, Callback callback, boolean async) {
225        mLooper = looper;
226        mQueue = looper.mQueue;//MessageQueue由Looper获取
227        mCallback = callback;
228        mAsynchronous = async;
229    }

这两个终极构造函数的差别就在于第一个Looper是自动获取的,而第二个是作为参数传入的。
相同点则是都初始化了mLooper、mQueue、mCallback、mAsynchronous。

(mCallback是个接口,里面只有一个handleMessage()方法,同时Handler内部也有一个handleMessage()空方法,因此要么传入Callback参数,要么继承Handler子类重写该方法)

构造函数中涉及到另外两个组件,Looper(mLooper),MessageQueue(mQueue),有这两个组件配合Handler,Handler才能成功运行。注意Looper是与线程关联的,每个线程只能拥有一个Looper,而MessageQueue又是通过Looper管理的,它在Looper创建时创建,获取也要通过Looper.myQueue()来获取,所以它也是线程唯一的。

从上面的源码中可看出,创建Handler时,会为Handler绑定一个Looper和一个MessageQueue。在未传入Looper参数的构造函数中,通过mLooper = Looper.myLooper()获取当前线程的Looper:

//返回一个与当前线程相关联的Looper,没有则返回null
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

如果返回null,Handler构造函数紧接着就会抛出异常。

注意在主线程中,已经为我们创建好了一个Looper,ActivityThread源码:

public static void main(String[] args) {
        //...     
        Looper.prepareMainLooper();//prepareMainLooper()中又调用了prepare()来创建Looper
		//...
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();//获取Handler
        }
        //...        
        Looper.loop();//启动Looper

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

所以主线程中不用也不能再创建Looper,但子线程中创建Handler前,必须调用Looper.prepare()创建Looper,否则会抛出异常。至于为什么要用Looper.prepare()来创建,而不直接使用构造函数,参考以下源码:

//Looper的构造方法是私有的,不能在外部调用
   private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建Looper的同时,创建了一个MessageQueue。
        mThread = Thread.currentThread();
    }

可以看见Looper的构造方法使用了private关键字,但它提供了一个publicprepare()方法:

public static void prepare() {
    prepare(true); //调用内部的重载方法
}

private static void prepare(boolean quitAllowed) {
//检查当前线程是否已经有Looper了,如果有则抛出异常,否则为当前线程创建一个Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

以上是创建Handler的过程。

总结一下:
1.主线程中已经创建好Looper,因此可以直接创建Handler
2.在非主线程中创建Handler前要先调用Looper.prepare()方法创建Looper
3.Looper是与线程关联的,每个线程只能有一个LooperLooper的构造方法是private的。
4.创建Looper时,也会创建一个MessageQueue,每个线程也只有一个MessageQueue
5.创建Handler时要重写handleMessage()方法,或者传入Callback参数。


使用Handler发送消息

Handler中发送消息的方法,如下:

public final boolean post (Runnable r)
public final boolean postAtFrontOfQueue (Runnable r)
public final boolean postAtTime (Runnable r, long uptimeMillis)
public final boolean postAtTime (Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed (Runnable r, long delayMillis)
public final boolean postDelayed (Runnable r, Object token, long delayMillis)

public final boolean sendEmptyMessage (int what)
public final boolean sendEmptyMessageAtTime (int what,  long uptimeMillis)
public final boolean sendEmptyMessageDelayed (int what, long delayMillis)

public final boolean sendMessage (Message msg)
public final boolean sendMessageAtFrontOfQueue (Message msg)
public boolean sendMessageAtTime (Message msg, long uptimeMillis)
public final boolean sendMessageDelayed (Message msg, long delayMillis)

可以看见上面的方法主要就分为三种,一种是以Runnable为参数的post(),一种是以what为参数的sendEmptyMessage(),还有一种以Message为参数的sendMessage(),其他的区别只在于是否延迟消息。

先看post()

   public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
//把Runnable作为Message的callback字段传给Message
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

可以看出,post()方法就是将Runnable对象赋值给Messagecallback字段,然后调用sendMessage()。可以猜到,sendEmptyMessage()也是将what值传给Message的某个字段,再调用sendMessage()方法,查看源码:

 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

果然,sendEmptyMessage()也是将int参数赋值给Message对象的what字段。

所以无论是通过哪种方法,发送的都是一个Message对象。

如果使用sendMessage()方法,需要先获取一个Message对象,如果有需要传输的数据,通常会设置给Message的obj字段或者data字段来存储,obj是Object类型,data是Bundle类型。post()方法中传入的token就是作为obj字段的值。

查看源码可发现,所有发送消息的方法最后其实都调用了Handler中的一个私有方法enqueueMessage()

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this; //msg.target即发送消息的Handler
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //queue即在Handler初始化中获取的MessageQueue实例mQueue
    return queue.enqueueMessage(msg, uptimeMillis);
}

MessageQueue中的enqueueMessage()

boolean enqueueMessage(Message msg, long when) {

			//...

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //如果队列中没有消息或消息延迟时间为0,则将消息插入到MessageQueue的队首
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {//否则按延迟时间插入队列
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

从上面的分析我们知道了所谓的Handler发送消息,其实就是将消息插入到消息队列中,那什么时候执行消息队列的消息呢?

Looper轮询消息

在前面的主线程代码中,最后一句是Looper.loop();,正是这个方法让启动了消息队列,让handler从消息队列中获取并处理消息。loop()源码如下:

public static void loop() {
    final Looper me = myLooper();//获取当前线程的Looper
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue; //获取Looper管理的MessageQueue
    
	//...
	//用死循环不断取出MessageQueue中的消息
    for (;;) {
    	//获取消息队列中的下一个消息,没有消息时可能会阻塞
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
       //...       
        try {
        //让Handler分发消息(msg.target即发送消息的Handler)
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
           	 //...
        } finally {
			//...
        }        
		//...
        msg.recycleUnchecked();//回收消息
    }
}

可以看到,Looper中的loop()会不断循环获取消息,然后调用Handler中的dispatchMessage()分发消息:

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {//如果是使用post方法发送的消息,就调用handleCallback()处理消息
        handleCallback(msg);
    } else {//否则调用Callback参数中实现的handleMessage()方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
       	//如果都不是就调用子类重写的handleMessage()方法
        handleMessage(msg);
    }
}

至此,就清楚了Handler的大致原理,运行的流程图如下:

在这里插入图片描述

总结:

Handler这套消息处理机制其实是几个组件共同协作的结果,Handler主要负责将消息发送给MessageQueue,并实现处理消息的方法(handleMessage())。MessageQueue负责接收Handler传来的消息,并按触发时间插入自己队列中。Looper才是处理消息的主要角色,通过loop()从队列中获取消息,并调用HandlerdispatchMessage()分发并处理消息。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值