Android 线程通信 —— Handler 使用

2,052 阅读6分钟

Android 线程通信 —— Handler 使用

Android 应用是一个多线程应用,Android 有一个主线程,在没有特定指明线程的前提下,你执行的代码都是在 Android 主线程中运行。一般来说 Android 的 View 绘制是在主线程中完成的,所以如果我们在主线程中进行耗时操作时,线程无响应就会导致 UI 绘制等其它用户所看到的东西和执行的操作卡住,给用户带来不好的体验。所以 Android 中规定了 ANR(Application Not Responding:应用无响应)。

ANR 规定当 Activity 中 5s 无响应,BroadcastReceiver 中 10s 无响应,Service 中 20s(前台)、200s(后台)无响应就会告诉用户选择继续等待或者关闭应用。所以我们要尽量避免避免 ANR。当然一款舒服的应用,就算没有规定 ANR,我们也不应该在主线程中进行耗时操作给用户带来不好的体验。

所以当我们使用多线程时,不可避免就会遇到线程通信。Android API 给我们提供了很多线程通信的方法。这里我们先来学习一下 Handler 的使用。

Handler 介绍

Handler 是 Android os 包中的一个用于进程内部、线程间通信的 API,Handler 的使用一般包含 Hander、Message、MessageQueue、Looper。

Handler 主要用于处理异步消息,使用 Hander 发出一条 Message 时,Message 会进入一个消息队列 MessageQueue 中,Looper 会循环读取 MessageQueue 中的 Message,并且将取出的 Message 交给 Handler 处理。

本文只对 Handler 的用法做一个分享,个中细节后面文章一起学习。

Handler 使用

一般我们在使用 Handler 时,只会使用到 Handler 和 Message,使用 Handler 的 sendMessage() 等方法发消息,重写 handleMessage() 去处理接收到的消息。

在看 Handler 发送和接受消息前,我们先看一下 Handler 发送的 Message 是什么。Message 是消息,也是我们 Handler 中通信的载体,Message 中有几个公有的字段,用作定义和描述任意数据对象的消息。

Message 中定义数据的包含两个 int 字段:arg1 和 arg2,还包含一个 Object 字段:obj,Message 中还有一个 int 类型的 waht 字段,作为 message code,来区分这条 message 是干什么的或者从哪来的。Messsage 的构造方法是 public 的,但是官方建议使用 Message.obtain()。

// 不建议使用
Message msg1 = new Message();
// 该方法会从全局池中返回一个 Message,避免重复创建对象
Message msg2 = Message.obtain();

Message 中的 obtain() 方法被重载了,支持传入 handler、what、arg1、arg2、obj 等入参。

简单介绍了一下 Message,我们可以正式开始看 Handler 的使用了,当然在这之前我们得先看一下如何创建一个 Handler。

Handler 有 7 个构造方法,这 7 个大家可以去看一下 Handler 类,这里就不一一贴出来了,主要说一下这 7 个构造方法干了什么。7 个构造方法主要是选择传入了 3 个参数:looper : Looper、callback : Callback、async : boolean。这三个参数主要是干什么的呢?Looper 就是上面说的用来循环读取 MessageQueue 中的 Message,CallBack 是一个接口,只有一个 handleMessage(),作用与 Handler 中 handleMessage() 方法类似,用于处理 Messsage。而且在 Handler 源码中 Handler 的 handleMessage() 和 Callback 的 handleMessage() 方法是在一起调用的。async 是干什么的呢?看名字就知道用于线程同步,如果 async 为 true,那么 Message 插入到 MessageQueue 中的过程就是异步的。但是所有带 async 的构造方法都适用了 hide 注解(hide:该 Api 不对外公开),所以我们正常来说可以使用的只有 4 个构造方法。

private Handler.Callback callback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // 处理 Message
            
            return false;
        }
    };
// 方式 1
private Handler handler = new Handler(Looper.getMainLooper(),callback);
// 方式 2
private Handler handler2 = new Handler(callback);

​ 一个 Handler 必须要持有一个 Looper,没有 Looper 的 Handler 在构造或使用时会抛出异常,这时候你们会很奇怪,那么方式 2 还有什么意义呢?其实,当调用没有 Looper 的构造方法时,Handler 会去拿当前线程的 Looper,而 Android 的主线程会自己在进程开始时创建 Looper 的,但是如果你在子线程使用方式 2 时,就会抛出异常了。所以不太建议使用方式 2。

那么我们如何生成 Looper 呢?

    // 获取主线程 Looper
    private Looper mainLooper = Looper.getMainLooper();

    // 子线程中只需要调用 
    Looper.prepare();

Handler 的发送 Message 有很多种方法,这里我们先看一下最简单的使用:

    private void sendMessage(int arg1, int arg2, Object obj){
        
        Message message = Message.obtain();
        message.arg1 = arg1;
        message.arg2 = arg2;
        message.obj = obj;
        // Handler 将 message 发送出去
        handler1.sendMessage(message);

    }

如果我们使用 Handler 是为了线程通信,那么 sendMessage() 和 handleMessage() 分别在不同的线程处理就可以。那怎么处理 Message 呢?我们可以使用 CallBack 的 handleMessage() 或者 Handler 的 handleMessage():

    private Handler.Callback callback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // 处理 Message

            return false;
        }
    };

    private Handler handler1 = new Handler(Looper.getMainLooper(),callback){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 处理 msg
        }
    };

我们可以看到,Callback 的 handleMessage() 需要返回一个 boolean 值,返回 true 就会拦截事件,CallBack 处理完成后不交给 Handler 处理了,如果返回 false 则 Callback 处理完成后还会交给 Handler 处理。

我们来看一个具体示例:

Case:网络下载一张图片,成功展示图片,失败展示吐司消息:


    private Handler.Callback callback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // 处理 Message

            return false;
        }
    };

    private Handler handler1 = new Handler(Looper.getMainLooper(),callback){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 处理 msg
            
            if (msg != null && msg.obj instanceof Bitmap){
                Bitmap bitmap = (Bitmap) msg.obj;
                imageView.setImageBitmap(bitmap);
            }else {
                Toast.makeText(context,"Fail",Toast.LENGTH_SHORT).show();
            }
        }
    };

    private void getImage(String url){

        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                // 下载图片,这里为伪代码
                Bitmap bitmap = DownloadUtil.download(url);
                Message msg = Message.obtain();
                
                if (bitmap != null){
                    msg.obj = bitmap;
                }else {
                    msg.obj = null;
                }
                handler1.sendMessage(msg);
            }
        };
        
        thread.start();

    }

上面是一个子线程与主线程的简单通信,Handler 发送消息的方法还有很多,我们接着来看。

// sendMessage 使用的是 sendMessageDelayed()
public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}

// sendMessageDelayed() 使用的是 sendMessageAtTime()
// 该方法指一定在 delayMillis 后才可能处理该 Message
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    // SystemClock.uptimeMillis() 方法是取不包含深度休眠(锁屏等)在内的开机到现在的时间
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
// sendMessageAtTime() 使用的是 enqueueMessage()
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);
}

Handler 还有 post() 方法,其实 post 方法也是调用的 send() 方法,传进来的 Runnable 对象赋值给了 Message 的 Callback:

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
// getPostMessage() 是 Message.obtain() 了一个 Message,并且将 Runnable 作为了 Message 的 Callback
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

那么 Message 的 Callback 是干什么的呢?上面说到,我们使用 sendMessage() 时,需要去 Handler.Callback 或者是 handleMessage() 去处理消息,而使用 post() 时,我们就可以不用在上述方法中去处理消息,当 Looper 循环从 MessageQueue 中取出这个消息时,就会运行 callback 的 run 方法。相当于代替了上面的 handleMessage 或者 Handler.Callback 的代码:

private Handler handler = new Handler(Looper.getMainLooper());
public void dealData(String msg){
    new Thread(new Runnable() {
        @Override
        public void run() {
            // 子线程中去使用 Handler post 一个消息,没有 start,所以并不是开启了一个新线程
            handler.post(new Runnable() {
                @Override
                public void run() {
                    textView.setText(msg);
                }
            });
        }
    }).start();
}

而还有更多的 post 方法,实现逻辑是一样的,只不过是调用了不同的 send() 方法。