zoukankan      html  css  js  c++  java
  • 深入理解AsyncTask

    众所周知,在Android中如果要执行耗时的操作,一般是在子线程中处理,使用new Thread的方法实现是最常见的方法之一。今天,我们要讲的是另外一个,Android提供的异步任务类AsyncTask,底层是使用线程池实现的。

    一、Android的线程

    线程是操作系统的最小执行单位,它的创建和销毁都会消耗一定的系统资源,如果频繁的创建和销毁,显然不是高效的做法,正确的做法是,采用线程池,缓存一定量的线程,通过复用这些线程,避免造成极大的系统开销。

    二、AsyncTask

    这是一个抽象类,使用方便,代码简洁,所以说是一个轻量级的异步类。它可以在线程池中执行后台任务,然后把执行的进度和结果通过Handler传给UI线程进而刷新视图。

    该类的声明如下:

    public abstract class AsyncTask<Params, Progress, Result>

    其中各个参数的含义如下:

    Params:开始异步任务执行时传入的参数类型;

    Progress:异步任务执行过程中,返回下载进度值的类型;

    Result:异步任务执行完成后,返回的结果类型;

    如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。

    三、AsyncTask源码

    AsyncTask内部封装了2个线程池:SerialExecutor和THREAD_POOL_EXECUTOR,和1个Handler(IntentHandler)。其中SerialExecutor线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR线程池才真正地执行任务,InternalHandler用于从工作线程切换到主线程。部分源码如下:

    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);
                }
            }
        }
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    sPoolWorkQueue, sThreadFactory);
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
    private static class InternalHandler extends Handler {
            public InternalHandler() {
                super(Looper.getMainLooper());
            }
    
            @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;
                }
            }
        }

    四、注意事项

    1、因为执行后的结果要传递到主线程,所以使用Handler进行工作线程和主线程的切换,所以AsyncTask创建实例和execute需要在主线程调用;

    2、onPreExecute(),onProgressUpdate(Progress... values),onPostExecute(Result result),onCancelled()方法是在主线程执行的,而doInBackground(Params... params)是在工作线程执行;

    3、执行顺序:onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute(),如果不需要执行更新进度则为onPreExecute() --> doInBackground() --> onPostExecute(),其中publishProgress方法在doInBackground方法中调用,会触发onProgressUpdate方法;

    4、AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。

    5、一个任务实例只能执行一次,如果执行第二次将会抛出异常。

    五、AsyncTask使用不当的后果

    1、生命周期:AsyncTask不与任何组件绑定生命周期,所以在Activity或者Fragment中创建执行AsyncTask时,最好在Activity或Fragment的onDestory()调用 cancel(boolean);

    2、内存泄漏:如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露;

    3、 结果丢失:屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

    参考链接:

    http://www.jianshu.com/p/817a34a5f200

  • 相关阅读:
    MultipartFile 多文件上传的应用
    启动关闭zookeeper集群的脚本
    分布式锁
    NFS部署教程
    Docker安装(Debian8)-构建简单的SpringBoot应用
    Nginx实战-后端应用健康检查
    分布式文件系统FastDFS安装教程
    Redis缓存使用技巧
    WebSocket原理与实践
    HashMap中ConcurrentModificationException异常解读
  • 原文地址:https://www.cnblogs.com/hacjy/p/6895150.html
Copyright © 2011-2022 走看看