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

android源码解析(5)--AsyncTask源码分析以及使用中的坑

2017-03-19 22:28 696 查看
废话少说,今天来分析AsyncTask的源码,看了网上很多人都说使用AsyncTask有最大线程的约束,但是呢,自己写了一个测试代码,发现同时扔进去一万多个线程也没有问题啊,于是对自己充满了否定,是不是我用错了?然后给同事打电话一问,原来就是没有那个问题了,我去,坑了半重点内容天原来网上分析的都有问题(应该是源码更新的缘故),还是撸开袖子自己分析源码吧!

第一.怎么使用的
首先我们先看看谷歌给的例子,很简单(源码中就有)**


private class DownloadFilesTask extends AsyncTask<URL, Integer, Long>{
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}

protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}

protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}

使用:new DownloadFilesTask().execute(url1, url2, url3);


第二.使用很简单,看看源码吧
首先我们先分析一下都有什么属性,以及作用:
1)第一个线程池构造


//获取cup数目
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;
//制造线程的工厂
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());
}
};
//线程队列,应该是大家看到这里就都认为是最多128个线程了
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);

/**
* 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);


2)第二个线程池登场了:


/**
* An {@link Executor} that executes tasks one at a time in serial
* order.  This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//默认线程池,也就是说我们添加的所有线程默认是添加到这里的
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

//看看这个线程池里有什么工作吧
private static class SerialExecutor implements Executor {
//一个类似于集合的队列(具体是什么没深究)
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;

//他的执行方法,下面会调用的
public synchronized void execute(final Runnable r) {
//这个方法是说把一个runnable对象添加到队列的最后面
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
//第一次执行肯定是空了,以后就不会执行了,而是在上面的finally中执行
if (mActive == null) {
scheduleNext();
}
}

//操作执行下一个
protected synchronized void scheduleNext() {
//这个方法是说获取队列中最开始的runnable
if ((mActive = mTasks.poll()) != null) {
//重量级登场了,另一个线程池执行任务去了,所以真正执行任务是这个另一个线程池
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}


3)两个线程池分析完了,再看看handler


//handler,不用多说吧
private static InternalHandler sHandler;
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:
// 处理完的结果
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//发布进度
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}

@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
//这里就是下面结束任务是调用finish的mTask
final AsyncTask mTask;
final Data[] mData;

AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
//这里有个判断,其实就再说明一件事情,就是我们设置取消,不一定会马上取消(线程就这么设计的),所以还需要标志来标识是否取消了
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}


4)下面就开始我们从构造器开始看吧:


//当前的工作
private final WorkerRunnable<Params, Result> mWorker;
//对任务的二次封装
private final FutureTask<Result> mFuture;

/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
* 这里说创建一个AsyncTask必须要在主线程中,对吗?分线程不可以吗?经过本人测试是可以的其实,至于为
* 什么可以,我查阅了一下网上说的为什么必须要在主线程,他们说其实主要是因为handler必须在主线程初始
* 化,但是我们返回去看看这里handler是怎么创建的,
* public InternalHandler() {
*      super(Looper.getMainLooper());
* }
* 我们可以看到,这里本来就是得到的主线程的Looper,也就是说不论你在哪个线程创建都会在主线程处理结果
* 至于为什么,那就要分析handler源码了
*/
public AsyncTask() {
//这就是一个具体的工作,传进去参数,返回来结果
mWorker = new WorkerRunnable<Params, Result>() {
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<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,构造器创建了一个要执行的任务,下面就开始分析execute方法:


@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
//这里说明一下:虽然注解注明必须要在主线程中执行,但是经过测试在子线程也可以的,不过onPeExecute
//方法就不是在主线程中执行了,而是在当前线程执行,onPostExecute方法仍然在主线程
@MainThread
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;
}


好了,任务放到第二个线程池里了,然后我们可以看看上面第二个线程池干了什么工作?将任务放到队列里最后面
,然后开始从头取出任务交给第一个线程池去工作,是不是顺起来了。那么第一个线程池执行的Runnable又是什
么呢?可以看到其实就是我们构造器中创建的那个mWorker任务,然后再去构造器中看看WorkerRunnable
里面做了什么:


mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
//设置优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//开始执行这个方法,具体什么操作交由我们自己来实现
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
//执行完了还是向主线程发送结果
return postResult(result);
}
};
//向主线程发送结果
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}


然后又回到上面handler里面处理完成的工作了:


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:
// 到这里处理完成的结果,result.mTask其实就是当前AsyncTask,看上面
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}


继续:


private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
//执行结束
onPostExecute(result);
}
mStatus = Status.FINISHED;
}


好了,至此整个执行过程全部完整分析结束了。下面开始说说都有什么坑:

第三:使用中的一些坑
1)就是面试的时候经常会问什么128个限制啊什么的,经过分析默认情况下使用是没有这个限制的,记住是默认,
但是下面这种用法就会有:


mMyAsy.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);


其实这就是说我们自己传入了一个线程池,替代了默认的那个线程池,为什么会有限制呢?源码分析分析:


@MainThread
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;
}


2)其实这个坑也不能说是AsyncTask中的坑,就是线程同步问题。我们做相机开发的时候由于bitmap占用大量内
存,所以时刻需要主动去回收图片,但是处理图片过程还必须放在异步中执行,所以就会出现一个线程已经回收
了图片了,另一个还在使用中...挂了。解决方法,个人是说两个线程各持有一把锁,如果你想要回收图片,就
去检查对方是否释放了锁(也就是处理完了),如果处理完了,则可以回收销毁,否则我们就把我们自己所持有的
锁释放掉,这样另一个线程执行完了检查我们的锁之后就可以回收了。
3)源码分析的时候说了,AsyncTask不是只能在主线程中使用,分线程也可以,但是onPreExecute()方法会
在同一个线程中执行,只有onPostExecute方法会在主线程中执行,原因就是


private static class InternalHandler extends Handler {
public InternalHandler() {
//它默认就去调用主线程的Looper了,所以肯定会到主线程执行最后的结果
super(Looper.getMainLooper());
}
//省略...
}


不过好像看谷歌的英文注释,确实是应该在主线程中初始化才可以,不过谷歌本来就是用来否定的...

4)就是我们调用cancle()方法不一定会立即执行取消操作,因为这是线程的一个机制,所以会造成什么结果呢,
就是加入说我打开了一个页面,启动asynctask联网,这时候呢,我手机屏幕旋转了,页面销毁掉重新创建了
然后再一次联网去了,按说页面销毁的时候我们会执行取消操作,但是SB不一定马上执行啊,结果就是第一次联
网成功了刷新页面,页面早就已经重建了,肯定就Crash了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: