基础一:ContextMenu,Option Menu和PopupMenu
基础二:为什么Button,ImageButton有焦点,textview,imageview没有焦点
基础三:一些重要方法的解释
基础四:Handler详解
基础五:不在Application类里面如何对全部activity生命周期监听
AsyncTask故名思议这是一个异步任务类,这个类在源码设计上运用了很多java特性,如泛型,带返回结果的线程,线程池等等,深入理解AsyncTask源码不仅对我们理解AsyncTask有帮助,也对我们学习java高级知识点有所帮助,这里涉及到的有 泛型,feturetask等
首先我来纠个错,在很多网页中或者开发书籍说AsyncTask类的实例需要在主线程当中,同时execute方法需要在主线程当中执行?这是真的吗?
答案是否; 首先我们从源码开始分析
在Android 20开始,ActivityThread的main方法变会调用AsyncTask的init方法去迫使创建主线程的Handler,最新Android 28虽然在ActivityThread的main方法中不再调用AsyncTask的init方法,同时AsyncTask也移除了init方法,但是在AsyncTask加载的时候,变会通过静态方法getMainHandler来完成创建主线程Handler;
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
不管Android 20还是Android 28,通过创建主线程的Handler来收发消息,这样AsyncTask的一些重写的方法变可以在主线程当中执行了
那为什么网络上会有那么多主线程创建AsyncTask实例的文章尼,细想下肯定是AsyncTask的某个重写方法不在主线程当中被回调,从而导致UI刷新crash,
为了保证版本的兼容性,我们还是在 主线程创建AsyncTask的实例吧
一个简单的继承自AsyncTask的类
private class MyAsnycTask extends AsyncTask<String, Object, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
@Override
protected void onProgressUpdate(Object... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled(String s) {
super.onCancelled(s);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
@Override
protected String doInBackground(String... strings) {
return null;
}
}
- AsyncTask是一个采用了泛型方式的抽象类:abstract class AsyncTask<Params, Progress, Result>,其中Params表示传递的参数的类型,Progress表示后台执行的进度,Result表示后台任务返回结果类型;
- onPreExecute方法在主线程当中执行,一般用于做一些准备工作,优先于doInBackground方法
- doInBackground方法在子线程中执行,子线程是运行在线程池中的
- onProgressUpdate方法运行在主线程中,这是因为它是通过创建的主线程Handler收发MESSAGE_POST_PROGRESS消息来执行onProgressUpdate,该方法作用是后台任务进度发生变化会被调用
- onPostExecute方法运行在主线程中,这是因为它是通过创建的主线程Handler收发MESSAGE_POST_RESULT消息来执行onPostExecute
- onCancelled()和onCancelled(Result result)都是在主线程当中执行,onCancelled(Result result)内部也是调用onCancelled()来完成的,onCancelled()方法会在客户端调用cancel(boolean mayInterruptIfRunning)或者doInBackground执行完毕之后被触发;
6.1 其中cancel(boolean)的作用是尝试取消一个正在执行的任务;当一个任务没有开始运行的时候,调用cancel是会成功的;可是如果一个任务已经开始了,那么则需要根据mayInterruptIfRunning来看执行此任务的线程是否应该被中断来试图阻止任务,其中如果mayInterruptIfRunning为true表示此任务应该被打断,false表示不被打断,将被运行执行完成
AsyncTask源码分析
- 构造AsyncTask
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
this((Looper) null);
}
注释要求我们在UI线程创建这个实例… …
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(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);
}
}
};
}
在构造AsyncTask的时候主要创建了三个属性
1)mHandler: 主线程Handler,用于发送消息给主线程
2)WorkerRunnable类型的mWorker:一个抽象的泛型类,实现了Callable接口,主要目的就是后台执行任务即调用doInBackground方法,并通过mHandler发送MESSAGE_POST_RESULT消息在UI线程完成onPostExecute操作,onPostExecute的参数即doInBackground操作返回的值
3)FutureTask类型的mFuture,因为WorkerRunnable实现了Callable接口,那必然需要一个FutureTask类来调用mWork做后台任务,完成后会调用FutureTask的done方法,done方法也很简单,他完成的是通过mHandler发送MESSAGE_POST_RESULT消息通知UI线程完成onPostExecute操作
- 执行AsyncTask
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
其中sDefaultExecutor为一个实现了Executor接口的串行线程池
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;
}
第15行,当一个任务开始调用execute方法之后,就会被标注为Status.RUNNING状态
第5行,当一个正在执行的任务,如果多次调用execute方法,就会抛出new IllegalStateException(“Cannot execute task:” + " the task is already running.");的异常
第17行,在主线程调用onPreExecute方法,完成一些初始化工作
第19行,将传递过来的参数paramsm赋值给mWorker.mParams,以方便mWorker完成后台任务
第20行,通过FutureTask的方式开始执行这个mWorker;mWorker完成之后会调用FutureTask的done方法
- AsyncTask更新进度操作
在后台执行任务doInBackground中,我们可以计算当前下载进度然后通过调用publishProgress方法完成进度更新
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
其通过UI线程的handler发送MESSAGE_POST_PROGRESS消息,
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;
}
}
使得onProgressUpdate方法被调用
那我们如何并行执行任务?
在AsyncTask内部其实还定义了一个线程池:threadPoolExecutor
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
我们利用这个线程池就能完成任务的并行操作,简单的代码如下
MyAsnycTask myAsnycTask = new MyAsnycTask();
myAsnycTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"hello");
这里虽然可以异步执行任务,但是因为他也是通过executeOnExecutor方法完成,所以他一样遵循如下原则
1):一个AsnycTask实例只能执行一次,如果多次执行会抛出IllegalStateException异常