一手遮天 Android - 异步和多线程: Handler 和 Looper 的使用

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

一手遮天 Android - 异步和多线程: Handler 和 Looper 的使用

示例如下:

/async/HandlerDemo2.java

/**
 * Handler 和 Looper 的使用(Handler 用于在线程之间传递信息)
 *     本例演示主线程向后台线程发送信息
 *
 *
 * Message - 用于封装需要传递的信息(一般通过 handler 的 obtainMessage() 来构造 Message 对象,而不是 new 一个。因为 obtainMessage() 会从 Message 池中拿一个不用的过来,这样会省去创建对象的开销)
 *     what - 信息类型(以便接收方区分不同类型的信息)
 *     arg1, arg2 - 自定义整型参数
 *     obj - 具体的信息对象
 * Handler - 与相关的 MessageQueue 和 Looper 形成绑定关系,有两个作用
 *     1、将信息发送到 Handler 所属线程的 MessageQueue
 *     2、接收 Looper 获取到的信息,以便在 Handler 所属线程进行处理
 * MessageQueue - 用于保存 Message 对象的消息队列
 * Looper - 线程的 MessageQueue 管理者,循环从 MessageQueue 获取消息,并交给 Handler 处理
 *
 *
 * 注:
 * 1、主线程已经启动 Looper 了,不用再 Looper.prepare()/Looper.loop() 了
 * 2、新开线程需要自己写 Looper.prepare()/Looper.loop()/myLooper.quit()
 */

package com.webabcd.androiddemo.async;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.webabcd.androiddemo.R;

public class HandlerDemo2 extends AppCompatActivity {

    private TextView _textView1;
    private Button _button1;
    private Button _button2;
    private Button _button3;

    private Handler _handler;

    private final int MESSAGE_QUIT_LOOPER = -1;
    private final int MESSAGE_GET_MAINLOOPER = -2;

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

        _textView1 = (TextView) findViewById(R.id.textView1);
        _button1 = (Button) findViewById(R.id.button1);
        _button2 = (Button) findViewById(R.id.button2);
        _button3 = (Button) findViewById(R.id.button3);

        sample();
    }

    private void sample() {
        // 启动一个后台线程
        final Thread myThread = new MyThread();
        myThread.setName("myThread");
        myThread.setDaemon(true);
        myThread.start();

        // 主线程发送消息到后台线程
        _button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 本例中,线程启动后在 Looper 循环阻塞,其状态是 RUNNABLE
                // 如果退出了后台线程的 Looper,则线程被释放,即线程的状态会变为 TERMINATED
                writeMessage("后台线程的状态: " + myThread.getState());

                Message msg = _handler.obtainMessage();
                msg.what = 0;
                msg.arg1 = 1;
                msg.arg2 = 2;
                msg.obj = "我是信息";

                // send 信息到 handler 所属线程
                _handler.sendMessage(msg);
            }
        });

        // 在后台线程获取主线程的 Looper,以便在主线程上执行逻辑
        _button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                writeMessage("后台线程的状态: " + myThread.getState());

                // 可以发送一个特定的 what 值给后台线程,后台线程收到此值后通过获取主线程的 Looper 的方式,在主线程上执行逻辑
                _handler.sendEmptyMessage(MESSAGE_GET_MAINLOOPER);
            }
        });

        // 退出后台线程的 Looper
        _button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 可以发送一个特定的 what 值给后台线程,后台线程收到此值后则退出后台线程的 Looper
                _handler.sendEmptyMessage(MESSAGE_QUIT_LOOPER);
            }
        });
    }

    // 自定义 Thread
    class MyThread extends Thread {
        @Override
        public void run() {
            // 初始化当前线程的 Looper 对象
            Looper.prepare();

            // 在当前线程实例化 Handler(默认与 Looper.myLooper() 形成绑定关系)
            _handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == MESSAGE_QUIT_LOOPER) {
                        // 获取当前线程的 Looper 对象
                        Looper myLooper = Looper.myLooper();
                        if (myLooper !=  null){
                            // 退出 Looper 循环(如果不用此线程了,请退出线程的 Looper,否则 Looper 会阻塞着这个线程导致线程不会被释放)
                            // myLooper.quit() - 清空 MessageQueue 中的全部消息,并退出 Looper
                            // myLooper.quitSafely() - 将 MessageQueue 中的非延迟消息发出去,同时清空全部延迟消息,并退出 Looper(在 api level 18 或以上支持)
                            myLooper.quit();
                            writeMessage("looper quit");
                        }
                    }
                    else if (msg.what == MESSAGE_GET_MAINLOOPER) {
                        // 实例化 Handler 并与指定的 Looper 形成绑定关系(本例是与主线程的 Looper 形成绑定关系)
                        new Handler(Looper.getMainLooper()).post(new Runnable() {
                            @Override
                            public void run() {
                                _textView1.append("通过在子线程获取主线程 Looper 的方式,实现在主线程运行逻辑的目的\n");
                            }
                        });
                    }
                    else {
                        // 判断当前线程是否是主线程
                        // Looper.getMainLooper() - 获取主线程的 Looper 对象
                        // Looper.myLooper() - 获取当前线程的 Looper 对象
                        boolean isMainThread = Looper.getMainLooper() == Looper.myLooper();

                        // 本例的 Handler 是在后台线程上实例化的,所以这里的 handleMessage 也是在后台线程上执行的
                        writeMessage(String.format("thread name:%s,isMainThread:%b,收到通过 handler 的 send 发送的信息,what:%d, arg1:%d, arg2:%d, obj:%s", Thread.currentThread().getName(), isMainThread, msg.what, msg.arg1, msg.arg2, msg.obj));
                    }
                }
            };

            // 启动 Looper,开始循环从 MessageQueue 获取消息。获取到了消息则提交给 handler 处理,获取不到消息就阻塞(此后如果有任何需要此线程执行的逻辑就发消息过来)
            Looper.loop();
        }
    }

    private void writeMessage(final String message) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                _textView1.append(String.format("%s\n", message));
            }
        });
    }
}

/layout/activity_async_handlerdemo2.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="主线程向后台线程发送消息(后台线程会启动 Looper 用于获取主线程发过来的消息)" />

    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="在后台线程获取主线程的 Looper,以便在主线程上执行逻辑" />

    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="退出后台线程的 Looper" />

</LinearLayout>

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

posted @ 2021-06-02 09:38  webabcd  阅读(149)  评论(0编辑  收藏  举报