《Android 高級進階》讀書筆記
Android中,異步處理技術有很多種,常見的有Thread、AsyncTask、Handler&Looper、Executors等,在實際項目中,我們需要根據具體業務需求進行選擇、一個完整的異步處理技術繼承樹如下:
1. Thread
線程是Java語言的一個概念,它是實際執行任務的基本單元,創建線程有兩種方法。
- 繼承Thread類並重寫run方法,語句如下:
public class MyThread extends Thread {
@Override
public void run() {
// 具體實現邏輯
}
}
// 需要的地方調用
MyThread myThread = new MyThread();
myThread.start(); // 使用start啓動線程
- 實現Runnable接口並實現run方法,如下:
public class MyRunnable implements Runnable {
@Override
public void run() {
// 實現具體邏輯
}
}
// 需要調用的地方
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
Android應用中各種類型的線程本質上都是基於Linux系統的pthreads,在應用層可以分爲三種類型的線程。
- 主線程:主線程也稱爲UI線程,隨着應用啓動而啓動,主線程用來運行Android組件,同時刷新屏幕上的UI元素。主線程中創建的Handler會順序執行接收到的消息,包括從其他線程中發送的消息。如果消息隊列中前面的消息沒有很快執行完,那麼它很可能會阻塞隊列中的其他消息的及時處理。
- Binder線程:Binder線程用於不同進程之間線程的通信,每個進程都維護了一個線程池,用來處理其他進程中線程發送的消息,這些進程包括系統服務,Intents、ContentProviders和Service等。一個典型的需要使用Binder線程的場景是應用提供一個給其他進程通過AIDL接口綁定的Service。
- 後臺線程:在應用中顯式創建的線程都是後臺線程,也就是當剛創建出來時,這些線程的執行體是空的,需要手動添加任務。在Linux系統層面,主線程和後臺線程是一樣的。在Android框架中,通過WindowManager賦予了主線程只能處理UI更新以及後臺線程不能直接操作UI的限制。
2. HandlerThread
HandlerThread是一個集成了Looper和MessageQueue的線程,當啓動HandlerThread時,會同時生成Looper和MessageQueue,然後等待消息進行處理,它的run方法源碼如下:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
使用HandlerThread的好處是開發者不需要自己去創建和維護Looper,它的用法和普通線程一樣
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 處理需要處理的事務
}
};
HandlerThread中只有一個消息隊列,隊列中的消息是順序執行的,因此是線程安全的,當然吞吐量受影響。隊列中的任務可能會被前面沒有執行完的任務阻塞。HandlerThread的內部機制確保了在創建Looper和發送消息之間不存在競態條件,這個是通過將HandlerThread.getLooper()實現爲一個阻塞操作實現的,只有當HandlerThread準備好接收消息之後纔會返回,源碼:
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;
}
如果具體業務要求在HandleThread開始接收消息之前要進行某些初始化操作的話,可以重寫HnadlerThread的onLooperPrepared函數,例如可以在這個函數中創建與HandlerThread關聯的Handler實例,這同時也可以對外隱藏我們的Handler實例
public class MyHandlerThread extends HandlerThread {
private Handler mHandler;
public MyHandlerThread(String name) {
super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
// Handler message
break;
case 2:
// Handler message
break;
}
}
};
}
public void publishedMethod1() {
mHandler.sendEmptyMessage(1);
}
public void publishedMethod2() {
mHandler.sendEmptyMessage(1);
}
}
3. AsyncQueryHandler
AsyncQueryHandler是用於在ContentProvider 上面執行異步的CRUD操作的工具類,CRUD操作會被放到一個單獨的子線程中執行,當操作結束獲取到結果後,將通過消息的方式傳遞給調用AsyncQueryHandler的線程。通常就是主線程。
AsyncQueryHandler是一個抽象類,繼承自Handler,通過封裝ContentResolver,HandlerThread,Handler等實現對ContentProvider的異步操作。源碼:
// 查詢
public void startQuery(int token, Object cookie, Uri uri,
String[] projection, String selection, String[] selectionArgs,
String orderBy) {
...
}
// 插入
public final void startInsert(int token, Object cookie, Uri uri,
ContentValues initialValues) {
...
}
// 更新
public final void startUpdate(int token, Object cookie, Uri uri,
ContentValues values, String selection, String[] selectionArgs) {
...
}
// 刪除
public final void startDelete(int token, Object cookie, Uri uri,
String selection, String[] selectionArgs) {
...
}
AsyncQueryHandler的子類實現可以根據實際需求實現回調函數
public class MyAsyncQueryHandler extends AsyncQueryHandler {
public MyAsyncQueryHandler(ContentResolver cr) {
super(cr);
}
@Override
protected void onDeleteComplete(int token, Object cookie, int result) {
super.onDeleteComplete(token, cookie, result);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
super.onQueryComplete(token, cookie, cursor);
}
@Override
protected void onInsertComplete(int token, Object cookie, Uri uri) {
super.onInsertComplete(token, cookie, uri);
}
@Override
protected void onUpdateComplete(int token, Object cookie, int result) {
super.onUpdateComplete(token, cookie, result);
}
}
4. IntentService
IntentService和Service一樣的生命週期,同時也提供了在後臺線程中處理異步任務的機制。與HandlerThread類似,IntentService也是在一個後臺線程中順序執行所有的任務。通過Context.startService傳遞一個Intent類型的參數可以啓動IntentService的異步執行。當後臺線程隊列中所有任務處理完成之後,IntentService將會結束它的生命週期,因此IntentService不需要開發者手動結束。
IntentService本身是一個抽象類,因此,使用前需要集成它並實現onHandleIntent方法,在這個方法中實現具體的後臺處理業務邏輯,同時在子類的構造方法中需要調用super(String name)傳入子類的名稱,語句如下:
public class SimpleIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public SimpleIntentService(String name) {
super(SimpleIntentService.class.getName());
setIntentRedelivery(true);
}
@Override
protected void onHandleIntent(Intent intent) {
// 這個方法是在後臺線程中調用的
}
}
setIntentRedelivery()方法設置爲true,那麼IntentService的onStartCommand方法將會返回START_REDELIVER_INTENT。這時,如果onHandleIntent方法返回之前進程死掉了,那麼進程將會重新啓動,intent將會重新投遞。
不要忘記在AndroidManifest.xml文件中註冊SimpleIntentService.
<service android:name=".SimpleIntentService"/>
再來看下IntentService的源碼,事實上IntentService是通過HandlerThread來實現後臺任務處理的,代碼邏輯很簡單,如下:
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*
* <p>If enabled is true,
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_REDELIVER_INTENT}, so if this process dies before
* {@link #onHandleIntent(Intent)} returns, the process will be restarted
* and the intent redelivered. If multiple Intents have been sent, only
* the most recent one is guaranteed to be redelivered.
*
* <p>If enabled is false (the default),
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
* dies along with it.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
...
}
5. Executor Framework
爲了解決頻繁出現線程的創建和銷燬影響應用的性能,使用Java Executor框架可以通過線程池等機制解決這個問題,改善應用體驗。Executor框架爲開發者提供如下功能:
- 創建工作線程池,同時通過隊列來控制能夠在這些線程執行的任務的個數
- 檢測導致線程意外終止的錯誤
- 等待線程執行完成並獲取執行結果
- 批量執行線程,並通過固定的順序獲取執行結果
- 在合適的時機啓動後臺線程,從而保證線程執行結果可以很快反饋給用戶
Executor 框架的基礎是一個名爲Executor 的接口定義,Executor 的主要目的是分離任務的創建和它的執行,最終實現上述所說的功能點。
public interface Executor{
void execute(Runnable command)
}
開發者可以通過實現Executor 接口並重寫execute 方法從而實現自己的Executor 類,最簡單的是直接在這個方法中創建一個線程來執行Runnable。
public class SimpleExecutor implements Executor {
@Override
public void execute(Runnable command) {
new Thread(command).start();
}
}
當然實際應用中很少是這麼簡單,通常需要增加類似隊列,任務優先級等參數,最終實現一個線程池。線程池是任務隊列和工作線程的集合,這兩者組合起來實現生產消費者模式。Executor框架爲開發者提供了預定義的線程池實現
- 固定大小的線程池:通過Executors.newFixedThreadPool(n)創建,其中n表示線程池中線程的個數。
- 可變大小的線程池:通過Executors.newCachedThreadPool()創建,當有新的任務需要執行時,線程池會增加新的線程來處理它,空閒的線程會等待60秒來執行新任務,當沒有任務時自動銷燬,因此可變大小線程池也會根據任務隊列的大小而變化。
- 單個線程的線程池:通過Executors.newSingleThreadExecutor()創建,這個線程池中永遠只有一個線程來執行串行執行任務列表中的任務。
預定義的線程池都是基於ThreadPoolExecutor類之上構建的,而通過ThreadPoolExecutor開發者可以自定義線程池的一些行爲,我們主要來看看這個類的構造函數定義。
// 需要如下幾個參數
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
}
- corePoolSize:核心線程數,核心線程會一直存在於線程池中,即使當前沒有任務需要處理;當線程數小於核心線程數時,即使當前有空閒的線程,線程池也會優先創建新的線程來處理任務。
- maximumPoolSize:最大線程數,當線程數大於核心線程數,且任務隊列已經滿了,這時線程池就會創建新的線程,直到線程數量達到最大線程數爲止。
- keepAliveTime:線程的空閒存活時間,當線程的空閒時間超過這個時,線程會被銷燬,直到線程數等於核心線程數。
- unit:keepAliveTime的單位,可選的有TimeUnit類中的NANOSECONDS、MICROSECONDS、MILLISECONDS和SECONDS。
- workQueue:線程池所使用的任務緩存隊列。
6. AsyncTask
AsyncTask是在Executor框架基礎上進行的封裝,它實現將耗時任務移動到工作線程中執行,同時提供方便的接口實現工作線程和主線程的通信,使用AsyncTask一般會用到如下方法。
public class FullTask extends AsyncTask{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Object doInBackground(Object[] params) {
return null;
}
@Override
protected void onProgressUpdate(Object[] values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
}
@Override
protected void onCancelled(Object o) {
super.onCancelled(o);
}
}
除了doInBackground方法是在工作線程中執行,其他的都是在主線程中執行。如果使用AsyncTask執行的任務需要並行執行的話,那麼在API Level大於13的版本上建議使用executeOnExecutor代替execute。
7. Loader
Loader是Android3.0開始引入的一個異步數據加載框架,它使得在Activity或者Fragment中異步加載數據變得很簡單,同時它在數據發生變化時,能夠及時發出消息通知。Loader框架涉及的API主要如下:
- Loader:加載器框架的基類,封裝了實現異步數據加載的接口,當一個加載器被激活後,它就會開始監視數據源並在數據發生改變時發送新的結果。
- AsyncTaskLoader:Loader的子類。它是基於AsyncTask實現的異步數據加載,它是一個抽象類,子類必須實現loadInBackground方法,在其中進行具體的數據加載操作。
- CursorLoader:AsyncTaskLoader的子類,封裝了對ContentResolver的query操作,實現從ContentProvider中查詢數據的功能。
- LoaderManager:抽象類,Activity和Fragment默認都會關聯一個LoaderManager的對象,開發者只需要通過getLoaderManager即可獲取。LoaderManager是用來管理一個或多個加載器對象的。
- LoaderManager.LoaderCallbacks:LoaderManager的回調接口,主要有如下三個方法
- onCreateLoader():初始化並返回一個新的Loader實例
- onLoadFinished():當一個加載器完成加載過程之後會回調這個方法
- onLoaderReset():當一個加載器被重置並且數據無效時會回調這個方法
一個例子看懂所有:
public class ContactActivity extends ListActivity implements LoaderManager.LoaderCallbacks {
private static final int CONTACT_NAME_LOADER_ID = 0;
// 這裏的PROJECTION 只獲取ID和用戶名兩個字段
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[]{
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME
};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
initAdapter();
// 通過LoaderManager 初始化Loader,這會回調到onCreateLoader
getLoaderManager().initLoader(CONTACT_NAME_LOADER_ID, null, this);
}
SimpleCursorAdapter mAdapter;
private void initAdapter() {
new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null,
new String[]{ContactsContract.Contacts.DISPLAY_NAME},
new int[]{android.R.id.text1}, 0);
setListAdapter(mAdapter);
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
// 實際創建Loader的地方,此處使用CursorLoader
return new CursorLoader(this, ContactsContract.Contacts.CONTENT_URI,
CONTACTS_SUMMARY_PROJECTION, null, null,
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
}
@Override
public void onLoadFinished(Loader loader, Object data) {
// 後臺線程中加載完數據後,回調這個方法將數據傳遞給主線程
mAdapter.swapCursor((Cursor) data);
}
@Override
public void onLoaderReset(Loader loader) {
// Loader 被重置後的回調,在這裏可以重新刷新頁面數據
mAdapter.swapCursor(null);
}
}
8. 總結
Android 平臺提供瞭如此多的異步處理技術,我們在進行選擇的時候需要根據具體的業務需求而定,綜合考慮一下幾個因素:
- 儘量使用更少的系統資源,例如CPU和內存等
- 爲應用提供更好的性能和響應度
- 實現和使用起來不復雜
- 寫出來的代碼是否符合好的設計,是否易於理解和維護