AsyncTask学习笔记

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的构造函数看看它的实例化。

screenshot

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

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

screenshot

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

screenshot

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

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

screenshot

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

screenshot

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

screenshot

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

screenshot

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

screenshot

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

screenshot

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

screenshot

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

screenshot

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

screenshot

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

screenshot

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

screenshot

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

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

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

screenshot

虽然取消了任务,但是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相关。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值