AsyncTask来源

什么是ANR?

ANR是“Application Not Responding”的缩写,即应用程序无响应。当android应用程序在相当长的一段时间内无响应的时候,系统就会弹出对话框提示程序已经停止响应,让用户可以选择退出应用或者继续等待。

ANR的触发原因

Android程序开始运行的时候会单独启动一个进程,默认情况下所有这个程序操作都在该进程中进行。一个android应用程序默认情况下只有一个进程,但是一个进程可以有子线程。在这些线程中,有一个线程叫UI 线程(或Main Thread),其它的线程都称为Worker Thread。UI线程主要负责控制UI页面的显示、更新和交互等。
如果一个应用程序的UI线程阻塞在IO操作(比如网络请求等)上而导致系统不能处理到来的用户输入事件,或者UI线程花了太多时间计算游戏中的下一个移动等,这就将导致ANR。Android系统中,应用程序的响应时间由Activity Manger Service和Window Manager Service监测。满足以下条件之一,即产生ANR。
1.对输入事件(如按键事件、触摸事件等)5秒之内无响应;
2.广播接收器在10秒之内未结束操作。

避免ANR

为了避免ANR的出现,任何情况下我们都应该把耗时的操作都放在Worker Thread里,而不能在UI线程,UI线程的操作要尽可能少。UI线程里的操作越简单,用户的操作也就更流畅。特别地,Activity的关键生命周期方法里如onCreate()和onResume()操作要尽可能少。因为worker thread里操作完成也可能需要更新UI控件,但是worker thread不能直接更新UI。所以Android有以下两种方法:
1.AsyncTask:后台执行复杂任务,并将执行结果发布在UI上。
2.Handler:新启线程发送消息,UI线程的Handler接收和处理消息。
本文主要介绍AsyncTask的学习。

AsyncTask使用

使用方法

AsyncTask< Params, Progress, Result >是一个抽象类,用于被继承。继承时需要指定三个泛型参数:
1.Params:输入给任务执行的参数的类型
2.Progress:在后台计算运行进度单元的类型
3.Result:后台计算完成返回结果的类型

以及实现任务执行四步骤的方法(如下),其中至少重写doInBackground方法,另外通常也重写onPostExecute方法。
1.onPreExecutor:在后台耗时操作前被调用
2.doInBackground:后台线程要完成的任务
3.onProgressUpdate:doInBackground 中调用publishProgress()方法更新任务的执行进度,触发此方法可在UI界面显示执行进度
4.onPostExecute:doInBackground完成后触发此方法,在UI界面显示执行结果。

最后,调用AsyncTask的子类实例的execute方法开始执行任务。

使用规则:
1.AsyncTask必须在UI线程加载,实例化对象必须在UI线程创建
2.execute方法必须在UI线程调用
3.不要直接调用onPreExecutor、doInBackground、onProgressUpdate、onPostExecute
4.每个任务只能被执行一次

示例代码

以下是一个非常简单的例子,点击界面上的Start文本,开启异步任务的执行,用进度条显示执行进度。执行完毕后,关闭进度条,更新Textview为Finished。

public class LearnAsyncTask extends Activity {    TextView tv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_learn_async_task);        tv = (TextView)findViewById(R.id.tvAsync);        tv.setText("Start!");        tv.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                new TestAsyncTask(LearnAsyncTask.this).execute();            }        });    }    private class TestAsyncTask extends AsyncTask<Void,Integer,Boolean> {        private Context mContext = null;        private ProgressDialog mDialog = null;        private int mCount = 0;        public TestAsyncTask(Context context) {            mContext = context;        }        @Override        protected void onPreExecute() {            super.onPreExecute();            mDialog = new ProgressDialog(mContext);            mDialog.setMax(100);            mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);            mDialog.show();        }        @Override        protected void onPostExecute(Boolean aBoolean) {            super.onPostExecute(aBoolean);            if(aBoolean && mDialog != null && mDialog.isShowing()) {                mDialog.dismiss();            }            tv.setText("Finished!");        }        @Override        protected void onProgressUpdate(Integer... values) {            super.onProgressUpdate(values);            mDialog.setProgress(values[0]);        }        @Override        protected Boolean doInBackground(Void... params) {            while(mCount < 100) {                publishProgress(mCount);                mCount += 10;                try {                    Thread.sleep(1000);                }catch (InterruptedException e) {                    e.printStackTrace();                }            }            return true;        }    }}

AsyncTask源码分析

首先,由AsyncTask的构造函数看看它的实例化。

构造函数初始化了两个AsyncTask类的成员变量mWorker和mFuture:
mWorker:内部类WorkerRunnable< Params,Result >的实例化对象,WorkerRunnable实现了Callable的接口。
mFuture:FutureTask的实例化对象,传入了mWorker作为形参,覆写了FutureTask的done方法。(FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值)

实例化对象之后执行execute()方法。

execute()方法调用了executeOnExecutor()方法,传入params作为形参,以及sDefaultExecutor。

该方法首先判断该异步任务的状态。Status包含三种:PENDING(表示该任务还未被执行),RUNNING(表示该任务正在执行),FINISHED(表示该任务已经执行结束)。当mStatus为RUNNING或者FINISHED的时候,则抛出异常。由此可以说明,execute方法只能调用一次。

接下来执行onPreExecute()方法。即AsyncTask中执行的第一个方法。重写这个方法做任务开始前的操作。

接着执行exec.execute(mFuture)语句。 exec即传入的形参sDefaultExecutor。注意,sDefaultExecutor是被static volatile修饰的。可以这样理解,static表示这个变量只有一份拷贝,不管这个类创建了多少个对象。因此一个对象更新了这个static变量,所有对象的这个变量都会发生变化。但是,如果两个线程同时访问同一个对象并且更新变量,线程操作的是它们各自本地的变量副本,所以一个线程里修改它的本地缓存中的变量,并不会影响到其他线程里的副本。将变量声明为static volatile,可以强迫线程每一次都去读取全局的值,而不是本地缓存的副本。也就是说,对于AsyncTask,所有AsyncTask实例都共用一个sDefaultExecutor对象。

sDefaultExecutor是SerialExecutor的实例。 SerialExecutor使用双端队列ArrayDeque来存储Runnable对象。执行execute方法时,会实例化一个Runnable加入到队列的最后,重写这个Runnable的run()方法执行传入的Runnable对象r的run()方法。然后判断mActive是否为空,第一次执行的适合,为空,则将调用scheduleNext()。这个方法会取出队列的头部的Runnable对象,调用THREAD_POOL_EXECUTOR执行。之后再有新的任务则等待上一个任务执行完毕后才能得到执行。所以说,SerialExecutor使得同一个时刻只有一个任务正在执行,其它任务均处在等待状态。

接下来,看看THREAD_POOL_EXECUTOR。也就是说,任务是在线程池里执行。且这个线程池也是多个异步任务共享的,不管应用程序中有多少异步任务都只有这一个线程池。

回到SerialExecutor的scheduleNext()方法中,THREAD_POOL_EXECUTOR.execute(mActive),mActive即传进来的mFuture对象,所以将调用FutureTask的run()方法。该方法中c.call()其实调用的就是mWorker.call()方法。

回到AsyncTask的构造函数中看WorkerRunnable中重写的call方法,可以看到接下来执行的是doInBackground方法。

重写doInBackground方法,就能在子线程里去处理耗时的操作了。等该方法执行结束,返回执行结果Result,然后调用postResult方法,向sHandler发送消息。

sHandler是InternalHandler的实例化对象,静态常量。 sHandler即UI线程里的Handler。所以就是子线程向UI线程发送数据,让sHandler来处理。

sHandler收到MESSAGE_POST_RESULT消息,会调用mTask即AsyncTask的finish方法。

finish方法如下。首先判断该任务是否被取消。若未被取消,则调用onPostExecute方法,重写这个方法则能在UI线程里进行任务执行完之后的操作。最后将任务的状态设置为执行完毕。

在doInBackground中调用publishProgress,可以在后台任务还在进行的时候显示UI更新。方法会向sHandler发送MESSAGE_POST_PROGRESS消息,然后调用onProgressUpdate方法。

AsyncTask怎么取消呢? AsyncTask的销毁不会随着activity的销毁而销毁,会一直执行doInBackgound方法直至结束。所以当不需要一个AsyncTask时,一定要取消它。

来看看AsyncTask的cancel方法。该方法试图取消任务的执行,但是如果任务已经被执行了,或已经被取消了或一些其它原因,则任务不能被取消。如果这个任务还没开始就被取消,那这个任务不会被执行;如果任务已经开始了,那么参数mayInterruptIfRunning决定了执行这个任务的线程是否能够被中断。

调用这个方法使得doInBackground执行之后会在UI线程执行onCancelled方法,并且onPostExecute方法就不会被调用了。

虽然取消了任务,但是cancel方法其实只是给这个任务设置了cancelled的状态,但实际doInBackground里执行后台任务的线程并不会真的结束,mayInterruptIfRunning设置为true时,也只是向运行中的线程发出interrupt的调用。所以,还应该在doInBackground操作中,定期检查isCancelled的返回值,以尽可能早地结束已经取消了的任务的执行。

总结

比较AsyncTask和Handler的使用,
1.AsyncTask其实是对Handler与Thread的封装。
2.AsyncTask底层需要一个线程池,Handler发送了一个消息队列,所以相对于AsyncTask更消耗资源。但是如果异步任务的数据特别庞大,Handler需要不停地new Thread,AsyncTask的线程池更节省开销。
3.另外AsyncTask只能在UI线程实例化,Handler实例化的位置随意,与其Looper相关。

更多相关文章

  1. 拨云见日---android异步消息机制源码分析(二)
  2. android中TextView中文字体粗体的方法 (android:textStyle="bold
  3. Android(安卓)音视频学习系列(一) JNI 从入门到精通
  4. android面试题(1)
  5. Android学习路线(十二)Activity生命周期——启动一个Activity
  6. Android进阶之AIDL的使用详解
  7. Android(安卓)扫一扫功能实现(Zbar)
  8. android 自定义属性通过反射关联组件与方法
  9. 一篇文章看明白 Android(安卓)从点击应用图标到界面显示的过程

随机推荐

  1. BufferQueue 学习总结(内附动态图)
  2. github上十二款Android播放器开源项目
  3. 视频播放器丢帧策略
  4. Android studio新版本 4.0安装和使用
  5. Android NoHttp基础详解
  6. Parcelable心得
  7. Android中向webview注入js代码
  8. Android无法自动创建USB打印机节点/dev/u
  9. Android-屏幕设备截屏
  10. android真机调试方法