在 Android 中,由于主线程负责维护 UI,不能被阻塞,那么在通过磁盘或者网络进行异步加载数据的时候就需要使用多线程了。以下是我整理的几种使用多线程执行异步操作的方式,如有纰漏欢迎指正。

1. Handler

Android 中多线程通信基本的方式是使用 Handler 机制,基本使用方式如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//init handler on the original thread
class OriginalThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
//send message to the original thread
mHandler.post(Runnable);
mHandler.postAtTime(Runnable, long);
mHandler.postDelayed(Runnable, long);
mHandler.sendEmptyMessage(int);
mHandler.sendMessage(Message);
mHandler.sendMessageAtTime(Message, long);
mHandler.sendMessageDelayed(Message, long);

系统还内置了AsyncQueryHandler 辅助子类,方便使用 ContentResolver 进行异步查询操作。

关于 Handler,需要注意的是,当 Handler 被声明为 Activity 的非静态内部类时, Handler 会持有外部 Activity 实例的引用,Handler 生命周期比 Activity 长时会导致 Activity 实例不能被正常释放,从而引起内存泄漏。一种解决方式是将 Handler 声明为 Activity 的静态内部类或者单独的类,在 Handler 内部使用 WeakReference/SoftReference 保存对 Activity的引用,既能访问 Activity 的 View 更新 UI,又可以避免内存泄漏。

2. AsyncTask

AsyncTask 方式是官方提供的用来简化手动写 Handler 的一种异步机制,其内部仍使用 Handler 实现。

需要继承AsyncTask<Params, Progress, Result>类,重写doInBackground,onPostExecute等回调方法,使用的时候调用 execute 方法(只能在 UI 线程调用)传入参数就可以方便的执行 IO 或网络等耗时操作并在操作完成时更新 UI。

使用 AsyncTask 同样需要注意内存泄漏问题。

3. Activity.runOnUiThread(Runnable action)

算是 Handler 方式的一种语法糖吧,使用了 Activity 自身维护的 一个 mHandler 实例,便于在 UI 线程执行操作,比如异步获取数据后更新 UI。

4. Loader

Loader 是在 Android 3.0 引入的用于在 Activity 和 Fragment 中简化异步加载数据的方式。

系统提供了 AsyncTaskLoader 和 CursorLoader 两个子类。

5. View.post(Runnable action)/View.postDelayed(Runnable action, long delayMillis)

6. RxJava

使用 RxJava 可以方便地进行多线程调度,通过调用 Observable.subscribeOn 和 Observable.observeOn,并使用 Schedulers.io() 和 Schedulers.mainThread() 等工厂方法传入参数即可自由切换线程。

7. EventBus

使用 EventBus 可以在任意线程发布数据,并通过订阅方法的命名约定规定在何种线程执行订阅的回调方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Called in the same thread (default)
public void onEvent(MessageEvent event) {
log(event.message);
}
// Called in Android UI's main thread
public void onEventMainThread(MessageEvent event) {
textField.setText(event.message);
}
// Called in the background thread
public void onEventBackgroundThread(MessageEvent event){
saveToDisk(event.message);
}
// Called in a separate thread
public void onEventAsync(MessageEvent event){
backend.send(event.message);
}