Android:異步處理之AsyncTask的應用(二)


前言

  在上一篇文章中《Android:異步處理之Handler+Thread的應用(一)》,我們知道Android的UI主線程主要負責處理用戶的按鍵事件、用戶的觸屏事件以及屏幕繪圖事件等;既然UI老人家都這么忙了,我們這些開發者肯定不能不識趣的去添亂阻塞UI線程什么的,否則UI界面萬一停止響應了呢——這不是招罵的節奏么?!所以我們知道用Handler+Thread的方法,在子線程中處理耗時的任務,任務完成后通過Handler通知UI主線程更新UI界面,皆大歡喜有木有。

  可是這樣,還是有某些人覺得用Handler+Thread的代碼會比較繁瑣,當然這個某些人里面包括我們偉大的谷歌。所以AsyncTask(異步任務)在Android 1.5中橫空出世;相對於Handler來說,由於比較好的封裝,AsyncTask顯得更加輕量級一點,適用於簡單的異步處理;當然使用起來也比較簡潔,果然是谷歌的親兒子!

概述

  AsyncTask是一個抽象類,通常是被繼承的命。AsyncTask的內部會維持一個靜態的線程池,每個后台任務自然也會被提交到線程池中運行,同時也使用Handler+Thread的機制來調用AsyncTask的各個回調方法;回調方法是在主線程運行的,所以該干什么我們都懂(~ o ~)~zZ(趕緊跟UI界面套近乎呀)。

我們知道AsyncTask<Params, Progress, Result>是抽象類,我們可以在這里面看出它支持三種泛型:

1、Params:我們的AsyncTask要開始干活時,我們給他的輸入的參數的類型,也就是傳遞給后台的參數

2、Progress:AsyncTask向我們報告它干活進度的參數類型,舉個例子就是下載進度的百分比

3、Result:后台執行任務完成,返回的結果的參數類型

如果某個泛型我們不需要指定,我們可以大大方方的指定Void,沒事AsyncTask不會傷心滴。

當然谷歌也幫我們將AsyncTask的后台任務運行的五種狀態,分別是:1、准備運行,2、正在后台運行,3、進度更新,4、完成后台任務,5、取消任務。每種狀態在AsyncTask中各有相應的回調方法。

1、准備運行:onPreExecute(),在任務開啟時該回調方法立即在UI線程中被調用,同時也是在執行后台耗時操作前被調用;通常該方法用於完成一些初始化工作,比如在界面上顯示進度條等。

2、正在后台運行:doInBackground(Params...),該回調函數由后台線程在onPreExecute()方法執行結束后立即調用,重寫該方法就是后台線程將要完成的耗時任務;由於是由后台線程調用,所以我們不能直接在這里更新UI界面,應該使用publishProgress(Progress...)觸發回調方法onProgressUpdate(Progress...)進行進度更新;任務計算的結果必須由該函數返回,並被傳遞到onPostExecute()中。

3、進度更新:onProgressUpdate(Progress...),在doInBackground()中調用publishProgress()方法更新任務的執行進度,將會在主線程中觸發該方法,一般用於動態地顯示一個進度條。

4、完成后台任務:onPostExecute(Result),當doInBackground()完成后,系統會自動調用onPostExecute()方法,並將doInBackground()的返回值傳遞給該方法。

5、取消任務:onCancelled (),在調用AsyncTask的cancel()方法時調用。

案例

參考代碼:

public class MainActivity extends ActionBarActivity implements OnClickListener{

    private Button startdownload;
    private ProgressBar probar;
    private TextView tv;
    
    private DownTask task;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        startdownload = (Button) findViewById(R.id.startdownload);
        probar = (ProgressBar) findViewById(R.id.probar);
        tv = (TextView) findViewById(R.id.tv);
        
        startdownload.setOnClickListener(this);
        
    }

    @Override
    public void onClick(View v) {
        task = new DownTask();//同一個AsyncTask的execute只能調用一次
        task.execute("輸入參數,可為空");//調用execute后將會回調onPreExecute方法
    }
    
    class DownTask extends AsyncTask<String, Integer, String>{

        @Override//該方法非在主線程運行,可進行耗時操作,不可更新UI界面,其他方法為主線程運行
        protected String doInBackground(String... params) {//params為execute輸入的參數
            for(int i = 1; i <= 100; i++){
                try {//模擬下載操作
                    Thread.sleep(333);
                    publishProgress(i);//傳遞參數i並觸發onProgressUpdate回調方法
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            String result = "任務已完成";
            return result;//將調用onPostExecute,並將result傳給該回調方法
        }

        @Override
        protected void onPreExecute() {//該回調方法執行完畢后,將會調用doInBackground
            probar.setMax(100);
            probar.setProgress(0);
            tv.setText("開始下載");
        }

        @Override
        protected void onPostExecute(String result) {//doInBackground結束后回調該方法,結束。
            Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
            tv.setText("下載完成");
        }

        @Override
        protected void onProgressUpdate(Integer... values) {//通知UI界面更新
            probar.setProgress(values[0]);
        }
        
    }

}

布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <Button
        android:id="@+id/startdownload"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="開始下載"/>
    <ProgressBar 
        android:id="@+id/probar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="@android:style/Widget.ProgressBar.Horizontal"
        />
    <TextView 
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000000"
        android:textSize="20sp"
        />

</LinearLayout>

代碼講解:

1、點擊Button后先實例化一個AsyncTask的繼承子類,此時將會創建一個task。接下來變執行execute(params)方法啟動異步任務。(同一個AsyncTask的實例只能執行execute一次,多次執行會拋出錯誤)。

2、在execute()被執行后,將會觸發onPreExecute()回調方法,設置進度條的初始屬性。在onPreExecute()執行完畢后,將會在后台線程開始執行doInBackground(params),該方法接收execute傳入的參數,進行耗時操作,這里是模擬網絡文件下載任務。

3、doInBackground()在后台線程運行中,如果需要與UI主線程交互更新進度,可以調用publishProgress(values)方法,將會觸發位於UI主線程運行的onProgressUpdate(values)的回調方法,代碼中在這里更新進度條的進度。

4、 當后台任務執行完成后,調用onPostExecute(Result),傳入的參數是doInBackground()中返回的對象。

注意事項

1、不要在同一個AsyncTask實例中多次執行execute(),正確的方法是new一個AsyncTask執行一次execute()。

2、耗時任務一定要在doInBackground()中處理,不要在其他回調方法中處理耗時任務以免引起UI主線程的阻塞。

3、不要再doInBackground()中更新UI界面,應該通過publishProgress()調用回調方法更新UI。

4、onCancelled()只能觸發AsyncTask的cancel()方法,並無法取消正在線程池運行的線程任務,但可以通過標志位來停止線程任務。

5、在不同的android版本中,AsyncTask多任務運行,有些是可以並行有些則是順序執行,不過在高版本Android中,可以通過指定參數設置線程池執行規則。

6、AsyncTask適合處理短時間的操作,長時間的操作,比如下載一個很大的視頻,這就需要你使用自己的線程來下載,不管是斷點下載還是其它的。

 

 

作者:enjoy風鈴
出處:http://www.cnblogs.com/net168/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則下次不給你轉載了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM