一. 概述
在Android中,进程间通信主流的有Binder,Socket,而线程之间的通信则有Android消息机制,在分析源码之前,先复习一下如何使用。
二. 传统使用
[-> SecondActivity.java]
package com.dopezhi.handlerdemo;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class SecondActivity extends AppCompatActivity {
private final String TAG = "SecondActivity";
public final int MSG_GET = 1;
public final int MSG_RESULT = 2;
public Handler mUiHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i(TAG, "mUiHandler handleMessage thread : " + Thread.currentThread());
switch (msg.what) {
case MSG_RESULT:
Toast.makeText(getApplicationContext(), (String) msg.obj, Toast.LENGTH_LONG).show();
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
LooperThread looperThread = new LooperThread("looper_thread");
looperThread.start();
findViewById(R.id.get_second).setOnClickListener(v -> {
looperThread.mSubHandler.sendEmptyMessage(MSG_GET);
});
}
class LooperThread extends Thread {
public Handler mSubHandler;
public LooperThread(String name) {
super(name);
}
@Override
public void run() {
Looper.prepare();
mSubHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i(TAG, "mSubHandler handler handleMessage thread : " + Thread.currentThread());
switch (msg.what) {
case MSG_GET:
double number = Math.random();
Message message = new Message();
message.what = MSG_RESULT;
message.obj = "dopezhi : " + number;
mUiHandler.sendMessage(message);
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.handlerdemo;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private final static String TAG = "MainActivity";
protected final int MSG_GET = 1;
protected final int MSG_RESULT = 2;
private HandlerThread mHandlerThread;
//子线程中的Handler实例
private Handler mSubTreadHandler;
private Handler mUiHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i(TAG, "mUiHandler handleMessage thread : " + Thread.currentThread());
switch (msg.what) {
case MSG_RESULT:
Toast.makeText(getApplicationContext(), (String) msg.obj, Toast.LENGTH_LONG).show();
break;
default:
throw new IllegalStateException("Unexpected value: " + msg.what);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "onCreate thread : " + Thread.currentThread());
//点击该按钮,向子线程handler发消息
findViewById(R.id.get).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSubTreadHandler.sendEmptyMessage(MSG_GET);
Log.i(TAG,"I frist");
}
});
findViewById(R.id.jumoToSecond).setOnClickListener(v -> {
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
startActivity(intent);
});
initHandlerThread();
}
private void initHandlerThread() {
//创建HandlerThread实例
mHandlerThread = new HandlerThread("handler_thread");
//开始运行线程
mHandlerThread.start();
//获取HandlerThread线程中的Looper实例
Looper loop = mHandlerThread.getLooper();
//NEW,当消息队列没有消息时,会回调到该idelHandler执行方法
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.i(TAG,"The queue is empty");
return false;
}
});
//创建Handler与线程绑定
mSubTreadHandler = new Handler(loop) {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i(TAG, "mSubThreadHandler handleMessage thread : " + Thread.currentThread());
switch (msg.what) {
case MSG_GET:
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG,"sorry , I first");
double number = Math.random();
String result = "dopezhi: " + number;
//向UI线程发送消息,更新UI
Message message = new Message();
message.what = MSG_RESULT;
message.obj = result;
//子线程收到消息后,向主线程Handler发送消息
mUiHandler.sendMessage(message);
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。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END