Android必學之AsyncTask

來源:互聯網
上載者:User

標籤:

AsyncTask,即非同步任務,是Android給我們提供的一個處理非同步任務的類.通過此類,可以實現UI線程和後台線程進行通訊,後台線程執行非同步任務,並把結果返回給UI線程.

.為什麼需要使用非同步任務?

我們知道,Android中只有UI線程,也就是主線程才能進行對UI的更新操作,而其他線程是不能直接操作UI的.這樣的好處是保證了UI的穩定性和準確性,避免多個線程同時對UI進行操作而造成UI的混亂.但Android是一個多線程的作業系統,我們總不能把所有的任務都放在主線程中進行實現,比如網路操作,檔案讀取等耗時操作,如果全部放到主線程去執行,就可能會造成後面任務的阻塞.Android會去檢測這種阻塞,當阻塞時間太長的時候,就會拋出Application Not Responsed(ANR)錯誤.所以我們需要將這些耗時操作放在非主線程中去執行.這樣既避免了Android的單執行緒模式,又避免了ANR.

.AsyncTask為何而生?

提到非同步任務,我們能想到用線程,線程池去實現.確實,Android給我們提供了主線程與其他線程通訊的機制.但同時,Android也給我們提供了一個封裝好的組件--AsyncTask.利用AsyncTask,我們可以很方便的實現非同步任務處理.AsyncTask可以在子線程中更新UI,也封裝簡化了非同步作業.使用線程,線程池處理非同步任務涉及到了線程的同步,管理等問題.而且當線程結束的時候還需要使用Handler去通知主線程來更新UI.而AsyncTask封裝了這一切,使得我們可以很方便的在子線程中更新UI.

.構建AsyncTask子類的泛型參數

AsyncTask<Params,Progress,Result>是一個抽象類別,通常用於被繼承.繼承AsyncTask需要指定如下三個泛型參數:

Params:啟動任務時輸入的參數類型.

Progress:背景工作執行中返回進度值的類型.

Result:背景工作執行完成後返回結果的類型.

.構建AsyncTask子類的回調方法

AsyncTask主要有如下幾個方法:

doInBackground:必須重寫,非同步執行後台線程要完成的任務,耗時操作將在此方法中完成.

onPreExecute:執行後台耗時操作前被調用,通常用於進行初始化操作.

onPostExecute:當doInBackground方法完成後,系統將自動調用此方法,並將doInBackground方法返回的值傳入此方法.通過此方法進行UI的更新.

onProgressUpdate:當在doInBackground方法中調用publishProgress方法更新任務執行進度後,將調用此方法.通過此方法我們可以知曉任務的完成進度.

下面通過代碼示範一個典型的非同步處理的執行個體--載入網狀圖片.網路操作作為一個不穩定的耗時操作,從4.0開始就被嚴禁放入主線程中.所以在顯示一張網狀圖片時,我們需要在非同步處理中下載圖片,並在UI線程中設定圖片.

MainActivity.java

package com.example.caobotao.learnasynctask;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity {    private Button btn_image;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btn_image = (Button) findViewById(R.id.btn_image);        btn_image.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                startActivity(new Intent(MainActivity.this,ImageActivity.class));            }        });    }}

 

ImageActivity.java

package com.example.caobotao.learnasynctask;import android.app.Activity;import android.graphics.*;import android.os.*;import android.view.View;import android.widget.*;import java.io.*;import java.net.*;/** * Created by caobotao on 15/12/2. */public class ImageActivity extends Activity {    private ImageView imageView ;    private ProgressBar progressBar ;    private static String URL = "http://pic3.zhongsou.com/image/38063b6d7defc892894.jpg";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.image);        imageView = (ImageView) findViewById(R.id.image);        progressBar = (ProgressBar) findViewById(R.id.progressBar);        //通過調用execute方法開始處理非同步任務.相當於線程中的start方法.        new MyAsyncTask().execute(URL);    }    class MyAsyncTask extends AsyncTask<String,Void,Bitmap> {        //onPreExecute用於非同步處理前的操作        @Override        protected void onPreExecute() {            super.onPreExecute();            //此處將progressBar設定為可見.            progressBar.setVisibility(View.VISIBLE);        }        //在doInBackground方法中進行非同步任務的處理.        @Override        protected Bitmap doInBackground(String... params) {            //擷取傳進來的參數            String url = params[0];            Bitmap bitmap = null;            URLConnection connection ;            InputStream is ;            try {                connection = new URL(url).openConnection();                is = connection.getInputStream();                //為了更清楚的看到載入圖片的等待操作,將線程休眠3秒鐘.                Thread.sleep(3000);                BufferedInputStream bis = new BufferedInputStream(is);                //通過decodeStream方法解析輸入資料流                bitmap = BitmapFactory.decodeStream(bis);                is.close();                bis.close();            } catch (IOException e) {                e.printStackTrace();            } catch (InterruptedException e) {                e.printStackTrace();            }            return bitmap;        }        //onPostExecute用於UI的更新.此方法的參數為doInBackground方法返回的值.        @Override        protected void onPostExecute(Bitmap bitmap) {            super.onPostExecute(bitmap);            //隱藏progressBar            progressBar.setVisibility(View.GONE);            //更新imageView            imageView.setImageBitmap(bitmap);        }    }}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:orientation="vertical"              android:layout_width="match_parent"              android:gravity="center"              android:layout_height="match_parent">    <Button        android:id="@+id/btn_image"        android:text="載入圖片"        android:layout_width="match_parent"        android:layout_height="wrap_content"/>   </LinearLayout>

progress.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:orientation="vertical"              android:layout_width="match_parent"              android:gravity="center"              android:layout_height="match_parent">    <ProgressBar        style="?android:attr/progressBarStyleHorizontal"        android:id="@+id/progress"        android:layout_width="match_parent"        android:layout_height="wrap_content"/></LinearLayout>

由於涉及到網路操作,需要在AndroidManifest.xml中添加網路操作許可權:<uses-permission android:name="android.permission.INTERNET"/>

運行結果:

        

 

下面再示範一個類比更新進度條的執行個體.

MainActivity.java

package com.example.caobotao.learnasynctask;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity {    private Button btn_progress;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btn_progress = (Button) findViewById(R.id.btn_progress);        btn_progress.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                startActivity(new Intent(MainActivity.this,ProgressActivity.class));            }        });    }}

 

 

ProgressActivity.java

package com.example.caobotao.learnasynctask;import android.app.Activity;import android.os.AsyncTask;import android.os.AsyncTask.Status;import android.os.Bundle;import android.widget.ProgressBar;import java.util.Scanner;/** * Created by caobotao on 15/12/2. */public class ProgressActivity extends Activity{    private ProgressBar progressBar;    private MyAsyncTask myAsyncTask;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.progress);        progressBar = (ProgressBar) findViewById(R.id.progress);        myAsyncTask = new MyAsyncTask();        myAsyncTask.execute();    }
} class MyAsyncTask extends AsyncTask<Void,Integer,Void>{ @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); //通過publishProgress方法傳過來的值進行進度條的更新. progressBar.setProgress(values[0]); } @Override protected Void doInBackground(Void... params) { //使用for迴圈來類比進度條的進度. for (int i = 0;i < 100; i ++){ //調用publishProgress方法將自動觸發onProgressUpdate方法來進行進度條的更新. publishProgress(i); try { //通過線程休眠類比耗時操作 Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } }}

 

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:orientation="vertical"              android:layout_width="match_parent"              android:gravity="center"              android:layout_height="match_parent">    <Button        android:id="@+id/btn_progress"        android:text="載入進度條"        android:layout_width="match_parent"        android:layout_height="wrap_content"/></LinearLayout>

progress.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:orientation="vertical"              android:layout_width="match_parent"              android:gravity="center"              android:layout_height="match_parent">    <ProgressBar        style="?android:attr/progressBarStyleHorizontal"        android:id="@+id/progress"        android:layout_width="match_parent"        android:layout_height="wrap_content"/></LinearLayout>

 

同樣需要在AndroidManifest.xml中添加網路操作許可權:<uses-permission android:name="android.permission.INTERNET"/>

運行結果:

           

點擊‘載入進度條‘按鈕後程式看起來運行正常.但是,正如上面圖示,如果接著點擊BACK鍵,緊接著再次點擊‘載入進度條‘按鈕,會發現進度條的進度一直是零,過了一會才開始更新.這是為什麼呢?

根據上述的講解,我們知道,AsyncTask是基於線程池進行實現的,當一個線程沒有結束時,後面的線程是不能執行的.所以必須等到第一個task的for迴圈結束後,才能執行第二個task.我們知道,當點擊BACK鍵時會調用Activity的onPause()方法.為瞭解決這個問題,我們需要在Activity的onPause()方法中將正在執行的task標記為cancel狀態,在doInBackground方法中進行非同步處理時判斷是否是cancel狀態來決定是否取消之前的task.

更改ProgressActivity.java如下:

package com.example.caobotao.learnasynctask;import android.app.Activity;import android.os.AsyncTask;import android.os.AsyncTask.Status;import android.os.Bundle;import android.widget.ProgressBar;import java.util.Scanner;/** * Created by caobotao on 15/12/2. */public class ProgressActivity extends Activity{    private ProgressBar progressBar;    private MyAsyncTask myAsyncTask;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.progress);        progressBar = (ProgressBar) findViewById(R.id.progress);        myAsyncTask = new MyAsyncTask();        //啟動非同步任務的處理        myAsyncTask.execute();    }    //AsyncTask是基於線程池進行實現的,當一個線程沒有結束時,後面的線程是不能執行的.    @Override    protected void onPause() {        super.onPause();        if (myAsyncTask != null && myAsyncTask.getStatus() == Status.RUNNING) {            //cancel方法只是將對應的AsyncTask標記為cancelt狀態,並不是真正的取消線程的執行.            myAsyncTask.cancel(true);        }    }    class MyAsyncTask extends AsyncTask<Void,Integer,Void>{        @Override        protected void onProgressUpdate(Integer... values) {            super.onProgressUpdate(values);            //通過publishProgress方法傳過來的值進行進度條的更新.            progressBar.setProgress(values[0]);        }        @Override        protected Void doInBackground(Void... params) {            //使用for迴圈來類比進度條的進度.            for (int i = 0;i < 100; i ++){                //如果task是cancel狀態,則終止for迴圈,以進行下個task的執行.                if (isCancelled()){                    break;                }                //調用publishProgress方法將自動觸發onProgressUpdate方法來進行進度條的更新.                publishProgress(i);                try {                    //通過線程休眠類比耗時操作                    Thread.sleep(300);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            return null;        }    }}

 

.使用AsyncTask的注意事項

① 必須在UI線程中建立AsyncTask的執行個體.

② 只能在UI線程中調用AsyncTask的execute方法.

③ AsyncTask被重寫的四個方法是系統自動調用的,不應手動調用.

④ 每個AsyncTask只能被執行(execute方法)一次,多次執行將會引發異常.

⑤ AsyncTask的四個方法,只有doInBackground方法是運行在其他線程中,其他三個方法都運行在UI線程中,也就說其他三個方法都可以進行UI的更新操作.

 

 

Android必學之AsyncTask

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.