1. 程式人生 > >Android非同步處理的幾種方式(附demo)

Android非同步處理的幾種方式(附demo)

在Android的程式開發中,許多耗時操作都要放到子執行緒中,避免阻塞主執行緒,導致ANR。但是在使用非同步執行緒的過程中都會遇到與主執行緒通訊的問題。
在這裡先總體介紹幾種常見非同步處理的技術,以及他們的對應關係:

Paste_Image.png

Thread

使用Thread有兩種方式,一直是使用Thread,一種是使用Runnable。

Thread方式:

 public class MyThread extends Thread{
        @Override
        public void run() {
            try {
                Thread.sleep(3000
); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.sendEmptyMessage(0); } }

然後在使用的時候呼叫

  new MyThread().start();

Runnable

 public class MyRunnable implements Runnable{
        @Override
        public void run
() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.sendEmptyMessage(0); } }

使用的時候呼叫:

 new Thread(new MyRunnable()).start();

總結

執行緒是執行任務的最基本的單元,在Android開發中,執行緒又分為主執行緒和子執行緒,主執行緒中不能進行耗時的操作,比如網路請求,子執行緒中不能執行更新UI的相關操作,否則都會丟擲異常。
根據上述例子可以看出,使用Thread執行任務,在結束後,需要通過Handler來發送訊息,通知UI主執行緒,進行對應的介面更新。

HandlerThread

上面提到過Thread執行完任務需要Handler來通知主執行緒,HandlerThread集合了Thread和Handler,集成了Looper和MessageQueue。當啟動HandlerThread時,會同時生成Looper和MessageQueue,然後等待訊息處理。
這裡簡單寫一下:

 private class myBackRunnable implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mainHandler.post(new myUIRunnable());
        }
    }
    private class myUIRunnable implements Runnable{

        @Override
        public void run() {
            btnStart.setText("執行緒結束");
        }
    }

啟動子執行緒:

 backHandler = new Handler(handlerThread.getLooper());
  mainHandler = new Handler(getMainLooper());
  backHandler.post(new myBackRunnable());

AsyncTask

AsyncTask提供了方便的介面實現工作執行緒和主執行緒的通訊,我這裡先貼一下程式碼,後面再根據程式碼進行解釋:

public class MyAsyncTask extends AsyncTask<Integer, String, String> {
        private Button btn;
        public MyAsyncTask(Button btn) {
            super();
            this.btn = btn;
        }

        @Override
        protected String doInBackground(Integer... integers) {
            Log.e("xxxxxx","xxxxxxexecute傳入引數="+integers[0]);
            try {
                Thread.sleep(1000);
                publishProgress("過了一秒");
                Thread.sleep(1000);
                publishProgress("過了兩秒");
                Thread.sleep(1000);
                publishProgress("過了三秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "doInBackground的返回";
        }
        /**
         * 這裡的String引數對應AsyncTask中的第三個引數(也就是接收doInBackground的返回值)
         * 在doInBackground方法執行結束之後在執行,並且執行在UI執行緒當中 可以對UI空間進行設定
         */
        @Override
        protected void onPostExecute(String result) {
            btn.setText("執行緒結束" + result);
        }
        //該方法執行在UI執行緒當中,並且執行在UI執行緒當中 可以對UI空間進行設定
        @Override
        protected void onPreExecute() {
            btn.setText("開始執行非同步執行緒");
        }
        /**
         * 這裡的Intege引數對應AsyncTask中的第二個引數
         * 在doInBackground方法當中,,每次呼叫publishProgress方法都會觸發onProgressUpdate執行
         * onProgressUpdate是在UI執行緒中執行,所有可以對UI空間進行操作
         */
        @Override
        protected void onProgressUpdate(String... values) {

            String vlaue = values[0]+"";
            Log.e("xxxxxx","xxxxxx vlaue="+vlaue);
            btn.setText(vlaue+"");


        }
    }

AsyncTask就是一個封裝過的後臺任務類,顧名思義就是非同步任務。
AsyncTask定義了三種泛型型別 Params,Progress和Result。

Params 啟動任務執行的輸入引數,比如HTTP請求的URL。
Progress 後臺任務執行的百分比。
Result 後臺執行任務最終返回的結果,比如String。
對應到上面的demo就是Integer, String, String。
doInBackground(Params…) 後臺執行,比較耗時的操作都可以放在這裡。注意這裡不能直接操作UI。此方法在後臺執行緒執行,完成任務的主要工作,通常需要較長的時間。在執行過程中可以呼叫publicProgress(Progress…)來更新任務的進度。
onProgressUpdate(String… Progress)這裡對應的是doInBackground中呼叫的publicProgress,在這裡進行處理,這裡是UI主執行緒可以進行介面的更新
onPreExecute()這裡相當於執行緒的開始,可以進行UI的處理
onPostExecute(Result) 相當於Handler 處理UI的方式,在這裡面可以使用在doInBackground 得到的結果處理操作UI。 此方法在主執行緒執行,任務執行的結果作為此方法的引數返回。
我們再來看一下如何開啟

  myAsyncTask.execute(1000);

execute的引數對應的就是上面提到過的Params。

IntentService

IntentService具有Service一樣的生命週期,也提供了後臺執行緒中的非同步處理機制。還是先看一下程式碼吧。

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("");
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            Thread.sleep(3*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Intent intentresult = new Intent(IntentServiceActivity.RESULT);

        sendBroadcast(intentresult);
    }
    public void onDestroy()
    {
        super.onDestroy();
    }
}

這裡我們是通過廣播,將結果返回到源Activity進行介面更新的,這樣的處理方式感覺很重,如非設計流程需要使用,不建議經常使用。
再看一下如何啟動:
Intent intent=new Intent(IntentServiceActivity.this,MyIntentService.class);
startService(intent);

其他

AsyncQueryHandler是用於在ContentProvider上面執行非同步操作的工具類,筆者自己也不常用,這裡不再贅述。
Exector框架的基礎是一個名為Exector的介面定義,Exector的主要任務是分離任務的建立和他的執行。這個我將在下一篇文章執行緒池相關的介紹中詳細描述。
上面介紹過的所有非同步機制,我已經整理出一個demo工程,方便開發者測試:
https://github.com/mymdeep/TestAsynchronous