淺析android中AsyncTask

作用

android是單線程模型。如果在UI主線程中執行耗時操作。可能導致ANR(應用無響應)。系統就會彈出一個ANR對話框。用戶選擇等待或者離開應用
注意:ANR出現場景:


  1. 主線程被IO操作(4.0以後主線程中不允許進行網絡IO操作)阻塞。
  2. 主線程中進行耗時的操作。
  3. 主線程中進行錯的操作,如Thread.wait Thread.sleep
    Android系統會監視應用響應情況:如果應用在5秒內沒有響應用戶輸入事件(如按鍵或者觸摸)或者Broadcase Receiver在10秒內未完成相關的處理都會彈出ANR。

如何避免:
  • 基本的思路就是將IO操作在工作線程來處理,減少其他耗時操作和錯誤操作
  • 使用AsyncTask處理耗時IO操作。
  • 使用Thread或者HandlerThread時,調用Process.setThreadPriority
    (Process.THREAD_PRIORITY_BACKGROUND)設置優先級,否則仍然會降低程序響應,因爲默認Thread的優先級和主線程相同。
  • 使用Handler處理工作線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程。
  • Activity的onCreate和onResume回調中儘量避免耗時的代碼
  • BroadcastReceiver中onReceive代碼也要儘量減少耗時,建議使用IntentService處理。

AsyncTask

android中可以使用Handler和AsyncTask來實現異步機制。
Handler模式需要爲每一個任務創建一個新的線程,任務完成後通過Handler實例向UI線程發送消息,完成界面的更新
AsyncTask是抽象類。開發者需要繼承後使用。
繼承AsyncTask 時需要給AsyncTask指定三個類型。

  • params 任務啓動時候傳遞的參數。即調用execute()時,傳遞的參數。
  • progress 該參數是在doInbackground()函數中調用publishProgress()將進程執行的值傳遞給doProgressUpdate()函數做參數。
  • result 是doInbackground()返回的值得類型。該函數返回的值會傳遞給doPostExecute()函數。進行任務完成後的處理。

繼承AsyncTask必須要重寫doInbackground()。在該函數中具體執行異步任務。
- doPreExecute()(在函數doInbackground()執行之前調用做一些準備工作)
- doPostExecute()(在函數doInbackground()執行之後進行相關的處理。並且doInbackground()的返回值會傳遞給該函數做參數)
- doProgressUpdate()()(在doInbackground()函數中調用publishProgress()後該函數被調用,將進程執行的值通過publishProgress()傳遞給doProgressUpdate()函數做參數)

public class MainActivity extends Activity {
   private ImageView imageView;
    private ProgressBar progressBar;
    public static String        url="https://img3.doubanio.com/lpic/s28385426.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView=(ImageView)findViewById(R.id.iamgeview);
        progressBar=(ProgressBar)findViewById(R.id.progress);
        new MyAsyncTask().execute(url);
    }
    //注意三個參數,分別是execute(url)函數傳入參數的類型,progress的返回值,protected Bitmap doInBackground的返回值
    class MyAsyncTask extends AsyncTask<String,Void,Bitmap>{
        @Override
        protected Bitmap doInBackground(String... params) {
            //取出參數
            String url=params[0];
            Bitmap bitmap=null;
            InputStream is=null;
            try {
                URLConnection urlc=new URL(url).openConnection();
                is=urlc.getInputStream();
                BufferedInputStream bis=new BufferedInputStream(is);
                bitmap= BitmapFactory.decodeStream(bis)
                is.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return bitmap;
        }
        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //任務執行前進度條設爲visible。
            progressBar.setVisibility(View.VISIBLE);

        }
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //任務執行完返回下載的圖片,進度條隱藏,在子線程中更新界面
            progressBar.setVisibility(View.GONE);
            imageView.setImageBitmap(bitmap);
        }
    }

}

AsyncTask取消

public class MainActivity extends Activity {
    private ProgressBar progressBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar=(ProgressBar)findViewById(R.id.progress);
        new MyAsyncTask().execute();
    }
    class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
        @Override
        protected Void doInBackground(Void... params) {
            for(int i=0;i<100;i++)
            {
                publishProgress(i);
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            //更新progressbar
            progressBar.setMax(100);
            progressBar.setProgress(values[0]);
        }
    }

}

該代碼段運行會出現這種問題:第一次打開應用正常執行。progressbar未更新完然後按返回鍵。再次打開應用時,應用會等上一個線程執行完然後纔會執行下一個任務。所以看到,等一會progressbar纔開始更新(等上一次執行時打開的線程執行完成)
所以避免這種情況可以讓任務執行的生命週期和activity相關聯

public class MainActivity extends Activity {
    private ProgressBar progressBar;
    private MyAsyncTask myAsyncTask;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar=(ProgressBar)findViewById(R.id.progress);
        myAsyncTask=new MyAsyncTask();
        myAsyncTask.execute();
    }

    @Override
    protected void onStop() {
        super.onStop();
        if(myAsyncTask!=null&&myAsyncTask.getStatus()==AsyncTask.Status.RUNNING)
        //cancel()方法只是設置AsyncTask爲cancel狀態,並沒有停止線程的執行。所在doInbackground()中判斷如果AsyncTask爲cancel狀態就停止循環
        {
            myAsyncTask.cancel(true);
        }
    }

    class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
        @Override
        protected Void doInBackground(Void... params) {
            for(int i=0;i<100;i++)
            {
                if(isCancelled())
                {
                    break;
                }
                publishProgress(i);

                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            progressBar.setMax(100);
            progressBar.setProgress(values[0]);
        }
    }

}

總結

  • AsyncTask只能在UI線程中創建和執行execute()
  • 重寫的AsycTask只能由系統自動調用,不可手動調用
  • 一個AsynTask只能被執行一次,多次執行可能會出現問題。如上面的例子。
  • AsyncTask的本質是一個靜態的線程池,AsyncTask派生出的子類可以實現不同的異步任務,這些任務都是提交到靜態的線程池中執行。
  • 當任務狀態改變之後,工作線程會向UI線程發送消息,AsyncTask內部的InternalHandler響應這些消息,並調用相關的回調函數
發佈了43 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章