一. 概述
在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。