Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android異步任務AsyncTask的使用與原理分析

Android異步任務AsyncTask的使用與原理分析

編輯:關於Android編程

在了解了Android緩存機制後我准備自己動手寫一個LruCache和DiskLruCache二級緩存的輕量級的圖片請求框架,在思考如何搭建這個框架時,糾結於用何種方式去下載圖片,是直接new出一個線程呢,還是用看起來稍微高大上檔次一點的AsyncTask異步任務來處理?思來想去,還是虛榮心作怪,還是用AsyncTask吧,正好這個工具類我之前用的也比較少,對它的原理也不是很清楚,趁這個機會,好好學一下AsyncTask的使用,並分析一下其源碼實現。待分析完AsyncTask之後,接著完成圖片請求框架的編寫。  

1、AsyncTask的使用

分析AsyncTask原理之前,還是好好學習一下它的具體使用方法。

1.1 AsyncTask簡介

在Android中,我們更新UI的操作必須要在主線程(UI線程)中進行,而下載圖片、文件這種操作必須要在子線程中進行,Android為我們提供了Handler機制,實現了子線程與主線程之間的通信。通常做法就是先new出一個子線程Thread,在子線程中完成下載操作後,通過handler發送一條Message給主線程,主線程收到消息後,就可以進行UI的更新工作了,如下:
Handler mHadler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if(msg.what == 1){
            Bitmap bitmap = (Bitmap) msg.obj;
            //更新UI...
        }
    }
};

private void download(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            // 這裡進行下載操作...獲得了圖片的bitmap
            //下載完後才,向主線程發送Message
            Message msg = Message.obtain();
            msg.obj = bitmap;
            msg.what = 1;//區分哪一個線程發送的消息
            mHadler.sendMessage(msg);
        }
    }).start();
}
可以看到,每次要進行下載工作,我們就得先創建出Thread,然後在主線程中寫好handler,為了對這個過程進行封裝,Android提供了AsyncTask異步任務,AsyncTask對線程和handler進行了封裝,使得我們可以直接在AsyncTask中進行UI的更新操作,就好像是在子線程進行UI更新一樣。  

1.2 創建AsyncTask子類

AsyncTask是一個抽象類,我們必須寫一個子類繼承它,在子類中完成具體的業務下載操作。為了對各種情況更好的封裝,AsyncTask抽象類指定了三個泛型參數類型,如下:
public abstract class AsyncTask{ ... }
其中,三個泛型類型參數的含義如下:   Params:開始異步任務執行時傳入的參數類型,即doInBackground()方法中的參數類型; Progress:異步任務執行過程中,返回下載進度值的類型,即在doInBackground中調用publishProgress()時傳入的參數類型; Result:異步任務執行完成後,返回的結果類型,即doInBackground()方法的返回值類型;     有了這三個參數類型之後,也就控制了這個AsyncTask子類各個階段的返回類型,如果有不同業務,我們就需要再另寫一個AsyncTask的子類進行處理。  

1.3 AsyncTask的回調方法

前面我們說過,AsyncTask對線程和handler進行了封裝,那它的封裝性體現在哪裡呢?其實,AsyncTask的幾個回調方法正是這種封裝性的體現,使得我們感覺在子線程進行UI更新一樣。一個基本的AsyncTask有如下幾個回調方法:   (1)onPreExecute():在執行後台下載操作之前調用,運行在主線程中; (2)doInBackground():核心方法,執行後台下載操作的方法,必須實現的一個方法,運行在子線程中;   (3)onPostExecute():後台下載操作完成後調用,運行在主線程中;       因此,AsyncTask的基本生命周期過程為:onPreExecute() --> doInBackground() --> onPostExecute()。其中,onPreExecute()和onPostExecute()分別在下載操作前和下載操作後調用,同時它們是在主線程中進行調用,因此可以在這兩個方法中進行UI的更新操作,比如,在onPreExecute()方法中,將下載等待動畫顯示出來,在onPostExecute()方法中,將下載等待動畫進行隱藏。   如果我們想向用戶展示文件的下載進度情況,這時,我們可以在doInBackground()下載操作中,調用publishProgress(),將當前進度值傳入該方法,而publishProgress()內部會去調用AsyncTask的另一個回調方法: (4)onProgressUpdate():在下載操作doInBackground()中調用publishProgress()時的回調方法,用於更新下載進度,運行在主線程中;   因此,在需要更新進度值時,AsyncTask的基本生命周期過程為:onPreExecute() --> doInBackground() --> publishProgress() -->onProgressUpdate()--> onPostExecute()。 可以看到,AsyncTask的優秀之處在於幾個回調方法的設置上,只有donInBackground()是運行在子線程的,其他三個回調方法都是在主線程中運行,因此,只要在AsyncTask中,就可以實現文件的後台下載、UI的更新操作。   好了,明白了如何創建一個AsyncTask,以及AsyncTask的幾個回調方法的調用時機,我們就可以來實戰體驗一下。在例子中,我們去下載一張圖片,並通過進度條顯示下載的進度。 首先實現一個AsyncTask的具體實現類,進行圖片的下載,如下:    
public class MyAsyncTask extends AsyncTask{
    private ProgressBar mPreogressBar;//進度條
    private ImageView mImageView;//圖片顯示控件

    public MyAsyncTask(ProgressBar pb,ImageView iv){
        mPreogressBar = pb;
        mImageView = iv;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mPreogressBar.setVisibility(View.VISIBLE);
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        String urlParams = params[0];//拿到execute()傳過來的圖片url
        Bitmap bitmap = null;
        URLConnection conn = null;
        InputStream is = null;
        try {
            URL url = new URL(urlParams);
            conn = url.openConnection();
            is = conn.getInputStream();

            //這裡只是為了演示更新進度的功能,實際的進度值需要在從輸入流中讀取時逐步獲取
            for(int i = 0; i < 100; i++){
                publishProgress(i);
                Thread.sleep(50);//為了看清效果,睡眠一段時間
            }
            //將獲取到的輸入流轉成Bitmap
            BufferedInputStream bis = new BufferedInputStream(is);
            bitmap = BitmapFactory.decodeStream(bis);

            is.close();
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

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

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        mPreogressBar.setVisibility(View.GONE);
        mImageView.setImageBitmap(bitmap);
    }
}
上面doInBackground()中獲取進度值時,我們只是為了做一個進度值更新調用的演示,實際項目文件下載中,我們可能會對拿到的輸入流進行處理,比如讀取輸入流將文件保存到本地,在讀取輸入流的時候,我們就可以獲取到已經讀取的輸入流大小作為進度值了,如下:
//實際項目中如何獲取文件大小作為進度值及更新進度值
            int totalSize = conn.getContentLength();//獲取文件總大小
            int size = 0;//保存當前下載文件的大小,作為進度值
            int count = 0;
            byte[] buffer = new byte[1024];
            while((count = is.read(buffer)) != -1){
                size += count;//獲取已下載的文件大小
                //調用publishProgress更新進度,它內部會回調onProgressUpdate()方法
                publishProgress(size,totalSize);
                Thread.sleep(100);//為了看清效果,睡眠一段時間
            }
在MainActivity中使用:
public class MainActivity extends AppCompatActivity {
    private ImageView mImageView;
    private ProgressBar mProgressBar;
    private static String URL = "http://c.hiphotos.baidu.com/baike/s%3D220/sign=86442af5a6c27d1ea1263cc62bd4adaf/42a98226cffc1e17d8f914604890f603738de919.jpg";
    private MyAsyncTask asyncTask;

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

        mImageView = (ImageView) findViewById(R.id.id_image);
        mProgressBar = (ProgressBar) findViewById(R.id.pb);

        asyncTask = new MyAsyncTask(mProgressBar, mImageView);
        asyncTask.execute(URL);//將圖片url作為參數傳入到doInBackground()中
    }
}
布局文件如下:

    android:padding="16dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
            android:id="@+id/id_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
            android:id="@+id/pb"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="30dp" />
效果如下: \   由於需要聯網,注意在AndroidManifest.xml中加入網絡訪問權限。  

1.4 取消下載任務

我們先來看兩個現象。 我們在布局中加一個按鈕,點擊這個按鈕再加載一次圖片,布局如下:

    android:padding="16dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
            android:id="@+id/id_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
            android:id="@+id/pb"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="30dp" />
            android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="15dp"
        android:id="@+id/id_btn"
        android:text="加載圖片"
        android:textSize="16sp"
        android:layout_alignParentBottom="true"
        android:onClick="loadImage"/>
因此,在MainActivity中,我們就需要加入loadImage方法,如下:
public class MainActivity extends AppCompatActivity {
    private ImageView mImageView;
    private ProgressBar mProgressBar;
    private static String url = "http://c.hiphotos.baidu.com/baike/s%3D220/sign=86442af5a6c27d1ea1263cc62bd4adaf/42a98226cffc1e17d8f914604890f603738de919.jpg";
    private MyAsyncTask asyncTask;

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

        mImageView = (ImageView) findViewById(R.id.id_image);
        mProgressBar = (ProgressBar) findViewById(R.id.pb);

        asyncTask = new MyAsyncTask(mProgressBar, mImageView);
        asyncTask.execute(url);
}

    public void loadImage(View v){
        asyncTask.execute(url);
    }
}
現象一:在loadImage()方法中,我們直接再次通過asyncTask.execute()執行加載。看看此時效果如何: \ onCreate中初始加載完一次圖片後,我們點擊“加載圖片”按鈕,此時程序直接崩潰了!這是因為,每一個new出的AsyncTask只能執行一次execute(),如果同一個AsyncTask多次執行execute()執行將會報錯。 現象二:我們來修改loadImage()方法,在該方法中,我們在打開自身MainActivity,使得多次初始化的時候進行加載,如下:
public void loadImage(View v){
    Intent i = new Intent(this,MainActivity.class);
    startActivity(i);
}
此時效果如下: \ 在第一次運行程序進入MainActivity,執行execute但在顯示出圖片之前,立即點擊“加載圖片”按鈕,新打開一個MainActivity,我們發現這個MainActivity的進度條沒有立即展示出進度出來,說明這個MainActivity的AsyncTask沒有立即執行doInBackground(),這是因為AsyncTask內部使用的是線程池,相當於裡面有一個線程隊列,執行一次execute時會將一個下載任務加入到線程隊列,只有前一個任務完成了,下一個下載任務才會開始執行。 為了達到我們想要的效果,我們自然想到把上一個任務給取消掉。的確,AsyncTask為我們提供了cancel()方法來取消一個任務的執行,但是要注意的是,cancel方法並沒有能力真正去取消一個任務,其實只是設置這個任務的狀態為取消狀態,我們需要在doInBackground()下載中進行檢測,一旦檢測到該任務被用戶取消了,立即停止doInBackground()方法的執行。 我們先修改MainActivity,根據不同業務需求,在不同地方進行任務的取消,我們這裡在onPause()中進行任務的取消,在MainActivity方法中加入onPause()方法,如下:
@Override
protected void onPause() {
    super.onPause();
    if(asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING){
        //cancel只是將對應的任務標記為取消狀態
        asyncTask.cancel(true);
    }
}
繼續修改AsyncTask,在這裡面進行任務是否被取消的檢測,這裡我們只簡單修改下doInBackground()和onProgressUpdae()方法,實際項目中開自己的業務邏輯來控制,如下:
@Override
protected Bitmap doInBackground(String... params) {
    String urlParams = params[0];//拿到execute()傳過來的圖片url
    Bitmap bitmap = null;
    URLConnection conn = null;
    InputStream is = null;
    try {
        URL url = new URL(urlParams);
        conn = url.openConnection();
        is = conn.getInputStream();

        for(int i = 0; i < 100; i++){
            if(isCancelled()){//通過isCancelled()判斷任務任務是否被取消
                break;
            }
            publishProgress(i);
            Thread.sleep(50);//為了看清效果,睡眠一段時間
        }
        BufferedInputStream bis = new BufferedInputStream(is);
        bitmap = BitmapFactory.decodeStream(bis);

        is.close();
        bis.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return bitmap;
}

@Override
protected void onProgressUpdate(Integer... values) {
    super.onProgressUpdate(values);
    if(isCancelled()){//通過isCancelled()判斷任務任務是否被取消
        return;
    }
    mPreogressBar.setProgress(values[0]);
}
在doInBackground()的for循環更新進度過程中,我們持續不斷的監聽任務十分被取消,一旦取消了,盡快退出doInBackground的執行,現在運行效果如下: \ 可以看到,現在每次點擊“加載圖片”按鈕,新的界面都會立即更新進度條,我們就業把前面的任務給取消掉了。   使用小結: (1)AsyncTask中,只有doInBackground()方法是處於子線程中運行的,其他三個回調onPreExecute()、onPostExecute()、onProgressUpdate()都是在UI線程中進行,因此在這三個方法裡面可以進行UI的更新工作; (2)每一個new出的AsyncTask只能執行一次execute()方法,多次運行將會報錯,如需多次,需要新new一個AsyncTask; (3)AsyncTask必須在UI線程中創建實例,execute()方法也必須在UI線程中調用;  

2、AsyncTask內部實現原理

進入AsyncTask源碼,簡單分析一下AsyncTask的內部實現。前面說到,AsyncTask內部封裝了異步任務隊列和Handler,那我們就從這兩塊入手。  

2.1 AsyncTask的內部異步隊列

在我們上面那個例子中,講到第二個現象時,就可以直觀的看到,AsyncTask的內部肯定是基於工作隊列這種方式的,每次執行execute()就會把當前的任務加入到工作隊列中。但是大家有沒有發現這麼個問題,我每次點擊“加載圖片”按鈕,都會進入MainActivity的onCreate方法中去新建一個AsyncTask的對象,既然對象都是新的了,為什麼AsyncTask中的工作隊列還是能夠正常管理呢?這就說明,AsyncTask內部的線程池、工作隊列的定義都應該是static的,而static定義的變量是屬於進程范圍內的,只有這樣,這些static的變量才能交給AsyncTask這個類來管理,而不是AsyncTask的具體子類對象。如下部分變量聲明:
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue sPoolWorkQueue =
            new LinkedBlockingQueue(128);

    /**
     * 真正用來執行任務的的線程池
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * 定義一個線程池,在線程池中有一個Runnable任務隊列,用來存放、順序執行任務
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    //AsyncTask內部默認使用的線程池
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    //自定義的一個Handler
    private static InternalHandler sHandler;
可以看到,優線程池有關的都定義為了static類型,其中,SERIAL_EXECUTOR內部就定義了一個任務隊列ArrayDequemTasks,當我們調用AsyncTask的execute()時,就會將當前任務加入到該隊列中。先來看執行AsyncTask的execute()時的情景:

@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
AsyncTask的execute()交給了executeOnExecutor()方法,將將默認的線程池作為參數傳進來,進入executeOnExecutor方法中:
@MainThread
public final AsyncTask executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {//1、這裡判斷當前AsyncTask是否正在執行或已執行完畢
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }
    mStatus = Status.RUNNING;//2、設置正在執行的狀態
    onPreExecute();//3、回調onPreExecute()方法
    mWorker.mParams = params;
    exec.execute(mFuture);//4、放到前面默認構造的線程池中去執行
    return this;
}
上面第一步,先判斷當前AsyncTask是否正在運行或已經執行完畢,如果正在執行或執行完畢再次執行將拋出異常,這也正是我們前面在使用的時候談到,同一個AsyncTask不能多次進行execute()的原因!到了第三步的時候,先去調用一下onPreExecute()方法,因為executeOnExecutor方法本身就是在UI線程中運行的,所以onPreExecute也會在UI線程中運行。第四步,才會開始講當前AsyncTask任務加入到隊列中,我們進入默認的線程池中去看一下:
private static class SerialExecutor implements Executor {
    final ArrayDeque mTasks = new ArrayDeque();
    Runnable mActive;
    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}
在第四步執行execute時,實際就是調用的SerialExecutor中的execute方法,在這裡面,先創建了一個Runnable對象,然後將這個Runnable對象添加到任務隊列mTasks中,在當執行到這個Runnable時調用scheduleNext去隊列中取出一個任務,然後交給另一個線程池去真正執行這個任務。  

2.2 與UI線程進行交互-handler

AsyncTask內部通過自定義的static類型的InternalHandler和UI線程進行交互。InternalHandler實現如下:
private static class InternalHandler extends Handler {
    public InternalHandler() {
        super(Looper.getMainLooper());
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
每當收到子線程發來的和UI線程進行通信的handler請求時,先從Message中拿到子線程發來的結果參數AsyncTaskResult,AsyncTaskResult裡面封裝了AsyncTask對象和數據信息,如下:
private static class AsyncTaskResult {
    final AsyncTask mTask;
    final Data[] mData;
    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}
然後根據不同狀態調用不同方法,如果是MESSAGE_POST_RESULT狀態,就調用AsyncTask的finish()方法,finish方法中會去判斷當前任務十分被cancel,如果沒有cancel則開始回調onPostExecute()方法;如果狀態是MESSAGE_POST_PROGRESS,則回調onProgressUpdate()方法。 當子線程需要和UI線程進行通信時,就會通過這個handler,往UI線程發送消息。需要通過handler來發送消息,肯定是在子線程異步任務的時候才需要,在AsyncTask中需要handler的地方其實就是兩個地方,一個是doInBackground()在運行過程中,需要更新進度值的時候;一個是doInBackground()運行完成後,需要回到到UI線程中的onPostExecute()方法的時候。 對於一:我們在doInBackground()中調用publicProgress()進行進度值的更新,因此在publicProgress()中肯定會有handler的身影,如下:
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult
(this, values)).sendToTarget();
}
}
對於二:其實就是一個異步任務執行完後後的返回處理,而FutureTask正是處理處理Runnable運行返回結果的。 在2.1部分的executeOnExecutor方法中第四步,我們在執行execute(mFuture),傳入了一個mFuture,mFuture是在初始化AsyncTask的時候進行構建的,如下:
mFuture = new FutureTask(mWorker) {
    @Override
    protected void done() {
        try {
            postResultIfNotInvoked(get());//這裡面通過handler往UI線程發送消息
        } catch (InterruptedException e) {
            android.util.Log.w(LOG_TAG, e);
        } catch (ExecutionException e) {
            throw new RuntimeException("An error occurred while executing doInBackground()",
                    e.getCause());
        } catch (CancellationException e) {
            postResultIfNotInvoked(null);
        }
    }
};
在上面的postResultIfNotInvoked()中會通過handler進行消息的發送。   AsyncTask原理總結: AsyncTask主要是對異步任務和handler的封裝,在處理異步任務時,AsyncTask內部使用了兩個線程池,一個線程池sDefaultExecutor是用來處理用戶提交(執行AsyncTask的execute時)過來的異步任務,這個線程池中有一個Runnable異步任務隊列ArrayDequemTasks,把提交過來的異步任務放到這個隊列中;另一個線程池THREAD_POOL_EXECUTOR,用來真正執行異步任務的。在處理handler時,自定義了一個InternalHandler,在publicProgress()和doInBackground()運行完成後,會通過這個handler往UI線程發送Message。  

3、AsyncTask在使用中的一個特殊情況

我們在使用AsyncTask的時候,一般會在onPreExecute()和onPostExecute()中進行UI的更新,比如等待圖片的顯示、進度條的顯示...當我們一個Activity中正在使用AsyncTask進行文件的下載時,如果此時屏幕發生了旋轉,Activity會進行re-onCreate,又會創建一個AsyncTask進行文件的下載,這個正是我們前面將取消任務的時候談到的第二個現象,我們只需要在onPause()中進行取消cancel()即可。但是這樣僅僅是解決了發生等待的情況,因為Activity再次進入了onCreate()方法,還是會進行文件的下載,為了解決這個問題,一種方案是通過判斷onCreate(Bundle savedInstanceState)方法參數中的savedInstanceState==null?來判斷是哪種情況,只有savedInstanceState==null時才去創建新的AsyncTask。  

4、AsyncTask和Handler的比較

AsyncTask: 優點:AsyncTask是一個輕量級的異步任務處理類,輕量級體現在,使用方便、代碼簡潔上,而且整個異步任務的過程可以通過cancel()進行控制; 缺點:不適用於處理長時間的異步任務,一般這個異步任務的過程最好控制在幾秒以內,如果是長時間的異步任務就需要考慮多線程的控制問題;當處理多個異步任務時,UI更新變得困難。 Handler: 優點:代碼結構清晰,容易處理多個異步任務; 缺點:當有多個異步任務時,由於要配合Thread或Runnable,代碼可能會稍顯冗余。   總之,AsyncTask不失為一個非常好用的異步任務處理類,只要不是頻繁對大量UI進行更新,可以考慮使用;而Handler在處理大量UI更新時可以考慮使用。   下一篇就要開始圖片請求的二級緩存框架的編寫了~      
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved