1. 子线程中更新 UI

安卓不允许在子线程中直接更新 UI,我们需要通过 handle 来实现在子线程更新 UI

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static final int UPDATE_TEXT = 1;

    private TextView text;

    private Handler handler = new Handler() {

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TEXT:
                    // 在这里可以进行UI操作
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }

    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.text);
        Button changeText = (Button) findViewById(R.id.change_text);
        changeText.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.change_text:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message); // 将Message对象发送出去
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}

2. 异步消息处理机制

Android 异步消息处理机制主要由四个部分组成,Message、Handle、MessageQueue 和 Looper。

Message

Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。

MessageQueue

MessageQueue 是消息队列,它主要用于存放所有由 Handler 发送过来的消息,这部分消息会一直在消息队列中,等待被处理。每个线程中只会有一个 MessageQueue 对象。

Handle

Handler 顾名思义也就是处理者的意思,它主要用于发送和处理消息。 发送消息一般使用 handler 的 sendMessage() 方法,处理消息会调用 handleMessage() 方法。

Looper

Looper 是每个线程中 MessageQueue 的管家, 调用 loop() 方法后,就会进入到一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息,就会将其取出,并传递到 handleMessage() 方法当中。每个线程中也只会有一个 Looper 对象。

了解了 Message、Handle、MessageQueue 以及 Looper 的基本概念之后,我们再来对异步消息处理的整个过程梳理一遍。首先需要在主线程当中创建一个 Handle 对象,并重写 handleMessage()方法。然后当子线程中需要进行 UI 操作时,就创建一个 Message 对象,并通过 Handle 将这条消息发送出去。之后这条信息会被添加到 MessageQueue 的队列中等待被处理,而 Looper 则会一直尝试从 MessageQueue 中取出待处理消息,最后分发回 Handle 的 handleMessage() 方法中。由于 Handle 是在主线程中创建的,所以此时handleMessage() 方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行 UI 操作了。

2012773427f1704bddebca6.webp

一条 Message 经过这样一个流程的辗转调用后,也就是从子线程进入到主线程,从不能更新 UI 变成了可以更新 UI,整个异步消息处理机制的核心思想也就是如此了。

3. AsyncTask

我们需要创建一个类来继承 AsyncTask,AsyncTask 有三个泛型

  • Params,执行 AsyncTask 需要传入的参数,可用于在后台任务中使用。
  • Progress,后台任务执行时,若需在界面显示进度,这个泛型为进度的单位。
  • Result,任务执行完毕后,若需对结果进行返回,此泛型为返回值类型。
/**
  * 步骤1:创建 AsyncTask 子类
  * 注: 
  *   a. 继承 AsyncTask 类
  *   b. 为3个泛型参数指定类型;若不使用,可用java.lang.Void类型代替
  *   c. 根据需求,在 AsyncTask 子类内实现核心方法
  */
private class MyTask extends AsyncTask<Params, Progress, Result> {
    // 方法1:onPreExecute()
    // 作用:执行 线程任务前的操作
    // 注:根据需求复写
    @Override
    protected void onPreExecute() {
    }

    // 方法2:doInBackground()
    // 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
    // 注:必须复写,从而自定义线程任务,此方法中不能更新 UI
    @Override
    protected String doInBackground(String... params) {
        // 自定义的线程任务
        // 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
            publishProgress(count);
    }

    // 方法3:onProgressUpdate()
    // 作用:在主线程,显示线程任务执行的进度,更新 UI
    // 注:根据需求复写
    @Override
    protected void onProgressUpdate(Integer... progresses) {
    }

    // 方法4:onPostExecute()
    // 作用:接收线程任务执行结果、将执行结果显示到 UI 组件
    // 注:必须复写,从而自定义 UI 操作
    @Override
    protected void onPostExecute(String result) {
        // UI操作
    }

    // 方法5:onCancelled()
    // 作用:将异步任务设置为:取消状态
    @Override
    protected void onCancelled() {
    }
}

/**
* 步骤2:创建AsyncTask子类的实例对象(即 任务实例)
* 注:AsyncTask子类的实例必须在UI线程中创建
*/
MyTask mTask = new MyTask();

/**
* 步骤3:手动调用execute(Params... params) 从而执行异步线程任务
* 注:
*    a. 必须在UI线程中调用
*    b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
*    c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute() 
*    d. 不能手动调用上述方法
*/
mTask.execute();