Android經常使用異步任務執行方法

Handler原理及基本概念

  • Message 意爲消息,發送到Handler進行處理的對象,攜帶描述信息和任意數據。
  • MessageQueue 意爲消息隊列,Message的集合。
  • Looper 有着一個很難聽的中文名字,消息泵,用來從MessageQueue中抽取Message,發送給Handler進行處理。
  • Handler 處理Looper抽取出來的Message。

在以下操做中都是基於UI主線程,在異步任務中使用Handler機制更新UI必須用new Handler();來初始化。java

// 默認使用UI主線程的Looper
Handler mHandler = new Handler();
mHandler.post(new Runnable(){});

Thread建立與銷燬

在Android開發中常常會使用到線程,一想到線程,不少同窗就當即使用
new Thread(){...}.start();這樣的方式。這樣若是在一個Activity中屢次調用上面的代碼,那麼將建立多個匿名線程,程序運行的越久可能會愈來愈慢。所以,須要一個Handler來啓動一個線程,以及刪除一個線程。
保證線程不會重複的建立。異步

使用HandlerThread和Handler配合實現異步後臺任務

特色async

  • 由2個Handler和1個HandlerThread來實現
  • 後臺線程串行執行

代碼示例:ide

// UI線程的Handler
Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 處理UI更新
};

HandlerThread mBackThread = new HandlerThread("mybackthread");
mBackThread.start();
// 後臺線程的Handler
Handler mBackHandler = new Handler(mBackThread.getLooper());
mBackHandler.post(new Runnable() {
    @Override
    public void run() {
        // 後臺線程執行耗時操做,異步
        ...
        // mHandler發消息,回到主線程更新UI
        mHandler.sendMessage(msg);
    }
});

注意mBackHandler的初始化必須在mBackThread.start();以後,不然拿不到這個線程的looper。
這種模式經過mBackHandler.post(new Runnable() {})來實現後臺異步任務執行,全部後臺任務都是經過HandlerThread這個線程執行的,可是HandlerThread是串行執行任務的,也就是每次post後進入隊列排隊執行。
HandlerThread的退出:oop

@Override
protected void onDestroy() {
    super.onDestroy();
    if(mBackThread != null){
        mBackThread.quitSafely();
        try {
            mBackThread.join();
            mBackThread = null;
            mBackHandler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

AsyncTask實現異步任務執行

查看源碼AsyncTask只是對Thread和Handler的一個封裝。post

基本概念

  • 3個泛型參數
    AsyncTask <Params, Progress, Result>
    Params: 指定的是咱們傳遞給異步任務執行時的參數的類型
    Progress: 指定的是咱們的異步任務在執行的時候將執行的進度返回給UI線程的參數的類型
    Result: 指定的是異步任務執行完後返回給UI線程的結果的類型
    咱們在定義一個類繼承AsyncTask類的時候,必須指定好這三個泛型的類型,若是都不指定的話,則都將其寫成Void
  • 4個執行步驟
    onPreExecute():UI Thread當中執行,這個方法是在執行異步任務以前的時候執行,咱們能夠在異步任務執行前作UI提示
    doInBackground(Params... params):這個方法就是來處理異步任務的方法,執行耗時操做。這個方法也是必需要實現的抽象方法。
    onProgressUpdate(Progess... values):UI Thread當中執行,用來更新進度條等
    onPostExecute(Result... result):UI Thread當中執行,當異步任務執行完以後,將doInBackground結果返回給這個方法來更新UI
  • 2種執行方式
    後臺線程能夠設置爲串行或者並行執行
    串行execute(Params... params)
    並行executeOnExecutor(Executor exec, Params... params)
    注意:各SDK版本execute默認執行方式不統一,1.5中順序執行,1.6到 2.3中並行執行,3.0之後又改回串行執行,並添加並行執行接口executeOnExecutor

注意事項

  • 必須在UI線程中加載和建立,以及調用execute
  • 不能作特別耗時的操做,建議只幾秒內的異步任務
  • 一個AsyncTask對象只能被執行一次,即只能調用一次execute,不然會拋出異常報錯
    Caused by: java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
  • 不能在程序中主動調用4個步驟

代碼示例

// 初始化AsyncTask及執行
protected void function() {
    ...
    // 串行執行,識別一張bitmap,每次執行前都須要從新new一個對象
    mClassifierAsyncTask = new ClassifierAsyncTask();    
    mClassifierAsyncTask.execute(bitmap);
}

// 自定義AsyncTask任務類,實現doInBackground
private ClassifierAsyncTask mClassifierAsyncTask;
private class ClassifierAsyncTask extends AsyncTask<Bitmap , Void, String >{

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mTvResult.setText(getString(R.string.classifying));
    }

    @Override
    protected String doInBackground(Bitmap... bitmaps) {
        if(mMyTfClassifier == null) {
            mMyTfClassifier = new MyTfClassifier(MainActivity.this);
        }
        String result = mMyTfClassifier.recognizeImage(bitmaps[0]);
        return result;
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        mTvResult.setText(result);
    }
}

存在的問題

Activity屏幕旋轉或銷燬時,若是AsyncTask沒有執行完畢就會存在內存泄露。特別是屏幕旋轉時AsyncTask沒有執行完畢,會致使屏幕異常。ui

相關文章
相關標籤/搜索