Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> AsyncTask 流程解析

AsyncTask 流程解析

編輯:關於Android編程

為什麼要使用異步任務?

Android 單線程模型,多線程的操作系統

耗時操作放在非主線程中運行

AsyncTask 為何而生?

子線程中更新UI

封裝簡化異步操作

構建AsyncTask子類的參數

AsyncTask

構建AsyncTask子類的的回調方法

doInBackground(): 必須重寫,異步執行後台線程將要完成的任務

onPreExecute(): 執行後台線程前被調用,通常用來做一些初始化操作

onPostExecute(): 當doInBackground() 方法完成後系統會自動調 用,並將doInBackground() 方法的返回值作為參數春遞給onPostExecute()方法

onProgressUpdate(): 在doBackground() 方法中調用publishProgress()方法更新任務的執行進度後,就會調用該方法

接下來我們寫個程序測試一下這些方法的執行順序

首先創建一個AsyncTask的子類 MyAsyncTask

public class MyAsyncTask extends AsyncTask{

    String LOGCAT = "LOGCAT";

    @Override
    protected Void doInBackground(Void... params) {
        Log.d(LOGCAT, "doInBackground------------");
        System.out.println("doInBackground------------");
        return null;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Log.d(LOGCAT, "onPreExecute");

    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        Log.d(LOGCAT, "onPostExecute");

    }

    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
        Log.d(LOGCAT, "onProgressUpdate");

    }

}

在 MainActivity 中進行測試

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyAsyncTask asyncTask = new MyAsyncTask();
        asyncTask.execute();

    }


}

在模擬器上部署運行之後,查看Logcat 可以看到下面的日志

這裡寫圖片描述
從日志中可以看到,幾個方法的執行順序依次為 : onPreExecute –>doInBackground –>onPostExecute

然後我們在doInBackground 方法中添加這句代碼 publishProgress();

@Override
    protected Void doInBackground(Void... params) {
        Log.d(LOGCAT, "doInBackground------------");

        //調用該方法後,會執行 onPostExecute() 方法
        publishProgress();

        return null;
    }

再次運行,觀察logcat 輸出,可看到在 doInBackground() 方法中執行了 publishProgress()方法後會調用 onProgressUpdate() 方法,顧名思義就是更新進度條的方法

這裡寫圖片描述

下面我們來看一個典型的異步操作的例子,網絡操作,從 Android4.0 之後,網絡操作就嚴禁被放入到主線程中執行.下面是一個采用在異步線程處理下載圖像
在UI線程設置圖像的例子

布局界面代碼比較簡單,如下



    

    

MainActivity代碼如下

public class MainActivity extends Activity {

    private ImageView image;// 要展示的圖片
    private ProgressBar pb;// 進度條
    // 要加載的圖片的url
    String imageUrl = "https://www.baidu.com/img/2016_10_09logo_61d59f1e74db0be41ffe1d31fb8edef3.png";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        image = (ImageView) findViewById(R.id.iv);
        pb = (ProgressBar) findViewById(R.id.pb);

    }

    // 下載按鈕的點擊事件
    public void loadImage(View view) {

        AsyncTaskTest asyncTaskTest = new AsyncTaskTest();
        // execute()方法接受一個可變長數組的參數,可在 doInBackground()方法中獲取
        asyncTaskTest.execute(imageUrl);

    }

    class AsyncTaskTest extends AsyncTask {

        Bitmap bitmap;

        // 下載開始前的一些初始化操作
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
            pb.setVisibility(View.VISIBLE);// 在下載之前將 Progress 顯示出來
        }

        // 在此方法中進行網絡耗時操作,下載完成後會執行 onPostExecute 方法,並把返回值傳遞給它
        @Override
        protected Bitmap doInBackground(String... params) {

            // 獲取傳遞進來的參數
            String url = params[0];
            Bitmap btm = null;
            URLConnection connection;
            InputStream is;

            try {
                connection = new URL(url).openConnection();
                is = connection.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(is);
                // 通過 BitmapFactory.decodeStream 方法吧輸入流轉換為 bitmap 對象
                bitmap = BitmapFactory.decodeStream(bis);

                is.close();
                bis.close();

            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            // 為了看清楚進度條,人為加一個延時操作,便於觀察
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return bitmap;
        }

        // doInBackground()方法執行完畢後會自動調用此方法, 此方法的參數是 doInBackground() 方法的返回值.
        @Override
        protected void onPostExecute(Bitmap result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            pb.setVisibility(View.GONE);// 隱藏進度條
            image.setImageBitmap(result);// 顯示下載的網絡圖片

        }

    }

}

上面代碼注釋很詳細,不再多做解釋,只要搞懂了 AsyncTask 的幾個方法的作用於執行周期,上面的代碼很容易理解.

效果圖如下

這裡寫圖片描述

下面我們再通過一個模擬進度條的小例子,進一步認識AsyncTask 異步任務的用法

布局界面很簡單,如下



    

    

Activity 代碼也很簡單

public class progressBarTest extends Activity {

    private ProgressBar pb;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pro);
        pb = (ProgressBar) findViewById(R.id.pb);
        tv = (TextView) findViewById(R.id.tv_show);

        MyAsyncTask myAsyncTask = new MyAsyncTask();
        myAsyncTask.execute();

    }

    class MyAsyncTask extends AsyncTask {

        @Override
        protected Void doInBackground(Void... params) {

            //模擬進度的更新
            for (int i = 0; i <= 100; i++) {
                // 更新進度條,重寫 onProgressUpdate()方法,參數為 publishProgress(i)的參數
                publishProgress(i);// 此方法傳入的參數就是 AsyncTask的第二個指定的參數類型

                // 睡眠200毫秒
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }

            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            // 更新顯示數據
            tv.setText(values[0] + "%");

            // 更新進度條
            pb.setProgress(values[0]);// 水平進度條的進度為百分制

        }

    }

}

效果圖如下

這裡寫圖片描述

是不是很簡單,但是不要高興太早,對於這個程序,當我們點擊下載,然後點擊返回,然後再點擊下載,進度條居然等了好久才開始更新,如下圖

這裡寫圖片描述

這是為啥呢???????????????????????????????????????

其實AsyncTask 底層是通過線程池進行作用的,當一個線程沒有作用完畢的時候,其它線程即必須進入線程池進行等待,等到前面的線程完事後,才會輪到自己執行,所以,當我們返回再次進入的時候,因為前一個線程正在執行更新進度條操作,所以當前線程必須等待前一個AsyncTask執行完畢後自己才可以執行.

那麼如何解決這個問題呢?

其實很簡單,AsyncTask 框架已經為我們考慮到了這個問題,我們可以通過 cancel() 方法來取消掉一個AsyncTask開啟的一個異步任務.此方法接受一個布爾值的參數,

我們要做的很簡單,重寫Activity的 onPause() 方法,把AsyncTask的聲明周期和Activity綁定到一起. 並且在 doInBackground() 方法中做異步判斷.代碼如下

@Override
        protected Void doInBackground(Void... params) {

            // 模擬進度的更新
            for (int i = 0; i <= 100; i++) {
                // 當收到取消請求時,不要在更新進度條,直接break結束for循環
                if (isCancelled()) {
                    break;

                }

                // 更新進度條,重寫 onProgressUpdate()方法,參數為 publishProgress(i)的參數
                publishProgress(i);// 此方法傳入的參數就是 AsyncTask的第二個指定的參數類型

                // 睡眠200毫秒
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }

            return null;
        }

並且在 onProgressUpdate () 方法中也做同樣處理

@Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            // 當收到取消請求時,不要在更新進度條,直接return結束
            if (isCancelled()) {
                return;
            }

            // 更新顯示數據
            tv.setText(values[0] + "%");

            // 更新進度條
            pb.setProgress(values[0]);// 水平進度條的進度為百分制

        }

    }

好了一切都做完了,我們再次運行程序可以看到

這裡寫圖片描述
問題完美解決

總結

AsynsTask使用注意事項:

必須在UI線程中創建AsyncTask實例

必須在UI線程中調用AsyncTask的execute() 方法,而且execute() 方法只能執行一次,多次調用,會拋出異常

在AsyncTask 的幾個方法中,只有 doInBackground() 方法是運行在子線程,其它方法都是運行在主線程中,都可以更新UI.

重寫的四個方法都是系統自動調用的,我們不應該也不能收懂去調用.

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved