Android Handler 的基本使用

1.前言

https://developer.android.google.cn/reference/android/os/Handler.html

Handler 是 Android 中线程通信的常用方式,文档如是说:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.

Handler 允许你发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。 每个 Handler 实例都与一个线程和该线程的消息队列 (MessageQueue) 相关联。 当你创建一个新的 Handler 时,它会绑定到 Looper。 它将向 Looper 的消息队列传递消息 (Message) 和可运行对象 (Runnable),并在 Looper 的线程上执行它们。

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handler 有两个主要用途: (1) 安排消息和可运行对象在未来某个时刻执行; (2) 将要在与你自己的线程不同的线程上执行的操作排队。

Scheduling messages is accomplished with the post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

消息的调度是通过 post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) 等方法来完成的。post 系列的方法允许你将 Runnable 对象放入队列,当消息队列处理该消息时 Runnable 被调用;sendMessage 系列的方法允许你将可包含数据的 Message 对象放入队列,消息会被传递到 Handler 的 handleMessage(Message) 接口进行处理,你需要实现该接口。

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

当 post 或 sendMessage 到 Handler 时,可以在消息队列准备好后立即处理该消息,也可以指定延时时间 (Delayed) 或者指定时间点 (AtTime) 处理该消息。 后两者可以让你实现超时、定时和其他基于时间的行为。

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.

当为应用程序创建进程时,其主线程专用于运行一个消息队列,该消息队列负责管理顶层应用程序对象(Activity、BroadcastReceiver 等)及其创建的任何窗口。你可以创建自己的线程,并通过 Handler 与主应用程序线程进行通信。这只需要在新线程中调用 post 或 sendMessage 方法。 然后,给定的 Runnable 或 Message 将在 Handler 的消息队列中进行调度,并在适当的时候进行处理。

2.基本使用

2.1.基本流程

post 和 sendMessage 的基本使用,子线程向主线程发消息:

    // 创建 Handler 时关联一个 Looper 消息循环
    Handler handler = new Handler(Looper.getMainLooper()) {
        // 在 Looper 对应的线程处理该消息
        @Override
        public void handleMessage(Message msg) {
            // 根据 Message what 区分不同的消息
            switch (msg.what) {
                case 0: {
                    // 从 Message 中获取传递的参数数据进行处理
                    // do some ... ...
                }
                break;
            }
        }
    };

    void doing() {
        // 在线程中执行任务
        new Thread(new Runnable() {
            @Override
            public void run() {
                // do some ... ...
                // 任务完成 send 消息给 Handler 处理
                handler.sendEmptyMessage(0);
                // 或者 post 一个回调到 Handler 线程执行
                handler.post(new Runnable() {
                    // Run 会在 Handler Looper 线程执行
                    @Override
                    public void run() {
                        // do some ... ...
                    }
                });
            }
        }).start();
    }

2.2.Message 对象

创建 Message 的几种方式:

Message m1 = handler.obtainMessage(); //通过 Handler 实例获取
Message m2 = Message.obtain(); //通过 Message 获取
Message m3 = new Message(); //创建新的 Message 实例

其中,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法:

    public final Message obtainMessage() {
        return Message.obtain(this); // this Handler 作为 target 参数
    }

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

obtain()从消息池拿一个 Message 对象,不足时 new 一个新的 Message 返回:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

Message 除了 what 属性可以用来区分消息类型,还可以携带参数:

Message msg = new Message();
// what:int 消息类型
msg.what = 1;
// arg1:int 和 arg2:int 两个 int 作为便捷参数,替代 obj
msg.arg1 = 0;
msg.arg2 = 0;
// obj:Object 存储对象
msg.obj = "Message";
// 存储复杂数据
// setData 设置内部 data,读取用 getData
Bundle bd = new Bundle();
bd.putString("name", "Bundle");
bd.putInt("code", 123);
msg.setData(bd);

handler.sendMessage(msg);

2.3.向子线程发消息

Handler 需要关联一个 Looper 对象,主线程直接用 Looper.getMainLooper() 获取,子线程需要用 Looper.prepare() 创建 Looper,再用 Looper.loop() 开启消息循环。

    Handler handler;

    void test() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e(LogTag, "Looper in");
                // 准备 Looper,不存在则创建
                Looper.prepare();
                // 获取当前线程 Looper
                handler = new Handler(Looper.myLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        // do some ... ...
                        Log.e(LogTag, "handleMessage " + msg.what);
                        // 退出消息循环
                        Looper.myLooper().quit();
                    }
                };
                // 开启消息循环
                Looper.loop();
                Log.e(LogTag, "Looper exit");
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000); // 延时,等 handler 初始化完
                    // 发送消息
                    Log.e(LogTag, "sendEmptyMessage");
                    handler.sendEmptyMessage(123);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

也可以用封装了 Looper 操作的 HandlerThread:

    Handler handler;
    HandlerThread thread;

    void test() {
        thread = new HandlerThread("test thread");
        thread.start();
        handler = new Handler(thread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                // do some ... ...
                Log.e(LogTag, "handleMessage " + msg.what);
                // 退出消息循环
                thread.quit();
                // 或者 thread.quitSafely();
            }
        };
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000); // 延时,等 handler 初始化完
                    // 发送消息
                    Log.e(LogTag, "sendEmptyMessage");
                    handler.sendEmptyMessage(123);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

3.内存泄漏 

界面 Activity finish 的时候,Handler 可能还被 MessageQueue 消息队列引用着。而前文 Handler 和 post 都用到了匿名内部类的写法:new Type(){},非静态的内部类和匿名内部类都会隐式持有外部类 Activity 的引用。Activity finish 不能立即回收,还要等 Handler 处理结束才能被回收,就造成了内存泄漏。

可以将 Handler 定义成静态内部类,静态内部类是不持有外部类的实例的。同时,为了能调用外部的实例方法,需要持有一个外部的弱引用。

public class MainActivity extends AppCompatActivity {

    private static class MyHandler extends Handler {

        // 弱引用持有 Activity, GC 回收时会被回收掉
        private WeakReference weakReference;

        public MyHandler(MainActivity activity) {
            super(Looper.getMainLooper());
            this.weakReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = weakReference.get();
            super.handleMessage(msg);
            if (null == activity) {
                return;
            }
            // do some ... ...
        }
    }

    private MyHandler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // do some ... ...

        handler = new MyHandler(this);
        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.sendEmptyMessage(123);
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        // 移除所有回调及消息
        handler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
}

4.参考

安卓文档:Handler  |  Android Developers (google.cn)

参考博客:Android——Handler详解_android handler_Yawn__的博客-CSDN博客

参考博客:Android - Handler_android handler looper_Jomurphys的博客-CSDN博客

你可能感兴趣的:(QtAndroid,Android,Handler)