在 Android 开发中,需要把耗时操作放到子线程中,避免阻塞主线程,从而导致程序 ANR。实现这类异步任务的方式有:
- Thread + Handler
- AsyncTask
- HandlerThread
- IntentService
本文来讲解分析下 HandlerThread,在真正开始前,我们先了解下 Handler 的使用方式。
Handler 机制
子线程中创建 Handler
public class MainActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
handler = new Handler();
}
}).start();
}
}
程序崩溃了,意思是说:在线程中没有调用 Looper.prepare() 方法,是不能创建 Handler 对象的。
原因分析:
我点进 Handler 构造函数源码看下,会发现在创建 Handler 对象时,系统会检验当前线程中是否存在 Looper 对象,如果没有,则会抛出异常。
Handler 源码:
/**
Handler.java
*/
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); // 获取当前线程的 Looper 对象
if (mLooper == null) {
throw new RuntimeException( // 没有Looper 对象,则需要调用 Looper.prepare()创建 Looper
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
那么为什么在主线程中创建 Handler 对象,没有调用 Looper.prepare() 方法,程序没有崩溃呢?这是因为主线程在创建时,系统帮我们创建好了 Looper 对象。看下程序入口 ActivityThread.main() 源码:
/**
* ActivityThread.java
*/
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper(); // 获取主线程中的Looper对象,该方法最终会调用Looper构造函数
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
结论:在线程中创建 Handler 对象,需要存在该线程的 Looper 对象。子线程需要我们手动创建 Looper 对象,即在该线程中调用 Looper.prepare()创建,并用 Looper.loop() 方法启动轮询;主线程中系统帮我们创建好了 Looper 对象。
常规用法:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare(); // 创建 Looper
workerHandler = new Handler();
Looper.loop(); // 开启轮询
}
}).start();
Handler 不同线程之间通信
(1)子线程向子线程发送消息
子线程和子线程发消息,就是在一个子线程中创建 Handler ,这样回调 handleMessage()就自然会在子线程中,然后,在另一个子线程中使用该 handler 进行发送消息。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "xinxing.tao";
private Handler workerHandler;
private WorkerThread workerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
workerThread = new WorkerThread("WorkerThread");
workerThread.start();
// 在另一个子线程中发送消息给 WorkerThread 子线程
new Thread(new Runnable() {
@Override
public void run() {
// sleep(1000) 确保 WorkerThread 已经初始化好了 workerHandler
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 6; i++) {
Log.d(TAG, "sendMessage :name = " + Thread.currentThread().getName() + ", i = " + i);
Message message = new Message();
message.what = i;
message.obj = "WorkerThread Message";
workerHandler.sendMessage(message);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 停止 WorkThread,因为 WorkThread run()通过 Looper.loop() 死循环轮询,所以需要拿到他的 Looper 进行停止。
workerHandler.getLooper().quit();
}
}).start();
}
class WorkerThread extends Thread {
public WorkerThread(String name) {
super(name);
}
@Override
public void run() {
// 子线程创建时,没有创建 Looper 对象,必须手动调用 prepare() 方法创建 Looper
Looper.prepare();
workerHandler = new Handler() {
// 子线程中创建 Handler, 回调自然在子线程中
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage: name = " + Thread.currentThread().getName() + ", msg.what= " + msg.what + " ,msg.obj = " + msg.obj);
}
};
Log.d(TAG, "run: end ??????");
Looper.loop(); // 开启Looper轮询,直到 Looper 停止退出轮询,才能执行后面的代码
Log.d(TAG, "run: end");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
(2)主线程向子线程发消息(Thread方式)
在子线程中创建 Handler(需要创建Looper对象),然后使用该 Handler 在主线程中发送消息。
public class MainThread2WorkerThreadActivity extends AppCompatActivity {
private static final String TAG = "debug";
private Handler workerHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_thread2_worker_thread);
// 子线程
WorkerThread workerThread = new WorkerThread("WorkerThread");
workerThread.start();
}
public void click(View view) {
// send message
Message message = new Message();
message.what = 100;
message.obj = "message from main to worker thread";
workerHandler.sendMessage(message);
Log.d(TAG, "sendMessage : " + Thread.currentThread().getName());
}
class WorkerThread extends Thread {
public WorkerThread(String name) {
super(name);
}
@Override
public void run() {
super.run();
// 子线程中使用 Handler,需要手动创建 Looper 对象
Looper.prepare();
workerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage: name = " + Thread.currentThread().getName() + ", msg.what = " + msg.what + ",msg.obj = " + msg.obj);
}
};
// 开启轮询
Looper.loop();
}
}
}
输出结果:
D/debug: sendMessage : main
D/debug: handleMessage: name = WorkerThread, msg.what = 100,msg.obj = message from main to worker thread
可以看到在子线程中使用 Handler 写法还是挺麻烦的,好在 Android 已经为我们提供更方便的使用方式,即 HandlerThread。
HandlerThread 解析
HandlerThread 介绍
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
用于方便开启子线程的类,内部包含 Looper , 可用来创建 Handler 类,注意,必须调用 start() 方法。
HandlerThread 常规用法
创建 HandlerThread 对象
HandlerThread handlerThread = new HandlerThread("handler-thread"); // 名字随意取
启动 HandlerThread
handlerThread.start();
创建子线程 Handler,用于处理异步任务,并与 handlerThread 进行关联。
Handler workerHandler = new Handler(handlerThread.getLooper()){ public void handleMessage() { // 执行耗时操作,运行于子线程中 } }
Demo1 : 以下代码使用 HandlerThread 方式完成主线程向子线程发送消息:
public class MainThread2WorkerThreadActivity extends AppCompatActivity {
private static final String TAG = "debug";
private HandlerThread handlerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_worker_thread2_main_thread);
// 创建,开启子线程 handlerThread
handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
// 创建 workerHandler对象,传入的参数是 handlerThread 中的 Looper,即在 handlerThread这个子线程中创建 handler
Handler workerHandler = new Handler(handlerThread.getLooper()) {
// 这个方法运行在 handlerThread 子线程中,可以执行耗时操作
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage: name = " + Thread.currentThread().getName() + ", msg.what = " + msg.what); // handleMessage: name = HandlerThread, msg.what = 11
}
};
// 主线程中使用 workerHandler 发送消息
Log.d(TAG, "sendMessage: name = " + Thread.currentThread().getName()); // sendMessage: name = main
Message message = new Message();
message.what = 11;
message.obj = "message";
workerHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 退出 handlerThread looper 循环
handlerThread.quit();
}
}
输出结果:
D/debug: sendMessage: name = main
D/debug: handleMessage: name = HandlerThread, msg.what = 11
Demo2 : 自定义 HandlerThread , 继承自 HandlerThread 类,构造 workerHandler 传入的 looper 是 HandlerThread 中的 looper 对象,所以 workerHandler 中的 handleMessage() 回调运行于子线程中。
// DownloadHandlerThread.java
public class DownloadHandlerThread extends HandlerThread implements Handler.Callback {
public static final int WHAT_START_DOWNLOAD = 1;
public static final int WHAT_FINISH_DOWNLOAD = 2;
private List<String> urlList = new ArrayList<>();
private Handler uiHandler;
private Handler workerHandler;
public DownloadHandlerThread() {
super("DownloadHandlerThread");
}
/**
* 构造 workerHandler 时,传入的 Looper 是 HandlerThread 中的 looper,
* 所以 workerHandler 中的 handleMessage() 是运行于子线程中。
*/
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
//
workerHandler = new Handler(getLooper(), this); // 使用HandlerThread子线程中的 looper
// 将接收到的任务消息挨个添加到消息队列中
for (String url : urlList) {
Message msg = workerHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("url", url);
msg.setData(bundle);
workerHandler.sendMessage(msg);
}
}
/**
* 子线程中处理任务,完成后发送消息给主线程
*
* @param msg
* @return
*/
@Override
public boolean handleMessage(Message msg) {
if (msg == null || msg.getData() == null) {
return false;
}
String url = msg.getData().getString("url");
// 下载之前通知主线程显示下载提示
Message startMsg = uiHandler.obtainMessage();
startMsg.what = WHAT_START_DOWNLOAD;
startMsg.obj = url + " = start download ";
// 发送消息给主线程
uiHandler.sendMessage(startMsg);
// 开始下载
SystemClock.sleep(2000); // 模拟下载过程
// 每个 URL 下载完成通知主线程更新ui
Message finishMsg = uiHandler.obtainMessage();
finishMsg.what = WHAT_FINISH_DOWNLOAD;
finishMsg.obj = url + " = finish download";
uiHandler.sendMessage(finishMsg);
return true;
}
public void setUrlList(List<String> list) {
this.urlList = list;
}
/**
* 注入主线程 handler
*
* @param handler
*/
public void setUiHandler(Handler handler) {
this.uiHandler = handler;
}
@Override
public boolean quitSafely() {
uiHandler = null;
return super.quitSafely();
}
}
// MainActivity.java
public class MainActivity extends AppCompatActivity implements Handler.Callback {
private String[] urls = {"http://www.baidu.com", "http://www.sina.com", "http://google.com"};
private DownloadHandlerThread handlerThread;
private TextView startTextView;
private TextView finishTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startTextView = findViewById(R.id.tv_tip);
finishTextView = findViewById(R.id.tv_finish);
Handler uiHandler = new Handler(this);
handlerThread = new DownloadHandlerThread();
handlerThread.setUrlList(Arrays.asList(urls));
handlerThread.setUiHandler(uiHandler);
}
public void click(View view) {
handlerThread.start();
}
/**
* 主线程回调
*
* @param msg
* @return
*/
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case DownloadHandlerThread.WHAT_START_DOWNLOAD:
startTextView.setText(startTextView.getText().toString() + "\n" + msg.obj);
break;
case DownloadHandlerThread.WHAT_FINISH_DOWNLOAD:
finishTextView.setText(finishTextView.getText().toString() + "\n" + msg.obj);
break;
}
return true;
}
}
HandlerThread 源码分析
/**
* HandlerThread.java
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
可以看到,HandlerThread 继承自 Thread ,所以本质上 HandlerThread 还是 Thread,其 run() 方法执行于子线程中。当我们使用 HandlerThread 时,通过调用它的 start() 开启异步任务时,即会调用 HandlerThread 的 run() 方法,在 run() 方法,调用 Looper.prepare() 创建该线程的 Looper 对象,然后调用了 Looper.loop() 开启消息的轮询,loop() 方法是一个无限循环,不断的从消息队列 MessageQueue 中拿到消息进行处理。由于消息队列是由链表结构构成的,所以异步任务时串行执行的。