Android消息机制 1 - 实战

742 阅读2分钟

一. 概述

在Android中,进程间通信主流的有Binder,Socket,而线程之间的通信则有Android消息机制,在分析源码之前,先复习一下如何使用。下面的例子模拟 点击按钮时,子线程发送消息给主线程,主线程收到消息后更新UI,并发送消息给子线程,通知子线程更新成功。

二. 传统使用

[-> MainActivity.java]

package com.dopezhi.handleDemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private final String TAG = "MainActivity";

    private final int MSG_CLICK_BUTTON = 1;
    private final int MSG_UPDATE_UI = 2;
    private final int MSG_UPDATE_DONE = 3;

    //子线程初始化
    private LooperThread mSubThread = null;

    //主线程Handler
    private Handler mUiHandler = null;


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

        createInit();

        //主线程给子线程发送click button消息
        findViewById(R.id.button).setOnClickListener(view -> {
            mSubThread.mSubHandler.sendEmptyMessage(MSG_CLICK_BUTTON);
        });

        findViewById(R.id.jump_second).setOnClickListener(view -> {
            Intent intent = new Intent(MainActivity.this, HandlerThreadActivity.class);
            startActivity(intent);

        });
    }

    private void createInit() {
        mUiHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case MSG_UPDATE_UI:
                        mSubThread.mSubHandler.sendEmptyMessage(MSG_UPDATE_DONE);
                        break;
                    default:
                        break;
                }
            }
        };

        mSubThread = new LooperThread("sub_Therad");
        mSubThread.start();
    }


    class LooperThread extends Thread {

        public Handler mSubHandler;

        public LooperThread(@NonNull String name) {
            super(name);
        }

        @Override
        public void run() {
            Looper.prepare();
            mSubHandler = new Handler(Looper.myLooper()) {
                @Override
                public void handleMessage(@NonNull Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what) {
                        case MSG_CLICK_BUTTON:
                            Log.i(TAG, "click button, update ui");
                            mUiHandler.sendEmptyMessage(MSG_UPDATE_UI);
                            break;
                        case MSG_UPDATE_DONE:
                            Log.i(TAG, "update ui done !");
                            break;
                        default:
                            break;
                    }
                }
            };
            Looper.loop();
        }
    }
}

在主线程中创建另外一个线程(looper_thread),并在looper_thread中初始化Looper,创建子线程的mSubHandler,并与子线程的Looper挂钩。

主线程中初始化mUIhandler,与主线程Looper挂钩。

点击按钮,主线程向子线程发送消息MSG_GET,子线程收到消息后,向主线程发送MSG_RESULT消息,最终主线程通过Toast展示子线程发过来的消息的内容。

三. 封装好的轮子-HandlerThread

为什么会有HandlerThread?
1.要让子线程能处理消息,首先要创建内部类继承Thread,之后调Looper.prepare,Looper.Loop,比较麻烦 2.如果主线程想拿到子线程的Looper,虽然子线程执行了Looper.prepare去创建Looper,但是并不一定马上调度到并创建好,容易造成Null Point。

[-> MainActivity.java]

package com.dopezhi.handleDemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.nfc.cardemulation.HostNfcFService;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class HandlerThreadActivity extends AppCompatActivity {

    private final String TAG = "HandlerThreadActivity";

    private final int MSG_CLICK_BUTTON = 1;
    private final int MSG_UPDATE_UI = 2;
    private final int MSG_UPDATE_DONE = 3;

    private Handler mUiHandler = null;

    private HandlerThread mHandlerThread = null;
    private Handler mSubHandler = null;

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

        createInit();

        findViewById(R.id.button2).setOnClickListener(view -> {
            mSubHandler.sendEmptyMessage(MSG_CLICK_BUTTON);
        });

    }

    private void createInit() {
        mUiHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case MSG_UPDATE_UI:
                        mSubHandler.sendEmptyMessage(MSG_UPDATE_DONE);
                        break;
                    default:
                        break;
                }
            }
        };

        mHandlerThread = new HandlerThread("subThread");
        mHandlerThread.start();
        mSubHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case MSG_CLICK_BUTTON:
                        Log.i(TAG, "click button, update ui");
                        mUiHandler.sendEmptyMessage(MSG_UPDATE_UI);
                        break;
                    case MSG_UPDATE_DONE:
                        Log.i(TAG, "update ui done !");
                        break;
                    default:
                        break;
                }
            }
        };
    }
}

可以看到,开启一个子线程并执行消息循环并不需要后面那么多步骤,只需要new HandlerThread.start就可以了。

[-> HandlerThread.java]

public class HandlerThread extends Thread

    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

可以看一下HandlerThread的源码,HandlerThread本质上也是Thread,重写了run方法,执行start时会初始化Looper,并开启Looper循环。如果在这之前调用mHanderThread.getLooper(),会wait(),直到Looper创建好调用notifyAll。