AsyncTask

Android開發(fā)者:你真的會用AsyncTask嗎?
導讀.1

在Android應用開發(fā)中,我們需要時刻注意保證應用程序的穩(wěn)定和UI操縱響應及時,因為不穩(wěn)定或響應緩慢的應用將會給應用帶來不好的印象。本文試圖從AsyncTask的作用說起,進一步講解一下內部的實現(xiàn)機制。


為什么引入AsyncTask?

在Android程序開始運行的時候會單獨啟動一個進程,默認情況下所有這個程序操作都在這個進程中進行。一個Android默認情況下只有一個進程,但是一個進程確實可以有許多線程的。

在這些線程中,有一個線程叫做UI線程,也叫作Main Thread,除了Main Thread之外的線程都可稱為Worker Thread.Main Thread主要負責控制UI頁面的顯示、更新、交互等。因為所有在UI線程中的操作要求越短越好,只有這樣用戶才會覺得操作比較流暢。一個好的做法是把一些比較耗時的操作,例如網絡請求、數據庫操作、復雜計算等邏輯都封裝到單獨的線程,這樣就可以避免阻塞主線程。為此,有人寫了如下的代碼:
利用Thread來實現(xiàn)
Android為我們提供了一個輕量級的異步類可以直接繼承AsyncTask,在類中實現(xiàn)異步操作,并提供接口反饋當前異步執(zhí)行的結果以及執(zhí)行進度,這些接口中有直接運行在主線程的,例如onPostExecute,onPreExecute等方法。

也就是說,Android的程序運行時是多線程的,為了更方便的處理子線程和UI線程的交互,引入了AsyncTask。

AsyncTask內部機制

AsyncTask內部邏輯主要有二個部分:

  1. 與主線程的交互,它內部實例化了一個靜態(tài)的自定義類InternalHandler,這個類是繼承自Handler的,在這個自定義類中綁定了一個叫做AsyncTaskResult的對象,每次子線程需要通知主線程,就調用sendToTarget發(fā)送消息給handler。然后在handler的handleMessage中AsyncTaskResult根據消息的類型不同(例如MESSAGEPOSTPROGRESS會更新進度條,MESSAGEPOSTCANCEL取消任務)而做不同的操作,值得一提的是,這些操作都是在UI線程進行的,意味著,從子線程一旦需要和UI線程交互,內部自動調用了handler對象把消息放在了主線程了。

  2. AsyncTask內部調度
    雖然可以新建多個AsyncTask的子類的實例,但是AsyncTask的內部Handler和ThreadPoolExecutor都是static的,這么定義的變量屬于類的,是進程范圍內共享的,所以AsyncTask控制著進程范圍內的所有子類實例,而且該類的所有實例都共用一個線程池和Handler。

可以看出,AsyncTask使用過程中需要注意的地方不少

  • 由于Handler需要和主線程交互,而Handler又是內置于AsnycTask中的,所以,AsyncTask的創(chuàng)建必須在主線程。
  • AsyncTaskResult的doInBackground(mParams)方法執(zhí)行異步任務運行在子線程中,其他方法運行在主線程中,可以操作UI組件。
  • 不要手動的去調用AsyncTask的onPreExecute, doInBackground, publishProgress, onProgressUpdate, onPostExecute方法,這些都是由Android系統(tǒng)自動調用的
  • 一個任務AsyncTask任務只能被執(zhí)行一次。
  • 運行中可以隨時調用cancel(boolean)方法取消任務,如果成功調用isCancelled()會返回true,并且不會執(zhí)行 onPostExecute() 方法了,取而代之的是調用 onCancelled() 方法。而且從源碼看,如果這個任務已經執(zhí)行了這個時候調用cancel是不會真正的把task結束,而是繼續(xù)執(zhí)行,只不過改變的是執(zhí)行之后的回調方法是 onPostExecute還是onCancelled。

AsyncTask和Activity OnConfiguration

上面提到了那么多的注意點,還有其他需要注意的嗎?當然有!我們開發(fā)App過程中使用AsyncTask請求網絡數據的時候,一般都是習慣在onPreExecute顯示進度條,在數據請求完成之后的onPostExecute關閉進度條。這樣做看似完美,但是如果您的App沒有明確指定屏幕方向和configChanges時,當用戶旋轉屏幕的時候Activity就會重新啟動,而這個時候您的異步加載數據的線程可能正在請求網絡。當一個新的Activity被重新創(chuàng)建之后,可能又重新啟動了一個新的任務去請求網絡,這樣之前的一個異步任務不經意間就泄漏了,假設你還在onPostExecute寫了一些其他邏輯,這個時候就會發(fā)生意想不到異常。

一般簡單的數據類型的,對付configChanges我們很好處理,我們直接可以通過onSaveInstanceState()和onRestoreInstanceState()進行保存與恢復。 Android會在銷毀你的Activity之前調用onSaveInstanceState()方法,于是,你可以在此方法中存儲關于應用狀態(tài)的數據。然后你可以在onCreate()或onRestoreInstanceState()方法中恢復。

但是,對于AsyncTask怎么辦?問題產生的根源在于Activity銷毀重新創(chuàng)建的過程中AsyncTask和之前的Activity失聯(lián),最終導致一些問題。那么解決問題的思路也可以朝著這個方向發(fā)展。Android官方文檔 也有一些解決問題的線索。

這里介紹另外一種使用事件總線的解決方案,是國外一個安卓大牛寫的。中間用到了Square開源的EventBus類庫http://square.github.io/otto/。首先自定義一個AsyncTask的子類,在onPostExecute方法中,把返回結果拋給事件總線,代碼如下:


Android中糟糕的AsyncTask

AsyncTask是一個很常用的API,尤其異步處理數據并將數據應用到視圖的操作場合。其實AsyncTask并不是那么好。本文會將AsyncTask會引起哪些問題,如何修復這些問題,并且關于AsyncTask的一些替代方案


AsyncTask是抽象類.AsyncTask定義了三種泛型類型 Params,Progress和Result。   
Params 啟動任務執(zhí)行的輸入參數,比如HTTP請求的URL。   
Progress 后臺任務執(zhí)行的百分比。   
Result 后臺執(zhí)行任務最終返回的結果,比如String。

AsyncTask的執(zhí)行分為四個步驟:每一步都對應一個回調方法,這些方法不應該由應用程序調用,開發(fā)者需要做的就是實現(xiàn)這些方法.

  1. 子類化AsyncTask
  2. 實現(xiàn)AsyncTask中定義的下面一個或幾個方法
  • onPreExecute(), 該方法將在執(zhí)行實際的后臺操作前被UI thread調用??梢栽谠摲椒ㄖ凶鲆恍蕚涔ぷ?,如在界面上顯示一個進度條。
  • doInBackground(Params...), 將在onPreExecute 方法執(zhí)行后馬上執(zhí)行,該方法運行在后臺線程中。這里將主要負責執(zhí)行那些很耗時的后臺計算工作。可以調用 publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現(xiàn)。
  • onProgressUpdate(Progress...),在publishProgress方法被調用后,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。
  • onPostExecute(Result), 在doInBackground 執(zhí)行完成后,onPostExecute 方法將被UI thread調用,后臺的計算結果將通過該方法傳遞到UI thread.

為正確的使用AsyncTask**類,以下是幾條必須遵守的準則: **

  1. Task的實例必須在UI thread中創(chuàng)建
  2. execute方法必須在UI thread中調用
  3. 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法
  4. 該task只能被執(zhí)行一次,否則多次調用時將會出現(xiàn)異常 doInBackground方法和onPostExecute的參數必須對應,這兩個參數在AsyncTask聲明的泛型參數列表中指定,第一個為doInBackground接受的參數,第二個為顯示進度的參數,第第三個為doInBackground返回和onPostExecute傳入的參數。

Android AsyncTask完全解析

http://blog.csdn.net/liubin8095/article/details/12705479?utm_source=tuicool&utm_medium=referral

AsyncTask本質是用handler更新界面;在3.0版本以后,它在AsyncTask中是以常量
的形式被使用的,因此在整個應用程序中的所有AsyncTask實例都會共用一個SerialExecutor;默認情況下SerialExecutor模仿的是單一線程池效果,如果我們快速地啟動了很多任務,同一時刻只會有一個線程正在執(zhí)行,其余的均處于等待狀態(tài);如果想同時啟動多個任務可以通過

Executor exec = new ThreadPoolExecutor(15,200,10,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
new DownloadTask().executeOnExecutor(exec);

這樣就可以使用我們自定義的一個Executor來執(zhí)行任務,而不是使用SerialExecutor。上述代碼的效果運行在同一時刻有15個任務正在執(zhí)行,并且最多能夠存儲200個任務。


我們都知道,Android UI線程是不安全的,如果想要在子線程里進行UI操作,就需要借助Android異步消息處理機制。(Android Handler、Message完全解析,從源碼角度)

不過為了更加方便我們在子線程中更新UI元素,Android從1.5版本就引入了一個AsyncTask類。首先從其基本用法開始,然后分析源碼。

AsyncTask的基本用法

由于AsyncTask是一個抽象類,所以如果我們想使用它,就必須要創(chuàng)建一個子類去繼承它。在繼承時我們可以為AsyncTask類指定三個泛型參數,這三個參數的用途如下:

  1. Params
    在執(zhí)行AsyncTask時需要傳入的參數,可用于在后臺任務中使用。
  1. Process
    后臺任何執(zhí)行時,如果需要在界面上顯示當前的進度,則使用這里指定的泛型作為進度單位。
  2. Result
    當任務執(zhí)行完畢后,如果需要對結果進行返回,則使用這里指定的泛型作為返回值類型。

第一個參數類型決定了doInBackground()方法的輸入參數類型
第二個參數類型決定了onProgressUpdate()方法的輸入參數類型
第三個參數類型決定了doInBackground()的返回值類型和onPostExecute()的輸入參數類型

因此一個最簡單的自定義AsyncTask就可以寫成如下形式:

class DownloadTask extends AsyncTask<Void, Integer, Boolean>{
....
}

這里我們把AsyncTask的第一個泛型參數指定為Void,表示在執(zhí)行AsyncTask色時候不需要傳入參數給后臺任務。第二個泛型參數指定為Integer,表示使用整型數據來作為進度顯示單位。第三個泛型參數指定為B哦哦了按,則表示使用布爾型數據來反饋執(zhí)行結果。

當然,目前我們定義的DownloadTask還是一個空任務,并不能進行任何實際的操作,我們還需要去重寫AsyncTask中的幾個方法才能完成對任務的定制。經常需要去重寫的方法有以下四個:

  1. onPreExecute()
    這個方法會在后臺任務開始執(zhí)行之前調用,用于進行一些頁面上的初始化操作,比如顯示一個進度條對話框等。
  1. doInBackground(Params...)
    這個方法的所有代碼都會在子線程中運行,我們應該在這里去處理所有的耗時任務。任務一旦完成就可以通過return語句來將任務的執(zhí)行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執(zhí)行結果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說返饋當前任務的執(zhí)行進度,可以調用publishProgress(Progess...)方法來完成。
  2. onProgressUpdate(Progress...)
    當在后臺任務執(zhí)行完畢并通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作為參數傳遞到此方法中,可以利用返回的數據來進行一些UI操作,比如說提醒任務執(zhí)行的結果,以及關閉掉進度條對話框等。

因此一個完整的自定義AsyncTask就可以寫成如下方式:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();
}

@Override
protected Boolean doInBackground(Void... params)     {
try{
while(true) {
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if(downloadPercent >=100){
break;
}
}
} catch(Exception e){
return false;}
return true;
}

@Override
protected void onProgressUpdate(Integer...values) {
progressDialog.setMessage("當前下載進度:"+values[0]+"%");
}

@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if(result) {
Toast.makeText(context, "下載成功",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, "下載失敗",Toast.LENGTH_SHORT).show();
}
}
}

這里我們虛擬了一個下載任務,在doInBackground()方法中去執(zhí)行具體的下載邏輯,在onProgressUpdate()方法中顯示當前的下載進度,在onPostExecute()方法中來提示任務的執(zhí)行結果。如果想要啟動這個任務,只需要簡單地調用一下代碼即可:

new DownloadTask().execute();

以上就是AsyncTask的基本用法。是不是感覺在子線程和UI線程之間進行切換變得靈活了很多?我們并不需要去考慮什么異步消息處理機制,也不需要專門使用一個Handler來發(fā)送和接收消息,只需要調用一下publishProgess()方法就可以輕松地從子線程切換到UI線程了。

分析AsyncTask的源碼

這里分析的是Android 4.0的源碼。

從之前DownloadTask的代碼就可以看出,在啟動某一個任務之前,要先new出它的實例,因此,我們就先來看一看AsyncTask構造函數中的源碼,如下所示:

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        return postResult(doInBackground(mParams));
    }
};
mFuture = new FutureTask<Result>(mWorker) {
    @Override
    protected void done() {
        try {
            final Result result = get();
            postResultIfNotInvoked(result);
        } catch (InterruptedException e) {
            android.util.Log.w(LOG_TAG, e);
        } catch (ExecutionException e) {
            throw new RuntimeException("An error occured while executing doInBackground()",
                    e.getCause());
        } catch (CancellationException e) {
            postResultIfNotInvoked(null);
        } catch (Throwable t) {
            throw new RuntimeException("An error occured while executing "
                    + "doInBackground()", t);
        }
    }
};
}

這段代碼雖然看起來有點長,但實際上并沒有任務具體的邏輯會得到執(zhí)行,只是初始化了兩個變量,mWorker和mFuture,并在初始化mFuture的時候將mWorker作為參數傳入。mWorker是一個Callable對象,mFuture是一個FutureTask對象,這兩個變量會暫時保存在內存中,稍后才會用到它們。
接著如果想要啟動某一個任務,就需要調用該任務的execute()方法,因此現(xiàn)在我們來看一看execute()方法的源碼,如下所示:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}

只有一行代碼,僅是調用了executeOnExecutor()方法,那么具體的邏輯就應該寫在這個方法里面了。

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
    Params... params) {
if (mStatus != Status.PENDING) {
    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;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}

Android Handler、Message完全解析,帶你從源碼的角度徹底理解

解決Android UI線程不安全。
創(chuàng)建一個Message對象,然后借助Handler發(fā)送出去,之后在Handler的handleMessage()方法中獲得剛才發(fā)送的Message對象,然后在這里進行UI操作就不會再出現(xiàn)崩潰了。

這種處理方式被稱為異步消息處理線程。


Android中AsyncTask的使用

android 提供了幾種在其他線程中訪問UI線程的方法:

Activity.runOnUiThread( Runnable )
View.post( Runnable )
View.postDelayed( Runnable, long )
Hanlder

這些類或方法同樣會使你的代碼很復雜很難理解,然而當你需要實現(xiàn)一些很復雜的操作并需要頻繁地更新UI時這會變得更糟糕。
下面我們介紹一個在安卓中比較常用且輕量級的線程輔助類:AsyncTask。它使創(chuàng)建需要與用戶界面交互的長時間運行的任務變得更簡單。


http://blog.csdn.net/hguang_zjh/article/details/41518483

Android API提到,AsyncTask非常適合短時間異步操作。如果需要執(zhí)行長時間操作,最好使用線程池Executor。

  • AsyncTask的生命周期沒有跟Activity的生命周期同步
  • 容易內存泄漏
AsyncTask和Activity的生命周期

如果你在一個Activity中創(chuàng)建了一個AsyncTask,你旋轉了屏幕,這個Activity將會被銷毀并且會重新創(chuàng)建一個新的實例。但是AsyncTask沒有銷毀,它將會繼續(xù)執(zhí)行直到完成。當它執(zhí)行完成后,它實際上是更新了上一個已經不存在的Activity,如果你原本想在onPostExecute()中更新UI的話,這時的AsyncTask將不會更新新的Activity,并且這個操作會引發(fā)一個異常。

內存泄漏問題

在Activity中作為內部類創(chuàng)建AsyncTask很方便。因為AsyncTask在執(zhí)行完成或者執(zhí)行中需要使用Activity中的view,因為內部類可以直接訪問外部類的域(也就是變量)。然而這意味著內部類會持有外部類的一個引用。
當長時間運行時,因為AsyncTask持有Activity的引用,所以即使當該Activity不再顯示時Android也無法釋放其占用的資源。


使用AsyncTask更新UI的示例

GhyAsyncTask.java代碼

public class GhyAsycTask extends AsyncTask<Object,Integer,Integer>
private EditText editText;

//此方法是第一個調用的
@Override
protected Integer doInBackground(Object...arg0){
try {  
        editText = (EditText) arg0[2];  
        Log.v("!", "從外部傳入2個參數 arg0[0]=" + arg0[0] + " arg0[1]=" + arg0[1]);  
        Log.v("!", "在doInBackground方法中做一些耗時的動作");  
        for (int i = 0; i < 5; i++) {  
            // 每次執(zhí)行后方法onProgressUpdate就被調用  
            publishProgress(i + 1, i + 2);  
            Thread.sleep(1000);  
        }  
    } catch (InterruptedException e) {  
        // TODO Auto-generated catch block  
        e.printStackTrace();  
    }  

    // 返回100代表任務成功運行結束了  
    return 100;  
}

 // 任務結束后運行onPostExecute方法  
@Override  
protected void onPostExecute(Integer result) {  
    super.onPostExecute(result);  
    if (result == 100) {  
        Log.v("!", "方法doInBackground成功運行結束了 并且參數result值為" + result);  
        Log.v("!", "并進入onPostExecute方法中");  
    }  
}  

  // publishProgress方法每一次被調用時  
// onProgressUpdate就被執(zhí)行1次  
@Override  
protected void onProgressUpdate(Integer... values) {  
    super.onProgressUpdate(values);  
    Log.v("!", "進入了onProgressUpdate方法");  
    editText.setText("values[0]=" + values[0] + " values[1]=" + values[1]);  
}  

}  

文件Main.java的核心代碼

public class Main extends Activity {
private EditText editText1;

//btn Click中響應實現(xiàn)
new GhyAsyncTask().execute("ghy1","ghy2",editText1);
}

AsyncTask實現(xiàn)異步處理
下載


android開發(fā)一個使用AsyncTask實現(xiàn)異步刷新的功能

/** 一個使用AsyncTask實現(xiàn)簡單異步刷新的功能。

  • 功能介紹:用一個按鈕觸發(fā)事件,用TextView來顯示textview值的變化
    */
    MainActivity
    //onCreate實現(xiàn)
    //btn Click監(jiān)聽
    task = new GetCSDNLogoTask();
    //觸發(fā)異步事件,并初始化參數為0
    task.execute(0);

/**

  • 此方法是在按回退鍵的時候停止后臺任務
    */
    @Override
    protected void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    task.cancel(true);
    }

Android中AsyncTask簡單實例操作

先來看看AsyncTask的定義:

public abstract class AsyncTask<Params, Progress, Result>

三種泛型類型分別代表“啟動任務執(zhí)行的輸入參數”、“后臺任務執(zhí)行的進度”、“后臺計算結果的類型”。在特定場合下,并不是所有類型都被使用,如果沒有被使用,可以用java.lang.Void類型代替。
一個異步任務的執(zhí)行一般包括以下幾個步驟:
1.execute(Params… params),執(zhí)行一個異步任務,需要我們在代碼中調用此方法,觸發(fā)異步任務的執(zhí)行。(這是這個類唯一應該被用戶調用的方法)
2.onPreExecute(),在execute(Params… params)被調用后立即執(zhí)行,一般用來在執(zhí)行后臺任務前對UI做一些標記。
3.doInBackground(Params… params),在onPreExecute()完成后立即執(zhí)行,用于執(zhí)行較為費時的操作,此方法將接收輸入參數和返回計算結果。在執(zhí)行過程中可以調用publishProgress(Progress… values)來更新進度信息。
4.onProgressUpdate(Progress… values),在調用publishProgress(Progress… values)時,此方法被執(zhí)行,直接將進度信息更新到UI組件上。
5.onPostExecute(Result result),當后臺操作結束時,此方法將會被調用,計算結果將做為參數傳遞到此方法中,直接將結果顯示到UI組件上。

MainActivity

mTask = new Mytask();
mTask.execute("http://www.baidu.com");

public void onClick(View v) { 
//取消一個正在執(zhí)行的任務,onCancelled方法將會被調用 
mTask.cancel(true); 
}


private class MyTask extends AsyncTask<String, Integer, String> { 
//onPreExecute方法用于在執(zhí)行后臺任務前做一些UI操作     
@Override 
protected void onPreExecute() { 
Log.i(TAG, "onPreExecute() called"); 
textView.setText("loading..."); 
} 
//doInBackground方法內部執(zhí)行后臺任務,不可在此方法內修改UI 
@Override 
protected String doInBackground(String... params) {     
Log.i(TAG, "doInBackground(Params... params) called"); 
try { 
HttpClient client = new DefaultHttpClient(); 
HttpGet get = new HttpGet(params[0]); 
HttpResponse response = client.execute(get); 
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 
HttpEntity entity = response.getEntity(); 
InputStream is = entity.getContent(); 
long total = entity.getContentLength(); 
ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
byte[] buf = new byte[1024]; 
int count = 0; 
int length = -1; 
while ((length = is.read(buf)) != -1) { 
baos.write(buf, 0, length); 
count += length; 
//調用publishProgress公布進度,最后onProgressUpdate方法將被執(zhí)行 
publishProgress((int) ((count / (float) total) * 100)); 
//為了演示進度,休眠500毫秒 
Thread.sleep(500); 
} 
return new String(baos.toByteArray(), "gb2312"); 
} 
} catch (Exception e) { 
Log.e(TAG, e.getMessage()); 
} 
return null; 
} 
//onProgressUpdate方法用于更新進度信息 
@Override 
protected void onProgressUpdate(Integer... progresses) { 
Log.i(TAG, "onProgressUpdate(Progress... progresses) called"); 
progressBar.setProgress(progresses[0]); 
textView.setText("loading..." + progresses[0] + "%"); 
} 
//onPostExecute方法用于在執(zhí)行完后臺任務后更新UI,顯示結果     
@Override 
protected void onPostExecute(String result) { 
Log.i(TAG, "onPostExecute(Result result) called"); 
textView.setText(result); execute.setEnabled(true); 
cancel.setEnabled(false); 
} 
//onCancelled方法用于在取消執(zhí)行中的任務時更改UI 
@Override 
protected void onCancelled() { 
Log.i(TAG, "onCancelled() called"); 
textView.setText("cancelled"); 
progressBar.setProgress(0); 
execute.setEnabled(true); 
cancel.setEnabled(false); 
} 
}
}

Android官方圖片加載利器BitmapFun解析

通過BitmapFun在項目中使用,結合代碼了解一下BitmapFun加載圖片的原理,本文說明不包括BitmapFun的緩存部分。
Bitmap中有總結
利用AsyncTask加載Bitmap大圖片,Bitmap中有總結,參考下面的文章,采用了弱引用
Android 之Bitmap大圖片加載處理


Android之AsyncTask的用法

Android開發(fā)筆記之:AsyncTask的應用詳解

Android AsyncTask兩種線程池分析和總結

Android 多線程-----AsyncTask詳解

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容