編輯:關於Android編程
Android的UI是線程不安全的,想在子線程中更新UI就必須使用Android的異步操作機制,直接在主線程中更新UI會導致程序崩潰。
Android的異步操作主要有兩種,AsyncTask和Handler。AsyncTask是一個輕量的異步類,簡單、可控。本文主要結合API 23的源碼講解一下AsyncTask到底是什麼。
聲明:Android不同API版本中同一個類的實現方法可能會有不同,本文是基於最新的API 23的源碼進行講解的。
public abstract class AsyncTask
Params:執行時傳入的參數
Progress:後台任務的執行進度
Result:返回值
AsyncTask是個抽象類,所以需要自己定義一個類繼承他,比如
class MyAsyncTask extends AsyncTask
新建一個Activity,一個Button和一個ProgressBar,點擊Button啟動一個AsyncTask並實時更新ProgressBar的狀態。
class MyAsyncTask extends AsyncTask {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressBar.setProgress(values[0]);
}
@Override
protected Boolean doInBackground(Void... params) {
for (int i = 0; i < 100; i++) {
//調用publishProgress,觸發onProgressUpdate方法
publishProgress(i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
@Override
protected void onCancelled() {
super.onCancelled();
progressBar.setProgress(0);
}
}
startAsyncBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
}
});
通過上面的例子可以發現,AsyncTask使用起來很簡單,很方便的就可以在主線程中新建一個子線程進行UI的更新等操作。但是他的實現並不像使用起來那麼簡單,下面就是對AsyncTask的源碼進行剖析。
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
在構造函數中只做了兩件事,初始化mWorker和mFuture兩個變量。mWorker是一個Callable對象,mFutre是一個FutureTask對象。execute()時會用到。
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
只有一行代碼,調用了executeOnExecutor方法,sDefaultExecutor實際上是一個串行的線程池,一個進程中所有的AsyncTask全部在這個串行的線程池中排隊執行。executeOnExecutor源碼如下。
public final AsyncTask 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;
}
可以看到在這個方法裡調用了onPreExecute(),接下來執行exec.execute(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();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
系統先把AsyncTask的Params參數封裝為FutureTask對象,FutureTask是一個並發類,這裡它相當於Runnable;接著將FutureTask交給SerialExecutor的execute方法,它先把FutureTask插入到任務隊列tasks中,如果這個時候沒有正在活動的AsyncTask任務,那麼就會執行下一個AsyncTask任務,同時當一個AsyncTask任務執行完畢之後,AsyncTask會繼續執行其他任務直到所有任務都被執行為止。從這裡就可以看出,默認情況下,AsyncTask是串行執行的
看一下AsyncTask的構造函數,mFuture構造時是把mWork作為參數傳進去的,mFuture的run方法會調用mWork的call()方法,因此call()最終會在線程池中執行。call()中調用了doInBackground()並把返回結果給了postResult。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
可以看到在postResult中通過getHandler()獲得一個Handler。
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
查看getHandler源碼,可以發現getHandler返回的是一個InternalHandler,再來看看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;
}
}
}
看到這裡已經豁然開朗了,InternalHandler是一個繼承Handler的類,在他的handleMessage()方法中對msg進行了判斷,如果是MESSAGE_POST_RESULT就執行finish(),如果是MESSAGE_POST_PROGRESS,就執行onProgressUpdate()。
MESSAGE_POST_PROGRESS消息是在publishProgress裡發出的,詳情見源碼。
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
綜述 在Android系統中,出於對性能優化的考慮,對於Android的UI操作並不是線程安全的。也就是說若是有多個線程來操作UI組件,就會有可能導致線程安全問題。所以
先看 SwipeLayout的效果圖圖太多了,我這只上傳一張,想看 listview和GridView效果的,和想看源碼的 —> GitHub怎麼實現後
安卓手機QQ自帶截圖功能,iPad、iPhone則需要使用iOS系統自帶的截圖方法或者第三方APP。下面就隨小編分別來看看,安卓手機QQ和iOS系統的ipa
前言本文在Ubuntu 64位系統上對qemu項目進行交叉編譯,並且只編譯與qemu user mode有關的代碼。下文中的”NDK”若無特殊說明