Android中的線程工具類學習:AsyncTask、HandlerThread、IntentService

首先是AsyncTask

這是一個用於異步處理數據的線程操作類。由於Android中存在UI線程的概念,只有在UI線程中才能對View(肉眼可見的地方)進行修改和調整。UI線程中是不能做一些耗時地操作的,比如說訪問網絡(http請求)、從SD卡讀寫數據(I/O操作)亦或者是比較複雜的數據運算,這些都是不能放在UI線程中進行的(會導致ANR異常)。

爲了解決這個問題,Android系統提供了這個異步操作類。(以下是部分代碼)

public abstract class AsyncTask<Params, Progress, Result> {

    /**
     * Override this method to perform a computation on a background thread. The
     * specified parameters are the parameters passed to {@link #execute}
     * by the caller of this task.
     *
     * This method can call {@link #publishProgress} to publish updates
     * on the UI thread.
     *
     * @param params The parameters of the task.
     *
     * @return A result, defined by the subclass of this task.
     *
     * @see #onPreExecute()
     * @see #onPostExecute
     * @see #publishProgress
     */
    @WorkerThread
    protected abstract Result doInBackground(Params... params);

    /**
     * <p>Runs on the UI thread after {@link #doInBackground}. The
     * specified result is the value returned by {@link #doInBackground}.</p>
     * 
     * <p>This method won't be invoked if the task was cancelled.</p>
     *
     * @param result The result of the operation computed by {@link #doInBackground}.
     *
     * @see #onPreExecute
     * @see #doInBackground
     * @see #onCancelled(Object) 
     */
    @SuppressWarnings({"UnusedDeclaration"})
    @MainThread
    protected void onPostExecute(Result result) {
    }
}

這裏注意到doInBackground方法上的註解@WorkerThread和onPostExecute方法上的@MainThread,這兩個註解就提示了我們,這兩個方法是運行不同的線程上的。

通常我們會把耗時操作放在doInBackground方法中,然後將執行結果Result返回。從上面的註釋中我們可以看到onPostExecute會跟在doInBackground之後執行,不同的是onPostExecute方法是在UI線程中執行的,在這裏我們對View(肉眼可見的界面)進行操作了。還有onPostExecute方法中有一個Result參數,這個參數就是doInBackground方法中返回的Result,我們可以根據這個Result做相關的UI操作。

例子:

    public class DecodeBitmapTask extends AsyncTask<String, Integer, Bitmap> {

        private ImageView imageView;

        public VideoShotTask(ImageView imageView) {
            this.imageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(String... params) {

            String sourcePath = params[0];
            Bitmap bitmap = BitmapFactory.decodeFile(sourcePath);

            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap result) {

            if (imageView != null && result != null) {
                imageView.setImageBitmap(result);
            }

            this.cancel(true);//執行完成之後可以取消任務
        }
    }

這就是一個從sd卡中加載圖片,並繪製到ImageView上的過程。

使用上面這個類的方法也很簡單

DecodeBitmapTask dbt = new DecodeBitmap(傳入一個ImageView);
dbt.execute("傳入一個圖片資源路徑");

這樣就可以實現工作線程(子線程)與UI線程(主線程)之間的配合啦。

ps:當然AsyncTask的功能不止於此,我這裏只是做個簡單的例子。

 

HandlerThread

就像它的名字一樣,它是由Handler和Thread有機結合起來的一個類。它區別於我們直接通過new Thread的方式創建線程,HandlerThread創建的線程是可以反覆重用的,也就減少了因爲new新的實例而導致的內存開銷。而且它Handler中功能,使得我們對線程的使用更加的靈活。

   //首先創建一個HandlerThread對象
   HandlerThread mHT = new HandlerThread("傳入線程的名字");

   //隨後啓動線程
   mHT.start();

   //根據HandlerThread的Looper來創建一個Handler對象
   Handler workHandler = new Handler( mHT.getLooper() ) {
       @Override
       public void handleMessage(Message msg) {
           //這裏是運行在工作線程中,而不是UI線程
           switch(msg.what){
              case 2:
                //do something。。。 
                break;
           }   
       }
   });

  //這裏通過傳統的Handler發送消息的方式,來觸發線程
  Message msg = Message.obtain();
  msg.what = 2; //消息的標識
  msg.obj = "B"; // 消息的存放
  workHandler.sendMessage(msg);

  // 最後不需要用的時候,結束線程,即停止線程的消息循環
  mHT.quit();

如此一來,我們就可以通過Handler來操作、執行工作線程。這樣做的好處就是減少了我們new Thread的操作,這樣可以大大的減少內存泄漏的風險和過多的內存開銷。

 

IntentService

這是一個Service與HandlerThread的結合體。首先我們知道Service是android的四大組件之一,長期在後臺處理數據。但是它並不是一個線程,如果在Service中執行耗時操作就會產生ANR異常,那麼當一個Service需要處理比較多的耗時任務時,就需要單獨開啓一個工作線程來完成。IntentService就是基於這種應用場景而誕生的。

IntentService繼承自Service

    //IntentService源碼節選
    @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);
    }

    /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     *               This may be null if the service is being restarted after
     *               its process has gone away; see
     *               {@link android.app.Service#onStartCommand}
     *               for details.
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);

在它的onCreate方法中可以看到,它的內部其實就是通過封裝HandlerThread來實現工作線程的。當我們要使用它時,需要先繼承IntentService,然後實現它的抽象方法onHandleIntent方法。(由於它也是一個Service,別忘了在AndroidManifest中配置它)

public class TestIntentService extends IntentService {
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public TestIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //do something。。。
        //此時也是運行在工作線程(子線程)中哦
        //Intent用於傳遞我們需要的數據,每一次通過startService方法觸發,都會按順序執行,不會無序執行
    }
}

在我們使用這個IntentService的時候,就是這樣:

Intent intent = new Intent(MainActivity.this, TestIntentService.class);
startService(intent);//intent當中記得要存放你需要傳遞的參數哦

就像我上面的註釋中說的那樣,每次startService,都會按照順序去執行,並不會像我們new Thread那樣,順序完全隨機。並且它與Service不同的地方在於,它不需要手動執行stopService方法來終止服務。因爲在它的源碼中:

    //在IntentService的內部類中可以看出來
    //每一次執行完任務,都會自己調用stopSelf方法
    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);
        }
    }

 

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