全面解析Android进阶面试常客之Handler

本文详细解析了Android中Handler的工作原理,包括Handler、Message、MessageQueue和Looper之间的关系。通过源码分析,揭示了如何在子线程中发送消息并在主线程中处理,解答了Handler如何实现线程间通信。同时,介绍了Looper.prepare()和Looper.loop()在Handler使用中的关键作用,以及Handler.sendMessage()的内部实现。文章适合Android开发者深入理解Handler机制。
摘要由CSDN通过智能技术生成

阅读本文后你将会有以下收获:

  • 清楚的理解Handler的工作原理
  • 理清Handler、Message、MessageQueue以及Looper之间的关系
  • 知道Looper是怎么和当前线程进行绑定的
  • 是否能在子线程中创建Handler
  • 获得分析Handler源码的思路

要想有以上的收获,就需要研究Handler的源码,从源码中来得到答案。

开始探索之路

Handler的使用

先从Handler的使用开始。我们都知道Android的主线程不能处理耗时的任务,否者会导致ANR的出现,但是界面的更新又必须要在主线程中进行,这样,我们就必须在子线程中处理耗时的任务,然后在主线程中更新UI。

但是,我们怎么知道子线程中的任务何时完成,又应该什么时候更新UI,又更新什么内容呢?为了解决这个问题,Android为我们提供了一个消息机制即Handler。下面就看下Handler的常见使用方式,代码如下

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 private Button mStartTask;
 @SuppressLint("HandlerLeak")
 private Handler mHandler = new Handler() {
 @Override
 public void handleMessage(Message msg) {
 super.handleMessage(msg);
 if (msg.what == 1) {
 Toast.makeText(MainActivity.this, "刷新UI、", Toast.LENGTH_SHORT).show();
 }
 }
 };
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 initView();
 }
 private void initView() {
 mStartTask = findViewById(R.id.btn_start_task);
 mStartTask.setOnClickListener(this);
 }
 @Override
 public void onClick(View v) {
 switch (v.getId()) {
 case R.id.btn_start_task:
 new Thread(new Runnable() {
 @Override
 public void run() {
 try {
 Thread.sleep(1000);
 mHandler.sendEmptyMessage(1);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }).start();
 break;
 }
 }
}

可以看到在子线程中,让线程睡了一秒,来模仿耗时的任务,当耗时任务处理完之后,Handler会发送一个消息,然后我们可以在Handler的handleMessage方法中得到这个消息,得到消息之后就能够在handleMessage方法中更新UI了,因为handleMessage是在主线程中嘛。到这里就会有以下疑问了:

  • Handler明明是在子线程中发的消息怎么会跑到主线程中了呢?
  • Handler的发送消息handleMessage又是怎么接收到的呢?

带着这两个疑问,开始分析Handler的源码。

Handler的源码分析

先看下在我们实例化Handler的时候,Handler的构造方法中都做了那些事情,看代码

 final Looper mLooper;
 final MessageQueue mQueue;
 final Callback mCallback;
 final boolean mAsynchronous;
/**
 * Default constructor associates this handler with the {@link Looper} for the
 * current thread.
 *
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 */
 public Handler() {
 this(null, false);
 }
/**
 * Use the {@link Looper} for the current thread with the specified callback interface
 * and set whether the handler should be asynchronous.
 *
 * Handlers are synchronous by default unless this constructor is used to make
 * one that is strictly asynchronous.
 *
 * Asynchronous messages represent interrupts or events that do not require global ordering
 * with respect to synchronous messages. Asynchronous messages are not subject to
 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
 *
 * @param callback The callback interface in which to handle messages, or null.
 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
 *
 * @hide
 */
 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;
 }

通过源码可以看到Handler的无参构造函数调用了两个参数的构造函数,而在两个参数的构造函数中就是将一些变量进行赋值。

看下下面的代码

 mLooper = Looper.myLooper();
 if (mLooper == null) {
 throw new RuntimeException(
 "Can't create handler inside thread that has not called Looper.prepare()");
 }

这里是通过Looper中的myLooper方法来获得Looper实例的,如果Looper为null的话就会抛异常,抛出的异常内容翻译过来就是

无法在未调用Looper.prepare()的线程内创建handler

从这句话中,我们可以知道,在调用Looper.myLooper()之前必须要先调用Looper.prepare()方法,现在来看下prepare方法中的内容,如下

/** Initialize the current thread as a looper.
 * This gives you a chance to create handlers that then reference
 * this looper, before actually starting the loop. Be sure to call
 * {@link #loop()} after calling this me
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值