在 Android 中,由于主線程負責維護 UI,不能被阻塞,那么在通過磁盤或者網絡進行異步加載數據的時候就需要使用多線程了。以下是我整理的幾種使用多線程執行異步操作的方式,如有紕漏歡迎指正。
-
Handler
Android 中多線程通信基本的方式是使用 Handler 機制,基本使用方式如下。
//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,又可以避免內存泄漏。
-
AsyncTask
AsyncTask 方式是官方提供的用來簡化手動寫 Handler 的一種異步機制,其內部仍使用 Handler 實現。
需要繼承AsyncTask<Params, Progress, Result>類,重寫doInBackground,onPostExecute等回調方法,使用的時候調用 execute 方法(只能在 UI 線程調用)傳入參數就可以方便的執行 IO 或網絡等耗時操作并在操作完成時更新 UI。
使用 AsyncTask 同樣需要注意內存泄漏問題。
-
Activity.runOnUiThread(Runnable action)
算是 Handler 方式的一種語法糖吧,使用了 Activity 自身維護的 一個 mHandler 實例,便于在 UI 線程執行操作,比如異步獲取數據后更新 UI。
-
Loader
Loader 是在 Android 3.0 引入的用于在 Activity 和 Fragment 中簡化異步加載數據的方式。
系統提供了 AsyncTaskLoader<D> 和 CursorLoader 兩個子類。
View.post(Runnable action)/View.postDelayed(Runnable action, long delayMillis)
-
RxJava
使用 RxJava 可以方便地進行多線程調度,通過調用 Observable.subscribeOn 和 Observable.observeOn,并使用 Schedulers.io() 和 Schedulers.mainThread() 等工廠方法傳入參數即可自由切換線程。
-
EventBus
使用 EventBus 可以在任意線程發布數據,并通過訂閱方法的命名約定規定在何種線程執行訂閱的回調方法。
// 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); }