AsyncTask使用

標準的寫法

private static class MyAsyncTask extends AsyncTask<String,Integer,Boolean> {

    /* 爲避免內存泄漏,引用應該用弱引用包裹 */
    private String TAG = "MyAsyncTask";
    private WeakReference<Context> contextWeakReference;
    private WeakReference<ProgressBar> progressBarWeakReference;
    private WeakReference<TextView> textViewWeakReference;
    
    public MyAsyncTask(Context context, ProgressBar progressBar, TextView tvProgress) {
        contextWeakReference = new WeakReference<>(context);
        progressBarWeakReference = new WeakReference<>(progressBar);
        textViewWeakReference = new WeakReference<>(tvProgress);
    }
    
    /**
     * 運行在UI線程中,用於參數初始化
     * 在 doInBackground 前調用
     */
    @Override
    protected void onPreExecute() {
        Log.i(TAG,"onPreExecute...");
        Context context = contextWeakReference.get();
        if (context!=null)
            Toast.makeText(context,"開始執行",Toast.LENGTH_SHORT).show();
    }
    
    /**
     * 後臺任務處理方法,運行在
     * 子線程,可以做一些耗時任務
     * @param strings 輸入參數,對應於泛型:Params
     * @return 結果
     */
    @Override
    protected Boolean doInBackground(String... strings) {
        Log.i(TAG,"doInBackground...");
        int i=0;
        while(i<100){
            if (isCancelled())
                break;
            i++;
            //任務執行過程進度/參數的回調,對應於泛型:Progress
            publishProgress(i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException ignored) {
            }
        }
        //返回值代表最後的結果,對應於泛型:Result
        return true;
    }
    
    /**
     * 任務執行過程中的更新,
     * 由子方法:publishProgress()
     * 觸發,運行在UI線程中
     * @param values 參數
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        TextView tvProgress = textViewWeakReference.get();
        ProgressBar progressBar = progressBarWeakReference.get();
        if (tvProgress!=null)
            tvProgress.setText(values[0]+"%");
        if (progressBar!=null)
            progressBar.setProgress(values[0]);
    }
    
    /**
     * 執行結束的回調,運行在UI線程
     * @param bool 參數
     */
    @Override
    protected void onPostExecute(Boolean bool) {
        Log.i(TAG,"onPostExecute, " + bool);
        Context context = contextWeakReference.get();
        if (context!=null)
            Toast.makeText(context,"執行完畢",Toast.LENGTH_SHORT).show();
    }
    
    /**
     * 執行 task.cancel() 方法後,
     * 會回調此方法
     */
    @Override
    protected void onCancelled() {
        Log.i(TAG,"onCancelled");
    }
}

執行

findViewById(R.id.btn).setOnClickListener(v -> {
        if (myAsyncTask==null) {
            myAsyncTask = new MyAsyncTask(TestAsyncTaskActivity.this,progressBar,tvProgress);
        }
        myAsyncTask.execute("start");
});
    
if (myAsyncTask!=null)
    myAsyncTask.cancel(true);

總結

  1. 泛型類型要清楚,如果不需要可以填Void

  2. 如果寫成內部類,則注意內存泄漏,需要寫成靜態內部類,同時注意,如果需要刷新UI控件,那麼得持有UI控件得引用,那麼建議使用弱引用包裹,避免泄漏。

  3. 清楚每個回調方法執行的線程,以及其泛型參數的對應位置。

  4. doInBackgroud方法中,要及時去判斷,任務是否被 cancel 了,如果Cancel了,則要立即取消這個耗時任務。假如,任務被取消後,而 doInbackgroud 中沒有及時退出,那麼Task會執行完成方法後,再進行回調 onCanceled 方法。

if (isCancelled())
   break;
  1. 關於AsyncTask的是否並行串行,其在不同Android版本上是有差異的,具體看源碼類註釋:
* <p>When first introduced, AsyncTasks were executed serially on a single background
 * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
 * to a pool of threads allowing multiple tasks to operate in parallel. Starting with
 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
 * thread to avoid common application errors caused by parallel execution.</p>
 * <p>If you truly want parallel execution, you can invoke
 * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
 * {@link #THREAD_POOL_EXECUTOR}.</p>

Android Version < 4 :串行;

11> Android Version >= 4 :並行;

Android Version >= 11 :默認串行,可以設置爲並行;

串行的意思就是,Task任務,在多次啓動下,會排隊執行,並行就是一併執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章