关于AsyncTask的一点愚见

在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开发艺术探索。辅助了我去阅读下面的源码。

时间: 2024-10-14 06:28:48

关于AsyncTask的一点愚见的相关文章

测试时间不够怎么办?

测试过程中我们经常会遇到测试时间不够的问题,今天就来讨论下,在这种情况下怎么办.一点愚见,欢迎一起讨论. 1.沟通确认是否可以延期.这是我最先想到的,虽然每个人都告诉你这是最后期限,但其实只要充分沟通,再找相关利益协商,至少有50%的情况并不是最后期限,确实能再延一段时间. 2.排优先级.按照重要性和风险排个优先级,优先测重要的和风险大的特性.功能点. 3.裁剪需求.部分不重要的特性是否可以裁剪,不进行测试,此方法要和2结合一起开展,并且要和相关利益人进行沟通确认,比如版本经理或下游客户. 4.

Node调试工具JSHint

Node调试工具JSHint的安装及配置教程 作者: 字体:[增加 减小] 类型:转载 时间:2014-05-27我要评论 Node的优势我就不再乱吹捧了,它让javascript统一web的前后台成为了可能.但是对于新手来说,server端的JS代码可能不像client端的代码那么好调试,直观.client端JS代码的调试基本上经历了一个从"肉眼--alert()--firebug(或者其它的developer tools)"的一个过程.而对于server端的调试,可能新手仍然停留在

数据仓库专题(4)-分布式数据仓库事实表设计思考---讨论精华

一.前言 上一篇分享博文<数据仓库专题(3)--分布式数据仓库事实表设计思考>后,陆续有各位兄弟参加大讨论,提出了各种问题,关于分布式环境下,维表和事实表设计,进行了比较深入的探讨,在此汇集整理,分享给大家.希望能有更多人参与尽力啊,共同探索分布式数据仓库数据模型的设计. 二.纪要 [活跃]北京-RTB-胖哥(1106110976) 10:21:36 分布式模式下事实表设计思考: 做大做强事实表,做小做弱维表: [冒泡]杭州-电子病历<[email protected]> 10:2

1-5章

第1章: 第1章是本书的开端,讲述的是软件工程是什么,软件工程就是把系统的.有序的.可量化的.方法应用到软件的开发.运营和维护上的过程. 在了解软件工程之前,我们先了解一下软甲有哪些特殊性,复杂性.不可见性.易变性.服从性.非连续性. 软件工程包括以下领域:软件需求分析.软件设计.软件构建.软件测试.和软件维护. 软件工程和下列学科相关:计算机科学.计算机工程.管理学.数学.项目管理学.质量管理.软件人体工学.系统工程.工业设计.和用户界面设计. 第2章: 第2章是个人技术和流程,个人技术大概讲

浅析去除验证码图片中的干扰线、噪点(java)

(原创文章,转载请加转载地址)  版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明 图片处理中最为重要的是对目标图片的特征分析,通过这些特征(点)设计图片预处理方法.针对带有干扰线.噪点特征的验证码图片,自然有它自己的处理方法,下面是个人的一点愚见,图像处理常用的是MATLAB和c++来做的,因为它们都有强大的图像处理的库,在Java中关于图像处理的就相对来说少一些. 图片像素由24位二进制的机器码表示,可以表示为ARGB,这里和色彩的RGB并不冲突,这里的A表示的是透明度,网上

java笔记多态性

今天听一个同学讲了java动态绑定的一个很好地例子,我深受启发,特此记录.java动态绑定是在运行时才会具体绑定,而不是在编译的时候进行相关操作,这为代码的灵活性提供了极大便利,也使得维护边的较为简便. 对于动态绑定我的理解是,在定义一个方法 的时候,需要某些参数,比如一个类对象,但是在运行的时候这个对象才回确定,比如打CS时候的各种枪,这个时候,为这个方法重载不同的对象参数会变得极为繁琐,所以我们可以造一个枪的父类,把父类的对象传到这个参数中,在运行时,再决定是哪种具体的枪. class Gu

ajax长轮询 (转)

javaWeb项目中需要一个实时提醒的功能,也就是某人做了某一操作,可以立即提醒到当前在线的用户 最开始想在用户做了操作后,储存一个状态到数据库中然后用每隔几秒用ajax去请求后台查询数据库来确定是否显示提醒窗口 提醒窗口使用jquery easyui 的messager 在右下角弹出如下图 后查得可通过AJAX长轮询的方法来解决频繁对后台的请求,进一步减小压力 在实现过程发现AJAX的多次请求会出现多线程并发的问题又使用线程同步来解决该问题 个人对ajax长轮询的一点愚见 ajax请示后台时,

Node调试之道-----JSHint

Node调试之道-----JSHint Node的优势我就不再乱吹捧了,它让javascript统一web的前后台成为了可能.但是对于新手来说,server端的JS代码可能不像client端的代码那么好调试,直观.client端JS代码的调试基本上经历了一个从"肉眼--alert()--firebug(或者其它的developer tools)"的一个过程.而对于server端的调试,可能新手仍然停留在使用"肉眼--console()"的阶段.其实,Node经过了这

关于 Head First SQL 中文版

我想谈谈 我对于Head  First  SQL  中文版的一些看法 事实上关于我翻译的这个Head  First  SQL 中文版..我自觉得:的确翻译得非常烂.. 和翻译Head  First  C#的飞哥相比,的确是要差了几个等级... 可是要知道...飞哥是过了六级...550多分...口语A级的鸟人的嘛.. 我还仅仅过了四级...今天看能不能凭点运气...把六级也过了... 可是从整个翻译的角度上来说.我觉得95%我是翻译到了位的.仅仅有少许的错误. 要知道:英语的东西,还是看原版的好