欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android - AsyncTask 使用及原理

程序员文章站 2022-07-14 16:13:41
...

定义

AsyncTask 为android 封装好的轻量级异步任务类,可用来处理耗时任务 I/O 网络访问 等

使用

使用步骤:
1. 自定义StormAsyncTask extends AsyncTask
2. 根据需求重写方法,其中doInbackground 必须重写,否则报错
3. 调用AsyncTask.execute(参数)

    //AsyncTask 为抽象类,所以在使用时,需继承其使用
    public abstract class AsyncTask<Params, Progress, Result> {
    //为抽象方法,必须重写
    protected abstract Result doInBackground(Params... params); 

    private void useAsyncTask() {
        StormAsyncTask mStormAs = new StormAsyncTask();
        mStormAs.execute("stormxz");
        mStormAs.cancel(true);
    }

自定义AsyncTask
三个泛型:

第一个为开发传入数据的类型 / doInBackgroudn 参数类型
第二个为onProcess 的类型 / onProgressUpdate 参数类型 / doInbackground 的publishProgress 参数类型
第三个为result 的类型 / onPostExecute 参数类型 / doInbackground 的返回值类型

    private class StormAsyncTask extends AsyncTask<String,Integer,Boolean>{

        /**
         *  准备操作
         *  main thread
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.d("stormxzAs", "onPreExecute  ThreadName = " + Thread.currentThread().getName());
        }

        /**
         *  后台处理
         *  asynctask thread 线程池
         */
        @Override
        protected Boolean doInBackground(String... params) {
            Log.d("stormxzAs", "doInBackground   ThreadName = " + Thread.currentThread().getName());
            publishProgress(333);
            return false;
        }

        /**
         *  过程数据更新
         *  main thread
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            Log.d("stormxzAs", "onProgressUpdate = " + values[0] + "   ThreadName = " + Thread.currentThread().getName());
        }

        /**
         *  结果返回
         *  main thread
         */
        @Override
        protected void onPostExecute(Boolean bool) {
            super.onPostExecute(bool);
            Log.d("stormxzAs", "onPostExecute = " + bool + "   ThreadName = " + Thread.currentThread().getName());
        }

        /**
         *  取消
         *  main thread
         */
        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }

运行流程:execute -> onPreExecute -> doInBackground -> publishProgress -> onProgressUpdate -> onPostExecute

D stormxzAs: onPreExecute
D stormxzAs: doInBackground
D stormxzAs: onProgressUpdate + 333
D stormxzAs: onPostExecute

注意:
1. 一个AsyncTask对象,只执行一次,否则会报一下错误
java.lang.IllegalStateException: Cannot execute task: the task is already running
2. 调用cancel 方法后,会调用onCancelled,不会调用onPostExecute,但是 doInBackground 中的方法是不会停止的,此时需要用isCancelled 中断doInBackground中的循环操作才行
BitmapFactory.decodeStream()的IO操作,调用cancle方法是无效的
3. 只有doInbackground 在 AsyncTask 的线程中,其他三个方法在main 线程中,可更新UI

原理

“2 个线程池” + Handler
此处为什么加引号呢,SerialExecutor仅仅是实现了Executor 接口,不能算作线程池

SerialExecutor :添加任务到队列中并触发第二个线程池
THREAD_POOL_EXECUTOR:真正执行任务的线程池
Handler:子线程与UI线程通信


标志位状态

    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

如果RUNNING 以及 FINISHED ,此时无法继续运行,保证了一个AsyncTask 对象只能运行一次execute


构造方法 初始化变量
创建Handler、WorkerRunnable、FutureTask对象

    public AsyncTask(@Nullable Looper callbackLooper) {

        //初始化Handler 用来子线程和UI 线程通信
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);


        //初始化具体任务行为 doInBackground
        //重写call 方法,在FutureTask run方法中运行
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //设置优先级
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    //此处运行doInBackground,为子线程中运行(线程池中),返回结果
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    //出现异常后,标志取消
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //运行结束,将结果通过Handler返回到主线程中去
                    postResult(result);
                }
                return result;
            }
        };

        //初始化工作任务
        //将WorkerRunnable 作为参数传入进去  最终会在此处调用THREAD_POOL_EXECUTOR.execute(mActive);
        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 occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

    //默认创建此Handler
    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @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
                    //执行AsyncTask 中的finish方法,实际就是调用的onPostExecute
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    //当在doInbackground中执行 publishProgress,此时调用上层的onProgressUpdate
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

    private void finish(Result result) {
        //判断是否取消,如果取消则返回结果到onCancelled方法;否则返回到onPostExecute
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

FutureTask
FutureTask 实际extends Runnable,在构造方法中传递参数WorkerRunnable

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable; //传递的workerrunnable
        this.state = NEW;       // ensure visibility of callable
    }

    public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                //此处调用WorkerRunnable 中的call方法,从而调用到doInbackground
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

WorkerRunnable

    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    } 

内部静态抽象类 实现Callable接口,重写call方法

从上面方法中可以看出,mWorker对象的call方法是在FutureTask的run方法中执行


execute
程序执行入口

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //判断当前AsyncTask能否运行,如果状态为正在运行或者已经运行结束,则返回;这就是为什么一个AsyncTask对象只能运行一次的原因
        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 方法,所以是第一个执行
        onPreExecute();

        //赋值参数
        mWorker.mParams = params;

        //将任务放入线程池中
        exec.execute(mFuture);

        return this;
    }
  1. 判断该AsyncTask是否可以运行
  2. 执行onPreExecute方法
  3. 将值赋值到mWorker中
  4. 将任务FutureTask 添加到线程池SerialExecutor 中

线程池
变量SERIAL_EXECUTOR 以及 THREAD_POOL_EXECUTOR static修饰,保证实例后的所有对象共享此线程池
(1) SerialExecutor 创建对象为静态变量,所以只要继承了AsyncTask,同一个进程中使用的都是同一个SerialExecutor 对象
SerialExecutor 中包含一个队列用来存储FutureTask
每次将队列的头部取出来,放入THREAD_POOL_EXECUTOR 线程池中运行,调用FutureTask的run 方法,从而调用到WorkerRunnable的call方法

   private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        //其实就是从队列中获取首端的FutureTask
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //将任务添加到ArrayDeque 队列中,这里只存,不运行runnable;在第二个线程池中运行
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        //执行FutureTask中的run方法,调用call
                        r.run();
                    } finally {
                        //查询下一个任务
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                //查询下一个任务
                scheduleNext();
            }
        }

        //此线程池会执行FutureTask的run 方法
        protected synchronized void scheduleNext() {
            //获取队列首端数据,将其放入第二个线程池中
            if ((mActive = mTasks.poll()) != null) {
                //再执行run方法
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

通过synchronized 同步方法,保证了其有序同步运行

(2)创建线程池 THREAD_POOL_EXECUTOR 参数与CPU相关
初始化线程池

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

AsyncTaskResult
可以看到在Handler中使用到

 @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task; //AsyncTask 本身
            mData = data; //数据,result 或者 progress的数据
        }
    }

cancel
通过mCancel 来记录是否取消,并通知FutureTask 中断线程

    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }

调用cancel 方法后,会调用onCancelled,不会调用onPostExecute,但是 doInBackground 中的方法是不会停止的,此时需要用isCancelled 中断doInBackground中的循环操作才行
BitmapFactory.decodeStream()的IO操作,调用cancle方法是无效的

内存泄漏

在Android 开发中 代码稍加不规范,就会导致内存泄露。
我使用的工具为LeakCanary 来检测的

产生原因:
非静态内部类/匿名类会隐式的持有外部类的引用
如果AsyncTask写在 Activity 中,会持有Activity的引用,当Acitivity 销毁后,doInbackground 实际还是运行的,所以导致Activity 无法正确回收

解决方法:
1. 静态内部类
2. 使用弱引用来调用外部类方法或变量
3. doInBackgroud 中如果有循环操作 那么 onDestory 中需要进行cancel 同时与isCancelled(),阻止循环继续运行

    private static class StormAsyncTask extends AsyncTask<String,Integer,Boolean>{

        private WeakReference<MainActivity> mWeak ;

        public StormAsyncTask(MainActivity activity) {
            super();
            mWeak = new WeakReference<MainActivity>(activity);
        }

        /**
         *  准备操作
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        /**
         *  后台处理
         */
        @Override
        protected Boolean doInBackground(String... params) {
            for (int i = 0; i < 1000000; i++) {
                if (isCancelled()) {
                    return false;
                }
                Log.d("stormll", "meile i = " + i);
            }
            publishProgress(333);
            return false;
        }

        /**
         *  过程数据更新
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        /**
         *  结果返回
         */
        @Override
        protected void onPostExecute(Boolean bool) {
            super.onPostExecute(bool);
            MainActivity activity = mWeak.get();
            if (activity == null
                    || activity.isFinishing()
                    || activity.isDestroyed()) {
                // activity没了,就结束可以了
                Log.d("stormlls", "activity null");
                return;
            } else {
                Log.d("stormlls", "activity not null");
            }
            // 继续更新ui
            activity.mStopButton.setVisibility(View.VISIBLE);
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }

看到新问题会继续补充~~