您的位置:首页 > 移动开发 > Android开发

【Android源码解析】从源码角度深入分析AsyncTask原理

2018-03-20 00:14 1006 查看

一. 什么是AsyncTask?

是Android提供的轻量级的异步类,封装了线程池和handler。它是个抽象类,使用时需要去继承它,然后去实现异步操作,并反馈异步执行的结果

二. AsyncTask使用方法

首先先来看个例子,这是之前写过的一个gif图片下载的异步任务。使用AsyncTask要注意它的三个参数五个方法

1. 三个参数

第一个,代表执行AsyncTask传入的参数;

第二个,代表执行后台任务的进度;

第三个,代表执行完任务返回的结果。

2. 五个方法

(1)onPreExecute:在ui线程调用,后台操作执行前调用;经常用于显示一个进度条。

(2)doInBackground:做的是耗时操作,并返回执行结果。同时,可以在doInBackground中调用publishProgress来发布进度单位。

(3)onPostExecute:接受执行结果。

(4)onProgressUpdate:当publishProgress调用完该方法被执行,一般用于控制进度条的进度。

public class GifImageDownLoader extends AsyncTask<String, Void, File> {
private static final String foldPath = Environment.getExternalStorageDirectory() + "/CTRIP/cache/";
private ICallBack<File, String> mCallBack;

public GifImageDownLoader(ICallBack<File, String> callBack) {
this.mCallBack = callBack;
}

@Override
protected File doInBackground(String... params) {
String[] url = params[0].split("/");
String gifName = url[url.length - 1];
File f = new File(foldPath + gifName);
if (!f.exists()) {
f = downLoadGifImage(params[0], gifName);
}
return f;
}

@Override
protected void onPostExecute(File f) {
if (isCancelled() || f == null) {
mCallBack.fail(null);
return;
}
mCallBack.success(f, null);
}

private File downLoadGifImage(String gifUrl, String gifName) {
File gifImageFile = null;
InputStream is = null;
FileOutputStream fos = null;
try {
URL downLoadUrl = new URL(gifUrl);
HttpURLConnection conn = (HttpURLConnection) downLoadUrl.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (conn.getResponseCode() == 200) {
File fold = new File(Environment.getExternalStorageDirectory() + "/CTRIP/cache");
if (!fold.exists()) {
fold.mkdirs();
}
is = conn.getInputStream();
gifImageFile = new File(foldPath, gifName);
fos = new FileOutputStream(gifImageFile);
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return gifImageFile;
}

}


三. AsyncTask内部原理

Android主线程肩负绘制UI界面和即使响应用户操作的重任,所以为了避免用户点击按钮没有反应,应该把耗时操作放在子线程进行。

1. AsyncTask本质是一个静态的线程池,其子类实现的异步任务都提交到静态线程池中进行。

2. 线程池的工作线程执行doInBackground(mParams)方法执行异步任务。

3. 当任务状态改变后,子线程向UI线程发送消息,AsyncTask内部的handler相应这些消息,并调用相关回调函数。

四. 线程池ThreadPoolExecutor

创建线程池一般不直接new一个线程池对象,因为需要配置一堆东西,我们来看下其构造方法:



所以官方也不推荐我们这样创建线程池,而是推荐使用Executors的工厂方法来创建线程池,Executors是官方提供的一个工厂类,它里面封装了众多功能的线程池,从而使得我们创建线程池非常简便,主要提供了五种线程池:

1. newFixedThreadPool()

创建一个固定线程数量的线程池,该线程池中的线程数量都不变,即不会创建新的线程,也不会销毁已经创建好的线程,自始至终都是那几个固定的线程工作。

如果有空闲线程则处理任务,如果没有则将任务放到一个队列中,一旦线程空闲了则按FIFO的方式处理队列中的任务。

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}


2. newCachedThreadPool()

该线程池中的线程数量不固定,可根据实际情况调整。

如果有新任务提交,当没有空闲线程则创建新线程,如果有空闲线程则复用该空闲线程,当空闲线程空闲时间超过配置的等待时间则被销毁。

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}


3. newSingleThreadPool()

只有一个线程的线程池。

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}


4. newScheduledThreadPool()

可定时执行某任务的线程池。

public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}


5. newSingleThreadScheduledPool()

只有一个线程的可定时执行某任务的线程池。

可以看到工厂方法最终都是通过ThreadPoolExecutor来创建线程池。在它的构造方法的参数中,最重要的是BlockingQueue任务队列,不同线程池的任务队列实现方法肯定不同。

而线程池的使用也很简单,当创建好线程池后,只需要调用其execute方法即可。

AsyncTask源码解析

AsyncTask的源码其实非常短,只有700多行。我们先来看AsyncTask的构造方法。

AsyncTask构造方法

public AsyncTask() {
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);
}
}
};
}


workRunnable是一个Callable,大家可能对Callable不熟悉,它其实跟Runnable使用非常像,只不过Callable有返回值而Runnable没有。

FutureTask是Callable的包装类。

execute方法

execute方法其实执行的是executeOnExecutor方法。

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
}


可以看到executeOnExecutor传入的第一个参数是sDefaultExecutor,这是一个线程池。我们稍后来分析这个线程池,我们先来看下executeOnExecutor里都做了什么。

1. 首先是判断AsyncTask当前的执行状态,只有当前状态是Status.PENDING才可继续使用;

2. 接着设置当前状态为RUNNING状态。

3. 然后调用onPreExecute方法。

4. 接着为mWorker设置参数,并执行该任务。

AsyncTask中线程池SerialExecutor

private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
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);
}
}
}


可以看到SerialExecutor就是将异步任务一个一个放入队列里,然后再去执行。不过还需要注意的是execute方法加上了synchronized修饰,从而保证AsyncTask中的任务是串行执行的

五. 内部handler

当任务执行完通过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;
}
}
}


六. 最后

以上就是我对AsyncTask源码的分析,后续当我有新的理解时还会继续更新。至于为什么想到要去看AsyncTask的源码了呢?主要是因为最近想要改进一下我们的缓存机制,希望能在合适的时机清理一下缓存,我们用AsyncTask比较多,也知道它是封装了线程池和handler,但具体怎么实现的还真没有系统地读过。

好了,时间不早了,现在是夜里十二点十五,大家晚安。如有任何疑问可以邮件联系我zhshan@ctrip.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: