在Android开发中,为了避免出现ANR现象,主要是指来自于接触事件响应事件过长来说,我们开发者通常会将耗时长的操作,如网络操作,大图片加载,IO操作等等会放在子线程中去处理。
而Android中线程除了来自于Java的Thread,Executor生成器之外,还有已经封装好的AsyncTask,IntentService,HandlerThread。
而本文就开始从AsyncTask开始说起自己的感悟吧。
AsyncTask 是一种轻量级别的异步任务类,它在线程池中执行后台人物,然后把执行进度以及结果传到主线程中。在AsyncTask中复用了Handler和Thread。
值得注意有三点:
1.AsyncTask作为轻量级的异步操作不适宜做耗时太过长的操作,毕竟AsyncTask核心线程只有5个,缓冲队列是10。倘若队列
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread More ...newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static final int MESSAGE_POST_CANCEL = 0x3;
从上面的代码可以清晰的知道AsyncTask抽象类里面调用了同步队列长度为10的LinkBlockingQueue来对任务进行缓冲,同时声明了一个核心线程数为5,线程池最大容量为128的线程池。(而在android5.0中同步队列为128)
换句话说,一旦做出超出这个线程池最大容纳量,就会造成后续的线程进入到阻塞状态。
2.AsyncTask要在主线程中声明。
且看ActivityThread中main函数,这就是我们想要知道的android对应在Java中的入口函数。
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
从上面的入口函数可以知道,AsyncTask初始化在主线程,同时在主线程Looper了一个MainLooper,而其中AsyncTask复用了Handler,这个Handler也是来源于MainLooper。这也从侧面印证了AsyncTask必须声明在主线程.
3.不要在主线程中直接调用onPreExecute(),onPostExecute,doInBackground,on-ProgressUpdate方法。
这里就就继续介绍AsyncTask使用方法。首先可以从第一段代码看到AsyncTask是一个抽象类,那么我们必须继承这个抽象类并且实现其中抽象方法,分别是:
1.onPreExecute
2.doInBackground
3.onProgressUpdate
4.onPostExecute
这四个方法。
这四个方法分别的作用分别是:
1.onPreExecute():执行在主线程中,在异步任务开始前,要做的准备工作。
2.doInBackground(Params…params):这个方法执行在线程池中,用于执行异步任务,Params表示异步任务参数。在这个方法中可以调用publishProgress(Progress…value)来更新任务进度,同时publishProgress将会调用onProgressUpdate。
3.onProgressUpdate(Progress…values)执行在主线程中,当后台任务执行进度发生改变时,该方法会调用。
4.onPostExecute(Result result)执行在主线程中,在异步任务执行之后,这个方法才会调用,result就是doInBackground的返回值。
下面是一个AsyncTask的简单控制长条型进度条的Demo:
public class MainActivity extends Activity {
private ProgressBar bar;
private Button button;
private TextView text;
private Button cancel;
private DownLoadTask dtask;
private Button sIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bar = (ProgressBar)findViewById(R.id.progress);
button = (Button)findViewById(R.id.start);
text = (TextView)findViewById(R.id.text);
cancel = (Button)findViewById(R.id.cancel);
sIntent = (Button)findViewById(R.id.intentService);
cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
if(dtask instanceof DownLoadTask){
dtask.cancel(true);
}
}
});
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
dtask = new DownLoadTask(text, bar);
dtask.execute(1000);
}
});
sIntent.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent service = new Intent(MainActivity.this, MyIntentService.class);
Log.e("service", "start");
service.putExtra("task_action", "Task1");
startService(service);
service.putExtra("task_action", "Task2");
startService(service);
service.putExtra("task_action", "Task3");
startService(service);
}
});
}
class network{
public void operate(){
try{
Thread.sleep(100);
}catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
class DownLoadTask extends AsyncTask<Integer, Integer, Integer>{
private TextView text;
private ProgressBar bar;
public DownLoadTask(TextView text,ProgressBar bar){
super();
this.text = text;
this.bar = bar;
}
@Override
protected void onPreExecute(){
bar.setProgress(0);
text.setText("Async start");
}
@Override
protected Integer doInBackground(Integer... params) {
// TODO Auto-generated method stub
network net = new network();
int value = 0;
for(int i = 0;i < 100;i++){
net.operate();
value = i;
publishProgress(i);
}
return value;
}
@Override
protected void onPostExecute(Integer result){
text.setText("AsyncTask End");
}
@Override
protected void onProgressUpdate(Integer... values){
int value = values[0];
Log.e("value", ""+value);
bar.setProgress(value);
text.setText(String.valueOf(value));
}
}
}
在上面Demo中用一个net的class来模拟反应时间较长的任务,同时每隔一定的时间就刷新UI控件。
那么就是这么做的:耗时的任务用处理异步任务的doInBackground来完成,而刷新UI控件的方法放在onProgressUpdate进行。这样就没有违背UI控件必须在主线程才能才能访问的原则。
下面继续来稍微的看看AsyncTask的工作流程,从上面的例子看来AsyncTask是通过execute启动的。先跳过构造器让我们看看吧,android5.0下面怎么做的:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
从上面可以知道,execute调用了 executeOnExecutor,那么继续跟踪:
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;
}
从上面的代码,我们可以知道对异常的判断之后,再获取AsyncTask状态以及先处理onPreExecute()这个函数,这也印证了为什么先处理onPreExecute()这个函数了。获取完传入参数之后,在进行异步处理。
在这里还要提一提AsyncTask这个异步任务和我们熟知的线程池不太一样,默认的情况下是串行,为什么这么说呢?看之前的代码:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
直接execute()的话,会调用默认的线程池sDefaultExecutor,而sDefaultExecutor又是什么呢?
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) {
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);
}
}
}
这就应该清楚了吧,sDefaultExecutor是一个来自于SerialExecutor的类,传入mFuture参数,这是一个在构造器完成了声明的并发类。接着mFuture将会插入队列中,如果没有可运行的任务就会调用THREAD_POOL_EXECUTOR.execute(mActive);来执行下一个任务,因此是默认的。那么又如何使其变成并发工作,而不是串行工作呢?
从上面的源码可以看出,真正在处理线程是THREAD_POOL_EXECUTOR这个线程池,如下声明:
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
而默认的串行执行,只是因为任务在队列中阻塞等待。那么我们不使用sDefaultExecutor来执行AsyncTask,转而使用THREAD_POOL_EXECUTOR来执行即可。
如下:
new MyAsyncTask("Task1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
即可完成并发执行。
之前我们跳过了构造器,那么我们现在来看看里面的构造器是怎么实现的:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
从上面的代码,我们可以清晰的看见mWorker的声明中,实现了抽象类WorkerRunnable,将mTaskInvoked设置为真,代表任务已被调用。而调用了doInBackground的方法最后会在FutureTask这个并发中执行,并且返回值给postResult()。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
从上面的代码可以清晰可见这里面复用了Handler,将返回值通过handler传回主线程(Hnandler是怎么回事上一章已经总结了),记下来看看处理器怎么处理的:
private static class InternalHandler extends Handler {
@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;
}
}
}
可以知道,handler在处理两个内容一个是处理发送过来的信息:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
检查是否被调用终止,是怎停止任务,并且下次从头开始,不是则处理onPostExecute(),来处理返回值。
另一个则是处理publishProgress方法,且看看他是怎么发送的:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
这样子就能完全明白了吧,通过publishProgress发送消息到handler之后,转到onProgressUpdate(Progress…value)方法处理刷新进度操作。
到这里,AsyncTask的流程就完全结束了。
在这里要感谢任玉刚的android开发艺术探索。辅助了我去阅读下面的源码。