編輯:關於Android編程
在平時項目開發中難免會遇到異步耗時的任務(比如最常見的網絡請求)。遇到這種問題,我們可以自己通過Handler+Message+Thread/ThreadPool來構造一個異步耗時任務框架。當你下次項目中又遇到一個網絡請求,你又不得不重寫異步耗時任務處理框架。出於避免開發者重復搬磚工作,Google工程師給開發者搭建了一個通用的異步耗時任務處理框架—-AsyncTask。
我把AsycnTask類稱之為異步任務處理框架,為什麼這麼說?因為其內部通過Handler+Message+ThreadPool技術構建了一個異步耗時任務處理框架。所謂框架,無非就是封裝復雜的邏輯操作,留給開發者少數接口或者方法來進行數據操作。AsyncTask類也一樣,目的是讓開發者很方便容易的在UI線程中處理一些異步耗時任務。AsyncTask類中將異步耗時任務處理放在ThreadPool線程池中處理,然後將處理結果通過Handler發送消息更新進度和結果,開發者只需要實現和重寫AsyncTask類中的幾個方法即可獲得當前異步任務處理的進度和結果。
由於AsyncTask是一個抽象類,所以開發者如果想使用AsyncTask類的話必須讓子類去繼承它。子類至少重寫AsyncTask類中的 doInBackground方法,一般我們也會重寫onPostExecute方法去獲取異步任務處理結果。在使用AsyncTask類時,我們知道需要准備3個泛型參數分別是:
Params:異步任務執行時需要的參數類型 Progress:異步任務執行過程中進度更新類型 Result:異步任務執行結束返回的結果類型當你在使用AsyncTask無需相應的參數時可以將對應參數設置為 Void類型。
AsyncTask使用的步驟順序可分為如下:
onPreExecute:該方法由系統在UI線程中調用,異步任務執行之前調用,做一些准備工作,比如初始化進度條。無需用戶自己去調用,用戶只需重寫該方法即可,當然用戶也可以不重寫該方法。 doInBackground:該方法由系統執行於後台線程中,當onPreExecute方法調用之後就調用該方法。所有異步耗時的任務都是在該方法中實現,同樣用戶無需自己去調用,用戶只需重寫該方法即可,且必須重寫該方法。 publishProgress:該方法在doInBackground方法中調用,用於發布當前異步任務執行的進度到UI線程中,該方法需要用戶自己在onInBackground中調用。 onProgressUpdate:該方法由系統在UI線程中調用,用於更新當前異步任務執行的進度,進而更新UI操作,該方法也無需用戶自己調用,用戶只需重寫該方法即可。想要在UI線程中獲得進度信息的前提是在doInBackground方法中調用了publishProgress方法。 onPostExecute:該方法由系統在UI線程中調用,用於異步任務執行完成之後獲取後台線程執行的結果。該方法同樣無需用戶自己調用,只需重寫該方法即可。上面的解釋可能會有點抽象,現在我們拿一個例子來說明AsyncTask的使用。
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
private TextView value;
private TextView result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
result = (TextView) findViewById(R.id.result);
}
//啟動一個任務
public void startTask(View view) {
String s1 = task1;
String s2 = task2;
String s3 = task3;
String s4 = task4;
String s5 = task3;
String s6 = task3;
String s7 = task3;
String s8 = task3;
new DownloadFilesTask().execute(s1, s2, s3, s4, s5, s6, s7, s8);
}
private class DownloadFilesTask extends AsyncTask {
//該方法執行與後台線程中,所有異步耗時的任務都在此處操作
@Override
protected Long doInBackground(String... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
try {
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
totalSize += 800;
//發布進度信息到UI線程中
publishProgress((int) ((i / (float) (count - 1)) * 100));
//為了安全起見,每次循環都需檢查當前任務是否被取消,如果被取消這退出循環
if (isCancelled()) break;
}
return totalSize;
}
//該方法在後台任務執行之前運行,在UI線程中執行,用於初始化一些信息
@Override
protected void onPreExecute() {
value = (TextView) findViewById(R.id.progress_value);
progressBar = (ProgressBar) findViewById(R.id.progress);
progressBar.setMax(100);
}
//該方法在UI線程中執行,用於獲取後台任務更新的進度信息
@Override
protected void onProgressUpdate(Integer... values) {
value.setText(values[0] + %);
progressBar.setProgress(values[0]);
}
//該方法在UI線程中執行,用於獲取後台任務執行完成之後的結果
@Override
protected void onPostExecute(Long aLong) {
result.setText(the result is + aLong);
Toast.makeText(MainActivity.this, the result is + aLong, Toast.LENGTH_SHORT).show();
}
}
}
以上示例是一個最簡單的模擬異步任務操作,我們主要的工作就是讓子類DownloadFilesTask繼承AsyncTask,然後重寫相應的方法,其中只要是重寫的方法都無需用戶去控制其調用邏輯,只需重寫裡面的方法實現計算邏輯。言外之意就是:AsycnTask類中的那些方法的調用順序是不需要用戶去控制的,其內部已經控制好這些方法的調用邏輯。
【轉載請注明出處:http://blog.csdn.net/feiduclear_up CSDN 廢墟的樹】
public abstract class AsyncTask {
//獲得當前運行狀態的cup數
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//根據當前機器CUP的個數決定線程池中的線程個數
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//獲得線程池中最大線程數
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//線程的存活時間
private static final int KEEP_ALIVE = 1;
//線程工廠,為線程池創建所需線程
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());
}
};
//線程池中的緩存隊列,此處為128個
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
//根據以上參數,構造線程池執行器
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
//獲得順序執行的線程池執行器
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//異步任務處理結果碼和進度更新碼
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
//內部類,消息的執行者handler對象
private static final InternalHandler sHandler = new InternalHandler();
//線程池中默認的執行器
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//異步任務回調接口
private final WorkerRunnable mWorker;
private final FutureTask mFuture;
//當前異步任務的狀態,初始狀態為“未執行”狀態
private volatile Status mStatus = Status.PENDING;
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
......................
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
//創建一個新的異步任務,該構造方法必須在UI線程中調用
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} 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);
}
}
};
}
..................
}
分析:AsyncTask類中的所有成員變量的作用都已經添加注釋,在AsyncTask類的成員變量中根據當前系統CPU的個數來構建一個固定大小的線程池THREAD_POOL_EXECUTOR成員變量。然後通SerialExecutor類創建了一個順序執行的線程池成員變量SERIAL_EXECUTOR,這裡我們暫且不討論SerialExecutor類的具體實現,只需知道它是順序執行的一個線程池執行器就可,感興趣的童鞋可以深究。細心的你會發現AsyncTask的成員變量幾乎都是靜態的,也就是說:一個應用中的內存中只保存有一份這些成員變量的值。
然後在構造方法中獲得了mWorker對象,並且實現了裡面的接口方法call,call方法裡面調用了doInBackground方法。當後台線程任務被執行時,該call方法就會被調用。並且將mWorker作為參數傳遞給了FutureTask類來獲取mFuture對象。因此在AsyncTask的構造方法中最後獲得mFuture對象。FutureTask類也是繼承自Runnable接口的。到此,我們可以理解成mFuture對象封裝了一個後台的異步耗時任務,等待線程池執行器去處理該耗時任務。mFuture對象會作為一個線程接口在後面使用到。
由前面分析我們知道,AsyncTask處理異步任務的邏輯都在該類的內部實現了,我們只需要重寫相應的方法即可。那麼我們就從execute異步任務的執行方法開始跟蹤AsyncTask類內部是怎麼處理異步任務的邏輯的。
/**
* Executes the task with the specified parameters. The task returns
* itself (this) so that the caller can keep a reference to it.
*
*
Note: this function schedules the task on a queue for a single background * thread or pool of threads depending on the platform version. When first * introduced, AsyncTasks were executed serially on a single background thread. * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed * to a pool of threads allowing multiple tasks to operate in parallel. Starting * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being * executed on a single thread to avoid common application errors caused * by parallel execution. If you truly want parallel execution, you can use * the {@link #executeOnExecutor} version of this method * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings * on its use. * *
This method must be invoked on the UI thread. * * @param params The parameters of the task. * * @return This instance of AsyncTask. * * @throws IllegalStateException If {@link #getStatus()} returns either * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. * * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) * @see #execute(Runnable) */ public final AsyncTask
分析:該方法的注釋比方法的實現還多,大概意思是:傳指定參數執行異步任務,該方法的返回值是AsyncTask對象本身,目的是讓調用者持有AsyncTask對象的一個引用。該方法的功能是:不同平台利用不同方式去處理隊列中的任務,AsyncTask最初設計是單個後台線程去處理隊列中的任務,到了Android1.6版本之後改為固定線程數的線程池去處理隊列中的任務,在之後到了Android3.0之後,又改回到單個後台線程去處理隊列中的任務,目的是為了解決Android1.6以後如果異步任務超過138個時AsyncTask會拋出異常。如果在Android3.0以後你想同時執行多個異步任務的話你可以使用AsyncTask類提供的executeOnExecutor方法實現。
其實execute方法的實現也僅僅是調用了executeOnExecutor方法而已。那麼我們跟蹤代碼進入executeOnExecutor方法
/**
This method must be invoked on the UI thread. * * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a * convenient process-wide thread pool for tasks that are loosely coupled. * @param params The parameters of the task. * * @return This instance of AsyncTask. * * @throws IllegalStateException If {@link #getStatus()} returns either * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. * * @see #execute(Object[]) */ public final AsyncTask
分析:注釋解釋,該方必須在UI線程中調用。第一個參數為線程池執行器Executor對象。第二個參數為異步任務執行所需參數Params。方法的返回值為AsyncTask實例對象。
1.代碼第16-26行:判斷當前異步任務狀態,如果不是”PENDING“未執行狀態,則會拋出相應的異常,如果是”RUNNING“,這拋出”Cannot execute task: the task is already running.”:當前任務正在執行;如果是”FINISHED”,則拋出Cannot execute task: the task has already been executed (a task can be executed only once)”:該任務已經被執行。由此可知AsyncTask同一個異步任務只能被執行一次。
2.代碼第28行:標記當前異步任務的狀態為”RUNNING”表示任務正在執行。
3.代碼第30行:調用onPreExecute方法,該方法是個空方法,在子類中可以重寫該方法做一些初始化的工作,比如初始化進度條信息等。該方法執行與UI線程中。
4.代碼第32-33行:調用線程池執行器去處理異步任務mFuture,此處開始執行異步耗時任務,在線程中操作。由AsyncTask構造方法知道,mFuture是一個異步任務的封裝。來看看FutureTask類中的run方法吧
...............
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
........
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
}
...............
}
分析:在FutureTask的構造方法中我們看到,將AsyncTask構造方法中的mWorker變量賦值給了FutureTask類中的callable變量。我們看到代碼第24行調用了callable的call方法,因此這裡調用如下接口方法
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
有上面代碼可以看出,在call方法中doInBackground方法的返回值作為參數傳遞給postResult方法,然後doInBackground是一個抽象方法,真正執行異步耗時任務的方法,具體實現需要在AsyncTask子類中實現。那麼我們來看看postResult方法做了什麼?
private Result postResult(Result result) {
@SuppressWarnings(unchecked)
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
該方法先通過getHandler方法獲得一個Handler對象,然後將異步任務doInBackground方法返回的結果Result封裝成消息發送出去,由 從Handler+Message+Looper源碼帶你分析Android系統的消息處理機制這篇博客我們知道,Handler消息處理機制是誰發送消息誰處理消息。那麼我們來看看getHandler方法吧
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
加鎖獲得Handler對象,目的是防止多線程同時持有Handler對象導致異步任務處理混亂情況。
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;
}
}
}
有上面代碼可以看出,InternalHandler是AsyncTask類的內部類,然而InternalHandler是通過Looper.getMainLooper()UI線程的Looper作為參數構造的,因此InternalHandler是在UI線程中處理消息的。我們看到,上面有兩條消息分支。
1.MESSAGE_POST_RESULT:也就是調用了postResult方法之後發送的消息才走這條分支,將異步任務處理結果推送到UI線程中。此處又調用了finish方法,進入看看其實現
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
該方法表示異步任務處理結束,首先判斷當前異步任務是否已經被取消,如果被取消則調用onCancelled方法,子類中如果想在異步任務取消時候做一些處理的話可以重寫onCancelled方法。如果沒有取消則調用onPostExecute方法,該方法的實現體是一個空,需要在子類中去實現拿到異步任務和處理結果。最後異步任務處理結束,將當前異步任務的狀態置為FINISHED狀態,以防止同一個任務被執行多次。
2.MESSAGE_POST_PROGRESS:該條消息是用來發布異步任務進度信息的。最後會調用onProgressUpdate來發布進度信息到UI線程中,其中該方法是一個空的,需要子類去實現獲得當前異步任務執行進度。那什麼情況下發送了這條消息呢?我們查看代碼發現,只有AsyncTask類中的publishProgress方法裡面發送了這條信息。那進入該方法看看
/**
* This method can be invoked from {@link #doInBackground} to
* publish updates on the UI thread while the background computation is
* still running. Each call to this method will trigger the execution of
* {@link #onProgressUpdate} on the UI thread.
*
* {@link #onProgressUpdate} will not be called if the task has been
* canceled.
*
* @param values The progress values to update the UI with.
*
* @see #onProgressUpdate
* @see #doInBackground
*/
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
方法的注釋解釋:該方法需要在doInBackground方法中調用,目的是去更新當前異步任務執行進度,每調用一次該方法就會觸發onProgressUpdate方法的調用,也就是每調用一次publishProgress方法就會發送一個消息碼MESSAGE_POST_PROGRESS的消息到InternalHandler類處理。
總結:由此可知,如果你想要在UI線程中獲得當前異步任務執行的進度信息,就必須在onInBackground方法中調用publishProgress方法,否則就獲取不到進度信息。
很多人可能都知道在Android3.0之前的AsyncTask類是有一個缺陷的,也就是當你同時執行139個異步任務的時候就會出錯了。為什麼會是這樣子呢?所有玄機都在AsyncTask的執行方法execute裡面。我們不妨從Android2.3的AsyncTask源碼去解答該答案,在該版本中的execute方法如下:
public final AsyncTask More ...execute(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;
sExecutor.execute(mFuture);
return this;
}
有上面代碼可以發現,Android2.3版本的execute方法實現和Android3.0以後的不一樣,也就是上面我們分析的。在2.3版本之前線程池執行器是這麼構造的
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue sWorkQueue =
new LinkedBlockingQueue(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread More ...newThread(Runnable r) {
return new Thread(r, AsyncTask # + mCount.getAndIncrement());
}
};
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
主要是構造一個固定線程為5,最大線程為128,緩存對了為10的一個線程池執行器。由此可以知道,當有第139個異步任務執行的時候就超出了最大線程數和緩存隊列的總和。因此會報錯。那麼Android3.0以後的版本是怎麼解決這個問題的呢?
在Android3.0以後的版本AsyncTask類修改了execute方法的實現
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
該方法又調用了executeOnExecutor方法去執行異步任務,此處使用的線程池執行器是sDefaultExecutor默認的執行器,有前面我們分析可得,默認的線程池執行器是一個順序執行的,且緩存隊列是無限大。也就是多個異步任務是順序執行的,只有當第一個異步任務執行完之後才能執行第二個,這麼一來不管你有多少個異步任務,都不會報錯。那有人可能會問?如果我需要在Android3.0以後的版本上同時執行多個異步任務怎麼辦?哈哈!騷年,不用擔心!Google Android工程師早已給你想好了。在Anddroid3.0以後的AsyncTask類給暴露出一個接口也就是上面的executeOnExecutor方法啦,我們只需要重新構造一個線程池執行器,比如說你可以調用newCachedThreadPool方法來創建一個無線大的緩存線程池,可以同時執行無線個任務。
//構建一個緩存線程池,用於同時執行無限多個異步耗時任務
ExecutorService executorService = Executors.newCachedThreadPool();
asyncTask.executeOnExecutor(executorService,params);
開發者可以根據項目需求選擇自己合適的線程池執行器:
Single Thread Executor : 只有一個線程的線程池,因此所有提交的任務是順序執行,代碼: Executors.newSingleThreadExecutor() Cached Thread Pool : 線程池裡有很多線程需要同時執行,老的可用線程將被新的任務觸發重新執行,如果線程超過60秒內沒執行,那麼將被終止並從池中刪除,代碼:Executors.newCachedThreadPool() Fixed Thread Pool : 擁有固定線程數的線程池,如果沒有任務執行,那麼線程會一直等待,代碼: Executors.newFixedThreadPool() Scheduled Thread Pool : 用來調度即將執行的任務的線程池,代碼:Executors.newScheduledThreadPool() Single Thread Scheduled Pool : 只有一個線程,用來調度執行將來的任務,代碼:Executors.newSingleThreadScheduledExecutor()總結: 在Android3.0之前的AsyncTask是有缺陷的,因為其內部使用了固定線程數和緩存大小的線程池來執行異步耗時任務,所以當同時有超過139個異步耗時任務時,AsyncTask就會報錯。 然而在Android3.0以後的AsyncTask是沒有缺陷的,因為其背部使用了一個順序執行的線程池來執行異步耗時任務,不論有多少個異步任務每次都只能執行一個,所以不會報錯。且Android3.0之後的AsyncTask提供了自定義線程池的方法,更加方便靈活的讓開發者根據自己所需來選擇不同的線程池執行器來處理耗時任務。
值得注意的是:如果你要兼容Android2.3以及3.0使用AsyncTask同時處理139個異步耗時任務是不可能的,這個時候你只能自己利用 Handler+Message+ThreadPool+Exexutor自己構建一個異步任務處理框架了。考驗你技術的時候到啦!其實你可以仿著3.0的AsyncTask寫一個就好了。
1.AsyncTask類只能在UI線程中使用,為什麼?可以看 AsycnTask的執行方法execute小節,因為裡面的Handler是有MainLooper構造的。
2.初學者可能會有這個疑問?一個App應用中有很多個異步耗時任務,每個任務都去創建一個AsyncTask對象豈不是很耗費資源?如果這麼想你就還不理解AsyncTask了。從AsyncTask的構造方法小節可以看到,裡面的絕大多數成員變量都是靜態static的。包括Executor執行器。因此整個應用都是共享且只有一個AsyncTask實例的。
3.關於AsyncTask類中的方法調用邏輯無需用開發者去關注,開發者只需要重寫相應的方法即可。當然如果你想在UI線程中獲得進度信息,你就必須在doInBackground方法中調用publishProgress方法才可以觸發UI線程中的onProgressUpdate方法來更新進度信息。
4.值得一提的是:在你重寫doInBackground方法執行異步耗時任務時,你最好在適當的地方調用
if (isCancelled()){
//做一些任務取消的處理
}
此方法調用的目的是檢測在執行異步耗時任務時該任務是否已經被取消了?如果被取消了,當前正在執行的異步任務就沒有必要繼續執行了,節省資源。比如博客開頭的例子:異步任務是一個循環任務,如果在執行到第2個循環的時候異步任務被取消了,此時就跳出循環結束此次的異步任務。
5.在Android3.0之前如果你想要同時執行超過139個異步任務的時候,AsyncTask是會報錯的,這時候你就不得不自己去重新構造一個多線程異步任務處理框架了,就不能直接使用AsyncTask框架了。但是在Android3.0之後改善了這個問題,3.0之後默認是順序執行異步任務,無論你有多少個異步任務都不會報錯,你也可以自定義一個符合需求的線程池執行器。
先看看效果圖:首先是布局文件<FrameLayout android:layout_width=match_parent android:layout_margin
如果你做過多媒體應用,一定會苦惱過,怎樣獲取sd卡中的多媒體文件。android還是很強大的,如果你知道怎麼調用android的api,萬事就ok了。 當手機或模擬器開機
在app開發過程中,經常需要顯示media文件的meta data信息,我們如何獲取這些信息呢? MediaStore首先想到的就是MediaStore類了,它
前言 高效的設計稿標注及測量工具Markman介紹。最近有個煩惱是UI設計師可能太忙了,經常給出的UI設計稿中有很多地方都沒有標注,比如長度和顏色值等。這個時候每次都要通