Android中AsyncTask分析--你所不注意的坑
AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.
本文不分析AsyncTask的使用,它的使用教程网上一搜一大堆,本文主要分析它的内部逻辑和实现,它是怎么实现异步的,它是怎么处理多个任务的,是并发么??
一、线程任务的调度
在AsyncTask内部会创建一个类相关的线程池来管理要运行的任务,也就就是说当你调用了AsyncTask的execute()后,AsyncTask会把任务交给线程池,由线程池来管理创建Thread和运行Therad。
在Android4.0版本中它内部是有两个线程池:SerialExecutor和ThreadPoolExecutor,SerialExecutor是串行的,ThreadPoolExecutor是并发的,而默认的就是SerialExecutor的,所以你一个程序中如果用了好几个AsyncTask你就得注意了:不要忘了换成并发的线程池执行。下面演示一下,穿行的调度
public class AsyncTaskDemoActivity extends Activity {
private static int ID = 0;
private static final int TASK_COUNT = 9;
private static ExecutorService SINGLE_TASK_EXECUTOR;
private static ExecutorService LIMITED_TASK_EXECUTOR;
private static ExecutorService FULL_TASK_EXECUTOR; static {
SINGLE_TASK_EXECUTOR = (ExecutorService) Executors.newSingleThreadExecutor();
LIMITED_TASK_EXECUTOR = (ExecutorService) Executors.newFixedThreadPool(7);
FULL_TASK_EXECUTOR = (ExecutorService) Executors.newCachedThreadPool();
}; @Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.asynctask_demo_activity);
String title = "AsyncTask of API " + VERSION.SDK_INT;
setTitle(title);
final ListView taskList = (ListView) findViewById(R.id.task_list);
taskList.setAdapter(new AsyncTaskAdapter(getApplication(), TASK_COUNT));
} private class AsyncTaskAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mFactory;
private int mTaskCount;
List<SimpleAsyncTask> mTaskList; public AsyncTaskAdapter(Context context, int taskCount) {
mContext = context;
mFactory = LayoutInflater.from(mContext);
mTaskCount = taskCount;
mTaskList = new ArrayList<SimpleAsyncTask>(taskCount);
} @Override
public int getCount() {
return mTaskCount;
} @Override
public Object getItem(int position) {
return mTaskList.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mFactory.inflate(R.layout.asynctask_demo_item, null);
SimpleAsyncTask task = new SimpleAsyncTask((TaskItem) convertView);
/*
* It only supports five tasks at most. More tasks will be scheduled only after
* first five finish. In all, the pool size of AsyncTask is 5, at any time it only
* has 5 threads running.
*/
task.execute();
// use AsyncTask#SERIAL_EXECUTOR is the same to #execute();
// task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
// use AsyncTask#THREAD_POOL_EXECUTOR is the same to older version #execute() (less than API 11)
// but different from newer version of #execute()
// task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// one by one, same to newer version of #execute()
// task.executeOnExecutor(SINGLE_TASK_EXECUTOR);
// execute tasks at some limit which can be customized
// task.executeOnExecutor(LIMITED_TASK_EXECUTOR);
// no limit to thread pool size, all tasks run simultaneously
//task.executeOnExecutor(FULL_TASK_EXECUTOR); mTaskList.add(task);
}
return convertView;
}
} private class SimpleAsyncTask extends AsyncTask<Void, Integer, Void> {
private TaskItem mTaskItem;
private String mName; public SimpleAsyncTask(TaskItem item) {
mTaskItem = item;
mName = "Task #" + String.valueOf(++ID);
} @Override
protected Void doInBackground(Void... params) {
int prog = 1;
while (prog < 101) {
SystemClock.sleep(100);
publishProgress(prog);
prog++;
}
return null;
} @Override
protected void onPostExecute(Void result) {
} @Override
protected void onPreExecute() {
mTaskItem.setTitle(mName);
} @Override
protected void onProgressUpdate(Integer... values) {
mTaskItem.setProgress(values[0]);
}
}
} class TaskItem extends LinearLayout {
private TextView mTitle;
private ProgressBar mProgress; public TaskItem(Context context, AttributeSet attrs) {
super(context, attrs);
} public TaskItem(Context context) {
super(context);
} public void setTitle(String title) {
if (mTitle == null) {
mTitle = (TextView) findViewById(R.id.task_name);
}
mTitle.setText(title);
} public void setProgress(int prog) {
if (mProgress == null) {
mProgress = (ProgressBar) findViewById(R.id.task_progress);
}
mProgress.setProgress(prog);
}
}
2.你想要的并发执行
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
当然也可以换成你自己的线程池。
二、源码分析
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());
}
};
//存放任务的阻塞队列
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(10); /**
* 可以平行的执行任务!就是并发的
* 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); /**
* 线性执行的执行器
* 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();
//内部交互的handler
private static final InternalHandler sHandler = new InternalHandler();
//默认的Executor
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
定义了需要用到的成员,可以根据名字就能知道干什么的,另外注意都是static修饰的:
第二行的sThreadFactory是创建线程的;
第十行的sPoolWorkQueue阻塞队列,存放任务的;
第十七行是 THREAD_POOL_EXECUTOR是线程池,这个是并发执行的线程池;
第26行是线性调度的线程池,SERIAL_EXECUTOR,执行完一个才会执行下一个;
第28行是一个内部封装的Handler:InternalHandler
第30行可以看出他默认的是线性调度的线程池, Executor sDefaultExecutor = SERIAL_EXECUTOR,看到这里你应该注意一个问题,如果程序里有好多个AsyncTask,它们就是线性调度的,这肯定可你预想的不一样,所以你别忘了换成并发的执行器。
2.内部Handler的使用:
2.1 自定义的InternalHandler(内部handler)
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
//接受message的处理,可以看到根据状态选择是完成了,还是更新着
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;
}
}
}
在上边handleMessage中,根据msg进行判断,是完成了还是在更新;
任务完成调用finish方法,在其中执行你定义的onPostExecute方法,
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
3.构造方法
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return postResult(doInBackground(mParams));
}
}; mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
final Result result = get(); postResultIfNotInvoked(result);
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
}
};
}
构造方法中其实隐藏的信息很多,WorkerRunnable和FutureTask;
其中WorkerRunnable继承了Callable接口,应该是用于在未来某个线程的回调接口,在其中执行了postResult(doInBackground(mParams));调用doInBackground,并用postResult方法把result发送到主线程。
FutureTask你看类的介绍是说控制任务的,控制任务的开始、取消等等,在这不细究,跟本文关系不大,而且我也没看明白。
第17行有一个方法:postResultIfNotInvoked(result);根据名字可以看出来是如果没有调用就把结果post出去,所以他应该是处理取消的任务的。
看下postResult方法:代码很少也很简单,就是把msg发送给handler:
//用shandler把设置message,并发送。
private Result postResult(Result result) {
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
构造方法就分析到这,下一步就是execute():
3.1 按照执行过程流程,实例化完,就可以调用execute():
//Params... 就相当于一个数组,是传给doInBackground的参数
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;
}
代码逻辑很清晰,没有几行:
20行:修改了状态;
21行:准备工作;
24行:设置参数;
25行:线程池调用执行,注意参数是mFuture。
3.2 execute的执行逻辑
就以它定义SerialExecutor为例:
/*An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.*/
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);
}
}
}
可以看到它的方法都是用synchronized关键字修饰的,且在他的介绍里也说了在同一时间只有一个任务在执行。
- 在execute方法中把Future 的 run方法封装到Runnable里,放到Task队列中,如果mActive为空则调用scheduleNext方法执行任务;在这里还有一个细节就是执行完r.run(),还有个finally模块调用scheduleNext 方法,所以它才会一个接一个的执行任务。
- 而在scheduleNext 中使用THREAD_POOL_EXECUTOR 执行任务。
三、AsyncTask中异步的处理逻辑
没有忘了前边构造方法中的postResult(doInBackground(mParams))和postResultIfNotInvoked(result);方法吧,如果忘了翻前边去看。这两个方法把执行成功的和失败的任务都包含了。
所以我们可以设想一下它是怎么执行的:
1.在executeOnExecutor方法中给变量赋值
2.用执行器Executor另起线程执行任务
3.在Executor中一些复杂的逻辑,用FutureTask进行判断任务是否被取消,如果没有就调用回调接口call()方法,
4.在call()方法中调用了postResult(doInBackground(mParams));
5.postResult发送给主线程的Handler进行后续处理。
看的时候可以画下图,会很清晰,基本逻辑就这样,好AsyncTask就分析到这,欢迎纠错。。。
转发请注明出处,原文地址:http://www.cnblogs.com/jycboy/p/asynctask_1.html
Android中AsyncTask分析--你所不注意的坑的更多相关文章
- Android中AsyncTask异步
今天我们学习了 AsyncTack, 这是一个异步任务. 那么这个异步任务可以干什么呢? 因为只有UI线程,即主线程可以对控件进行更新操作.好处是保证UI稳定性,避免多线程对UI同时操作. 同时要把耗 ...
- Android中AsyncTask的使用 (包含文件的下载与存储)
今天看到大神写的相关详解Android中AsyncTask的使用,真的很是佩服,下面我将学习到的AsynTask知识运用到项目中,其中也涉及一些文件的下载与存储到本地 啥都不说了,直接上代码,我将对其 ...
- Android中AsyncTask使用具体解释
在Android中我们能够通过Thread+Handler实现多线程通信.一种经典的使用场景是:在新线程中进行耗时操作.当任务完毕后通过Handler向主线程发送Message.这样主线程的Handl ...
- 详解Android中AsyncTask的使用
在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更 ...
- 具体解释Android中AsyncTask的使用
在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式须要为每个任务创建一个新的线程,任务完毕后通过Handler实例向UI线程发送消息,完毕界面的更新 ...
- Android中AsyncTask的使用
原文 https://blog.csdn.net/liuhe688/article/details/6532519 在Android中实现异步任务机制有两种方式,Handler和AsyncTask. ...
- Android中AsyncTask的简单用法 .
在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2. 确保只 ...
- android中asynctask的使用实例
参考此blog写的非常的好http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html MainActivity.java imp ...
- Android笔记(三十五) Android中AsyncTask
AsyncTask<Params,Progress,Result> 是一个抽象类,通常继承这个抽象类需要指定如下几个泛型参数: 1. Params :启动任务时出入参数的类型 2. P ...
随机推荐
- centos6.5 下搭建lamp环境
一直以为很麻烦,实际操作起来并没有遇到什么问题,yum源是163,php版本是5.3(有点老,以后再更新吧):详细步骤如下: 第一步:安装php mysql apache yum -y install ...
- Android studio每次启动卡在fetching Android sdk compoment information的解决方案
网上有人给出了方案:1)进入刚安装的Android Studio目录下的bin目录.找到idea.properties文件,用文本编辑器打开.2)在idea.properties文件末尾添加一行: d ...
- 深入理解DOM节点类型第七篇——文档节点DOCUMENT
× 目录 [1]特征 [2]快捷访问 [3]文档写入 前面的话 文档节点document,隶属于表示浏览器的window对象,它表示网页页面,又被称为根节点.本文将详细介绍文档节点document的内 ...
- 外网访问原理分析 - 每天5分钟玩转 OpenStack(105)
本节我们会将上节创建的 ext_net 连接到 router,并验证内外网的连通性. 更重要的,我们会分析隐藏在表象之下的原理. 将外网连接到 Neutron 的虚拟路由器,这样 instance 才 ...
- C# 对象锁——Monitor
Monitor里边有一些static方法,可以用于在对象上获取同步锁,来进行一些进程同步控制操作 用法及注意点如下: using System; using System.Collections.Ge ...
- UCOSII内核代码分析
1 UCOSII定义的关键数据结构 OS_EXT INT8U OSIntNesting; OSIntNesting用于判断当前系统是否正处于中断处理例程中. OS_EXT ...
- 【记录】AutoMapper Project To not support ResolveUsing
示例代码: public List<OrderLineDTO> GetLinesForOrder(int orderId) { Mapper.CreateMap<OrderLine, ...
- html标签属性(attribute)和dom元素的属性(property)
简介 attribute和property都有属性之意,但对于attribute和property的区分其实并不难.从对象来说,attribute是html文档上标签属性, 而property则是对应 ...
- window.open
window.open 的三个参数 第一个参数:url 第二个参数:对应新打开标签或者窗口的window.name属性,如果为填默认为 "_blank" 第三个参数:如果有则打开的 ...
- hammer用法 jquery.hammer.js
jquery.hammer.js使用时要先引入hammer.min.js 下面代码是滑动效果: $("#nav").hammer().bind('swiperight', fu ...