編輯:關於Android編程
先從最熟悉的Task使用開始說起,給出LastDaysTask作為實例如下
@SuppressLint("NewApi")
private class LastDaysTask extends AsyncTask{
@Override
protected void onPreExecute()
{
//TODO UI線程或者主線程
}
@Override
protected Boolean doInBackground(Void... params)
{
//TODO 工作線程或者子線程
return true;
}
@Override
protected void onProgressUpdate(Integer... values)
{
//TODO UI線程或者主線程
}
@Override
protected void onPostExecute(Boolean aBoolean)
{
//TODO UI線程或者主線程
}
}
這裡我們把AsyncTask的第一個泛型參數指定為Void,表示在執行AsyncTask的時候不需要傳入參數給後台任務。第二個泛型參數指定為Integer,表示使用整型數據來作為進度顯示單位。第三個泛型參數指定為Boolean,則表示使用布爾型數據來反饋執行結果。
接下來需要重寫其中的四個方法,分別是:
onPreExecute() onProgressUpdate(Integer… values) doInBackground(Void… params) onPostExecute(Boolean aBoolean)對上面這四個方法簡短說明如下:
onPreExecute()
這個方法會在後台任務開始執行之間調用,用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。 onProgressUpdate(Progress…)
當在後台任務中調用了publishProgress(Progress…)方法後,這個方法就很快會被調用,方法中攜帶的參數就是在後台任務中傳遞過來的。在這個方法中可以對UI進行操作,利用參數中的數值就可以對界面元素進行相應的更新。 doInBackground(Params…)
這個方法中的所有代碼都會在子線程中運行,我們應該在這裡去處理所有的耗時任務。任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress…)方法來完成。 onPostExecute(Result)
當後台任務執行完畢並通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作為參數傳遞到此方法中,可以利用返回的數據來進行一些UI操作,比如說提醒任務執行的結果,以及關閉掉進度條對話框等。
使用異步任務獲取數據
@Override
public void onCreate()
{
super.onCreate();
LastDaysTask task = new LastDaysTask();
task.execute();
}
這樣就簡單的完成了子線程和UI線程之間的通信。
看完上面如何使用異步任務獲取遠程數據,我們以task.execute();
作為切入點,深入源碼看看究竟。
AsyncTask
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
/*
如果同一個task被啟動兩次,就會拋出下面兩種異常,所以如果task作為成員變量的時候,一定要判斷當前task是否已經在運行,否則不可以重復execute.除非先把該task取消以後才可以繼續execute.
*/
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;
/*
該方法在UI線程,獲取遠程數據之前,UI需要更新狀態可以重寫該方法
*/
onPreExecute();
/*
這裡有兩個成員變量mWorker和mFuture很重要,這兩個變量都是final型,在構造器中初始化,另外還有一個sDefaultExecutor多線程執行器,然後返回結果當前實例this。
*/
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
成員變量mWorker和mFuture
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private final WorkerRunnable mWorker;
private final FutureTask mFuture;
/*
- 構造器,初始化兩個重要的成員變量mWorker和mFuture,在mWorker中我們看到了熟悉的doInBackground()方法,很明顯這個是放在子線程中執行的,另外WorkerRunnable這個類簡單的實現了Callable接口,新增成員變量Params數組。
*/
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);
}
}
};
}
private static abstract class WorkerRunnable implements Callable {
Params[] mParams;
}
sDefaultExecutor默認分發器
/*
- 默認分發器,SerialExecutor根據名稱大概可以猜到這是個串行分發器,然後前面使用異步任務的時候調用了execute()方法,我們一路跟蹤到這裡,最後進入到上面代碼L29 exec.execute(mFuture), 而這個exec就是這個串行分發器,然後進入串行分發器的execute(final Runnable r)方法中.mTasks是個循環隊列,offer加入隊列尾,poll拿出隊列頭,剛開始進入execute()方法,將mFuture加入循環隊列中,然後判斷mActive(就是加入進來的mFuture)是否為空,第一次進來當然為空,所以執行 scheduleNext()方法,取出剛加入的mFuture放入線程池中去執行,接下來看線程池的定義
*/
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();
}
}
/*
進入線程池execute方法以後執行Runnable中的run方法,即上面L10處,然後執行出入參數為Runnable的run方法,最後在finally塊中執行下面一個隊列中的Runnable的run方法,依次取出隊列。我們看到無論隊列中前面一個Runnable執行的如何都會去取下面一個Runnable實例去執行,所以不用擔心諸如異常導致的阻塞行為。
*/
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
THREAD_POOL_EXECUTOR線程池
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
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;
/*
線程工廠生產出來的線程做了名稱標記,所以我們看Log經常會看到"AsyncTask #" + mCount.getAndIncrement()比如“AsyncTask #1”等等字樣。而線程池的核心線程數和最大線程數都是通過Runtime動態獲取和指定的。
*/
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());
}
};
/**
* 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);
經過上面的源碼分析,我們小結一下:
AsyncTask中有個默認串行分發器,它負責將Runnable實例加入循環隊列中,然後逐一取出交由線程池去執行,根據Runtime決定線程池的大小和規格,最小核心線程數是2,線程名都是以”AsyncTask #”開頭,所以當我們在一個生命周期中提交多個異步任務,一般是逐步加入線程池中去執行,這種實現就像是模擬單一的線程池一樣,如果我們快速地啟動了很多任務,同一時刻只會有一個線程正在執行,其余的均處於等待狀態。我們知道對於這個默認的串行分發器,在Android3.0之前是沒有的,那麼為什麼加入這個默認的串行分發器呢?原因就是AsyncTask在Android3.0以後提供了更大的靈活性,這裡僅僅是提供了默認的串行分發器,如果你感覺太過保守,你可以不使用這個默認的串行分發器,而是直接這樣。
Executor exec = new ThreadPoolExecutor(15, 200, 10,
TimeUnit.SECONDS, new LinkedBlockingQueue());
new LastDaysTask().executeOnExecutor(exec);
這樣就可以使用我們自定義的一個Executor來執行任務,而不是使用SerialExecutor。上述代碼的效果允許在同一時刻有15個任務正在執行,並且最多能夠存儲200個任務。
PS:
在Android 1.5剛引入的時候,AsyncTask的execute是串行執行的; 到了Android 1.6直到Android 2.3.2,又被修改為並行執行了,這個執行任務的線程池就是THREAD_POOL_EXECUTOR,因此在一個進程內,所有的AsyncTask都是並行執行的; 但是在Android 3.0以後,如果你使用execute函數直接執行AsyncTask,那麼這些任務是串行執行的;
小結一下AsyncTask的任務是並行還是串行執行?
前面我們分析了大概脈絡,這裡我們進一步分析子線程和UI線程的通信部分。
提交給線程池執行execute()方法,在sDefaultExecutor默認分發器中的L12行,參數Runnable中的r傳參是mFuture實例,所以直接進入FutureTask的run()方法中.
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);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
重點看上面的L12行,調用Callable的call()方法,而這個Callable就是我們構造器中的mWoker,所以還是回到AsyncTask構造器。
public AsyncTask() {
/*
進入call()方法中,設置task是否被喚起,線程優先級以及我們熟悉的doInBackground方法,並將結果postResult到UI線程中,接著繼續跟postResult方法。
*/
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
}
postResult()方法,該方法完成線程間通信。
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
/*
這裡看到了熟悉的Message,獲取message以後將消息一並發出去,接受者key就是MESSAGE_POST_RESULT,所以我們跟蹤這個key,看看處理這如何處理。
*/
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
MESSAGE_POST_RESULT
private static class InternalHandler extends Handler {
@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;
}
}
}
L9 調用finish()方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
上面的finish方法根據Task的狀態調用不同的狀態方法,然後置位狀態。這裡看到了熟悉的onPostExecute()方法又一次回到了UI線程。我們注意到,在剛才InternalHandler的handleMessage()方法裡,還有一種MESSAGE_POST_PROGRESS的消息類型,這種消息是用於當前進度的,調用的正是onProgressUpdate()方法,那麼什麼時候才會發出這樣一條消息呢?相信你已經猜到了,查看publishProgress()方法的源碼,如下所示:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
最後我們還有一個構造器中的mFuture還沒有分析,因為只有一個done方法,所以有必要一起來看一下FutureTask中的done方法
/**
* Protected method invoked when this task transitions to state
* {@code isDone} (whether normally or via cancellation). The
* default implementation does nothing. Subclasses may override
* this method to invoke completion callbacks or perform
* bookkeeping. Note that you can query status inside the
* implementation of this method to determine whether this task
* has been cancelled.
*/
protected void done() { }
看注釋應該清楚,這裡需要子類去重寫,需要判斷狀態做出相應的處理即可,解析來看看AsyncTask構造器中的done方法
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);
}
}
};
主要的方法是postResultIfNotInvoked(get())然後把結果傳過去
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
如果task沒有被喚起的話,或者說沒有執行call()方法的話,wasTaskInvoked標志位為false,還是通過postResult方法將數據傳給UI線程,到此分析結束。
我們在開發Android的時候經常通過Adapter把數據和UI對象連接在一起,spinner、ListView之類的控件都可以用適配器來自定義其組建,使其更加豐富。適配
前言前面我們已經完整的講述了屬性動畫的實現,我們已經學會了怎麼實現動畫,如果沒有屬性我們也學會了怎麼添加屬性,還學習了用ValueAnimator來實現動畫。Evalua
小米手環APP更新之後出現了一個新功能,就是小米手環的接入微信得位置發生了變化,下面下載吧小編就為大家介紹一下小米手環如何接入微信,隨時查看自己今日活動是否
1.2.//使用回調接口,首先初始化pintuview並綁定,實現回調接口的方法 mPintuLayout = (PintuLayout) findViewById