編輯:關於Android編程
在Android開發中,我們進行異步處理一般會采用兩種方式:
1.Thread +Handler
通常我們在Thread裡面發送消息,然後在Handler的handleMessage方法裡面去處理對應的任務,因為Android是不允許UI線程去更新UI的,這個時候我們可以采取這種方式
2.AsyncTask
AsyncTask是Android為我們封裝的一個輕量級的異步處理框架,其實底層也是用了類似Thread+Handler的方式。對外提供了一些方法,我們實現這些方法就可以很方便的進行異步處理了。
既然兩種方式都可以實現異步處理任務,那麼有什麼區別呢?該使用哪個呢?
1.AsyncTask實現的原理和適用的優缺點
AsyncTask,是android提供的輕量級的異步類,可以直接繼承AsyncTask,在類中實現異步操作,並提供接口反饋當前異步執行的程度(可以通過接口實現UI進度更新),最後反饋執行的結果給UI主線程.
使用的優點:
簡單,快捷
過程可控
使用的缺點:
在使用多個異步操作和並需要進行Ui變更時,就變得復雜起來.
2.Handler異步實現的原理和適用的優缺點
在Handler 異步實現時,涉及到 Handler, Looper, Message,Thread四個對象,實現異步的流程是主線程啟動Thread(子線程)運行並生成Message-Looper獲取Message並 傳遞給HandlerHandler逐個獲取Looper中的Message,並進行UI變更。
使用的優點:
結構清晰,功能定義明確
對於多個後台任務時,簡單,清晰
使用的缺點:
在單個後台異步處理時,顯得代碼過多,結構過於復雜(相對性)
綜上所述:
數據簡單使用AsyncTask:實現代碼簡單,數據量多且復雜使用handler+thread :相比較
AsyncTask來說能更好的利用系統資源且高效 .AsyncTask其實是Android給開發者提供的
一個簡單輕量級的多線程類,通過它我們可以很容易新建一個線程做一些耗時的操作,並在
這個過程中更 新UI。之所以說它輕量級,是因為缺少了直接使用Thread的靈活性。如果是
很復雜的操作,還是建議通過Thread來操作,然後通過 Broadcast的方式來更新UI。
好了,看了兩種方式的區別,我們進入今天的重點,AsyncTask源碼的分析。
我們平時會這樣使用AsyncTask,
1.創建一個類繼承AyncTask(因為AsyncTask是抽象的)
class MyTask extends AsyncTask{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(Integer[] params) {
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}
這裡講解兩個地方:
(1)參數
我們看到AsyncTask有三個泛型,Params,Progress,Result,這裡分別為Integer,String,String,每個泛型代表的意思如下
Params:異步任務執行需要傳入的參數類型
Progress:異步任務執行過程中返回進度值的類型
Result:異步任務執行結束後返回結果的類型
(2)方法
這裡我們重寫了三個最常用的方法
onPreExecute:異步任務開始之前做一些初始化的動作,比如初始化進度條,AsyncTask實例創建在哪個線程,這個方法執行在哪個線程
doInBackground:執行後台任務的方法,運行在子線程
onPostExecute:異步任務結束後調用的方法,運行在主線程,通常用來處理結果。
2.執行任務
調用如下方法使異步任務開始執行
//創建AsyncTask對象,調用execute方法
new MyTask().execute();
public abstract class AsyncTask {
private static final String LOG_TAG = "AsyncTask";
//獲得當前CPU的核心數
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//當前線程池容量 = CPU核心數+1
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//最大線程池容量 = CPU核心數*2+1
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//當前線程數量大於核心線程數量時,空閒線程在執行任務前等待的最大時間,超過此時間如果還沒有任務則會被終止
private static final int KEEP_ALIVE = 1;
//創建新線程需要的線程工廠,需要作為參數傳遞到ThreadPoolExecutor的構造函數中
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
//使用Runnable對象創建線程對象並指定名稱
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());//AsyncTask #1
}
};
//用來保存任務的工作隊列,需要作為參數傳遞到ThreadPoolExecutor的構造函數中,此處采用阻塞隊列,遵循FIFO,容量為128,也就是說最多存儲128個任務
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);
//順序執行任務的Executor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//發送結果的消息
private static final int MESSAGE_POST_RESULT = 0x1;
//發送進度的消息
private static final int MESSAGE_POST_PROGRESS = 0x2;
//默認使用順序執行器
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//用來發送消息的InternalHandler
private static InternalHandler sHandler;
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();
//串行任務執行器的實現,取出一個個任務交給上面的線程執行器執行
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);
}
}
}
//當前任務的狀態
public enum Status {
PENDING,//掛起,等待執行
RUNNING,//正在運行
FINISHED,//執行結束
}
//獲得InternalHandler對象
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
/** 隱藏方法*/
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
//創建WorkerRunnable對象,繼承自Callable,重寫call方法
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
//設置線程優先級Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//調用doInBackground,傳入我們的輸入參數
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
//調用postResult方法,傳入doInBackground的結果
return postResult(result);
}
};
mFuture = new FutureTask(mWorker) {
@Override
//任務執行完畢調用此方法
protected void done() {
try {
//如果沒有傳遞任務則進行傳遞,get方法為獲取FutureTask執行完畢的結果
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);
}
}
};
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {//如果沒有傳遞,傳遞結果
postResult(result);
}
}
.........
接下來我們看看比較重要的一個方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
這個方法是干啥的呢?將doInBackground處理的結果封裝成一個AsyncTaskResult對象發送給自定義的Handle對象進行處理,我們可以看到這裡有一個getHandler方法
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
采用了同步鎖的方法來獲取InternalHandler對象,這個Handler對象就是用來處理消息的,我們待會分析,獲得Handler對象後,我們發送了一個msg.obj為如下的消息
new AsyncTaskResult(this, result)
我們去看看這個類
private static class AsyncTaskResult {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
這個類很簡單,有兩個成員變量,代表當前任務的AsyncTask對象,這裡為this,然後就是數據Data,這裡我們傳遞過來的是doInBackground的處理結果。既然消息發送了,我們看看是如何處理的吧
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;
}
}
}
上面先獲取到了發送過來的消息,然後根據消息的類型進行了判斷,進行了不同的處理,如果是投遞結果的消息,執行下面的操作
result.mTask.finish(result.mData[0]);
result.mTask就是當前的任務,然後調用了finish方法,我們去看看
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
很簡單,首先判斷當前任務是否取消了,如果取消了,就調用onCancelled方法,定義如下
protected void onCancelled(Result result) {
onCancelled();
}
當任務取消的時候會回調onCancelled方法,所以如果我們想要在任務取消的時候做一些操作,可以重寫這個方法。
如果沒有取消,則執行onPostExecute方法,傳入我們的結果,最後將當前任務狀態設置為完成,這裡調用了onPostExecute方法,
至此,我們已經看到了doInbackground和onPostExecute方法的調用之處,那麼還剩下一個onPreExcute方法怎麼沒看到啊?因為我們到目前為止只分析了創建任務實例這個過程所發生的事,任務還沒執行執行呢。
執行任務我們會調用execute方法,如果必要的話會傳入參數
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
這裡又調用了executeOnExecutor方法,傳入了sDefaultExecutor和我們的參數,大家還記得sDefaultExecutor是什麼嗎?懶得向上找了,再給大家貼出來吧
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
就是串行執行器,我們去看看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;
}
首先判斷當前狀態,前提是不是掛起狀態,如果正在執行或者已經執行完畢,我們再次調用execute方法就會拋出異常,接下來將當前狀態標記為正在執行,然後調用onPreExecute方法,這裡我們終於看到onPreExecute方法被調用了,然後將我們執行任務時傳入的參數賦值給mWorker的mParams,這個mWorker是啥,之前說過了,
private final WorkerRunnable mWorker;
最後調用sDefaultExecutor 的execute執行任務,並且傳入我們的FutureTask對象。至此,整個過程就完了。
接下來講一些細節性的東西,可能大家也發現有些地方還沒有分析
1.SerialExecutor是如何保證一個任務執行完畢才執行下一個的,我再次把代碼貼出來,給大家分析分析
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);
}
}
}
首先創建了一個ArrayDeque對象,用來接收我們的消息,然後調用了此對象的offer方法,再裡面調用了execute參數中Runnable對象的run方法,那麼這個Runnable對象是啥啊,如果你仔細閱讀了上面的內容,就會發現這個Runnable對象就是FutureTask,為什麼FutureTask能轉換為Runnable呢,
public class FutureTask implements RunnableFuture
public interface RunnableFuture extends Runnable, Future {
明白了吧,廢話我就不說了,
那麼我們就去看看FutureTask的run方法
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
.....
只講重點,這裡將一個callable對象賦值給了一個新的對象,然後調用了call方法,我們看看這個callable對象是在哪裡賦值的
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
是在FutureTask構造方法裡面復制的,到這大家是不是想起什麼了,是的,我們再AsyncTask中創建鍋Callable的實現類WorkerRunnable並且將其傳給了FutureTask的構造函數,所以,當我們最終調用sDefaultExecutor 的execute方法的時候,就執行了WorkerRunnable中的call方法,call中又調用doInbackground,然後postResult發送消息,又回到了上面的分析。
run執行完畢之後,會判斷mActive是否為空,第一次肯定為空,執行scheduleNext方法
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
調用了mTasks的poll方法從當前隊列頭部取出消息,給線程執行器執行,以後mActive就不為null,我們可以看到scheduleNext放到了finally方法裡面,保證了每次都會執行。
好了,AsyncTask的源碼就分析到這裡了,已經快吐血了,費了不少時間,腦子現在還是有點亂,最後大概總結一下下面這句話都干了啥
new MyTask().execute();
1.創建對象(new MyTask())
創建WorkerRunnable對象(Callable),重寫call方法—->將WorkerRunnable傳遞給FutureTask
2.執行任務(execute())
execute(Params… params)—->executeOnExecutor(Executor exec,
Params… params) —->調用onPreExecute—->調用SerialExecutor的execute方法,傳入上面的FutureTask對象,請注意,這個FutureTask包裝了WorkerRunnable對象,可不簡單哦—->調用FutureTask的run方法(run方法裡面調用了call)—->調用WorkerRunnable的call方法—->調用scheduleNext執行下個任務
最後說一下AsyncTask版本上的差異
在1.6之前,AsyncTask是串行執行任務的,1.6的時候AsyncTask開始采用線程池裡處理並行任務,但是從3.0開始,為了避免AsyncTask所帶來的並發錯誤,AsyncTask又采用一個線程來串行執行任務
那麼我們現在有沒有辦法讓AsyncTask並行執行呢》有
在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()
SharePoint Content Types working with Word這個文檔是按照SharePoint 2010 Development with Vis
美團電商應用平台大家使用非常頻繁,下面小編通過本文給大家介紹電商應用平台中常用的選擇類別下拉列表的實現。先給大家展示下效果圖:一、下拉列表的實現其實實現方法有很多,這時實
現在的Android應用,只要有一個什麼新的創意,過不了多久,幾乎所有的應用都帶這個創意。這不,咱們公司最近的一個持續性的項目,想在首頁加個從左滑動出來的菜單,我查閱網上
大家好,這一節給大家分享的是Android中幾種圖像特效處理的小技巧,比如圓角,倒影,還有就是圖片縮放,Drawable轉化為Bitmap,Bitmap轉化為Drawab