AsyncTask从使用到源码解析(Android-26/22)

AsyncTask相信或多或少都使用过,其封装了线程池和handler,更优雅的处理了异步任务的交互,用起来也极为简单
本篇大概会涉及:

  • asynctask为何不能在主线程execute
  • asynctask为何不能执行两遍execute
  • asynctask的cancel如何停止一个正在执行的线程
  • 我们最常用的几个方法,在asynctask中何处被调用,如何被调用
  • asynctask的整个执行逻辑
  • -

简单使用:

private class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
        @Override
        protected void onPreExecute() {
            mDialog.show();
        }

        @Override
        protected Void doInBackground(Void... params) {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(80);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                publishProgress(i);
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            mMyAsyncTask.cancel(true);
            mDialog.setProgress(values[0]);
        }

        @Override
        protected void onPostExecute(Void result) {
            mDialog.dismiss();
            mTextView.setText("加载完毕");
            //MyAsyncTask myAsyncTask = new MyAsyncTask();  这里加上 就会循环执行
            //myAsyncTask.execute();
        }

        @Override
        protected void onCancelled() {
            Log.e(TAG, Thread.currentThread().getName() + " onCancelled ");
        }
    }
MyAsyncTask myAsyncTask = new MyAsyncTask();
    myAsyncTask.execute();

使用注意:不可以在子线程调用myAsyncTask.execute();且同一个任务只可以执行一次(同一个任务指的是new MyAsyncTask())

太简单了,都不好意思说怎么用的,简单说下这泛型中的三个参数:
Params, Progress, Result
Params是doInBackground的参数类型,是execute执行时传递的参数类型
Progress 是onProgressUpdate的参数类型
Result 是onPostExecute任务结束返回的参数类型


———————-不华丽的风格陷———————————
看源码解析吧:
我们基于myAsyncTask.execute();这行代码分析;

 @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //必须是未执行的状态也就是创建asyncTask默认的状态
        //如果是正在执行的任务或者是已经执行的任务都会直接抛出异常
        //这里就验证了 一个执行中的任务不可以再次执行
        //一个已经执行的任务(指的是new MyAsyncTask())不可以再次执行
        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 通常我们会实现这个protected来做一些准备工作
        //比如ui的更新 此时还是在主线程中
        onPreExecute();

        mWorker.mParams = params;

        exec.execute(mFuture);

        return this;
    }

那么 mWorker exec mFuture分别都是什么?
mWorker:实现了Callable其call方法主要功能是调用doInBackground方法并获取结果,并将结果通过Handler切换到主线程
mFuture:实现了Runnable 其run方法会在子线程调用mWorker的call方法
exec:通过线程池执行mFuture
为什么没有展示一下他们的源码呢,下面会按流程分析到

看下exec,其传入的是一个默认sDefaultExecutor,最终来到这里 看下execute做了啥

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);
            }
        }
    }

就是将一个runnable加入到队列中,这个r我们前面也说了就是mFuture下面的函数就是通过线程池执行这个runnable,好像没有什么逻辑这里,那么我们再去看下mFuture的run做了什么

public void run() {

                result = c.call();

                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);
        }

     if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // 释放占用的cpu资源 让给其他的任务 但放弃的时间不确定

yield:释放占用的cpu资源 让给其他的任务 但放弃的时间不确定,这里不深究多线程的东西,继续走我们的主线
为了避免篇幅过长,我们只关注核心方法,首先调用了c.call();拿到了结果,那么我们肯定有个疑问,这个c是什么鬼?ok,我们看下c在哪里赋值的,

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


        //asyncTask的构造函数中
        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);
                }
            }
        };

看到这里明白了,c是mWorker,那么我们知道了,真正获取结果还是在mWorker的call方法中啊,再去看一下,他的位置在asyncTask的构造函数中

 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
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

看到一个属性的方法doInBackground,是不是突然觉得遇到熟悉的东西了..这里就调用我们自己的实现了,doInBackground(mParams)是运行在子线程的,这里我们模拟一下数据立即就返回了,那么会调用postResult(result);我们跟着看一下他的实现

 private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

这个应该也是非常熟悉的了,看到这个直接找handleMessage实现就稳了

 @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;
            }
        }

我们发送的是MESSAGE_POST_RESULT那么直接去看下finish就行,mTask也就是this

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

如果没有调用cancel那么直接调用onPostExecute了,ok这里我们一次任务执行差不多结束了,但是其mFuture的done还没有执行,可能有疑惑了,这哥们哪里调用的?

public void run() {

                result = c.call();

                if (ran)
                    set(result);
         }

回到mFuture的run方法,在我们拿到数据后会调用 set(result); 在finishCompletion方法中我们可以看到 done();的调用

 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);
                }
            }
        };

回到done方法 看下他的实现做了啥呢?

          private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

这里根据wasTaskInvoked判断是否调用postResult,但是在mWorker的call方法中我们已经将其置为true了,所以这里就GG了

走到这里我们认为其一次任务执行完毕,但是还有一个经常用到的publishProgress方法也要讲一下,这哥们怎么用的?

@WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

其也是运行在子线程的,这个消息我们上面也看到过

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;
            }
        }

又遇到我们熟悉的onProgressUpdate方法了,我们经常在doInBackground执行耗时任务的同时通过publishProgress来触发onProgressUpdate来做ui更新,原来就是这样实现的啊…

还有个cancal方法需要讲一下 cancel(true);

public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

t.interrupt();这里中断了线程的执行,call结束后通过handler处理时发现这个任务已经被取消 那么就会调用onCancelled 我们就可以通过实现onCancelled来完成一些ui交互 因为此时又在主线程了

 private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

      @SuppressWarnings({"UnusedParameters"})
    protected void onCancelled(Result result) {
        onCancelled();
    }    

经过我测试发现,就算调用了cancel,doInBackground中的任务还是会执行完,仅仅是不会调用onPostExecute罢了,而是走onCancelled,学过Java多线程的可能都知道通过interrupt来停止线程是停止不了的,需要辅以抛异常的方法,或者使用interrupted判断和retrun结合来停止线程,但是在run方法中貌似都没有这些,这里暂时我还没想到为什么停止了一个没有停止的线程有何意义,知道的烦请流个言,我是通过调用cancel辅以在doInBackground中catch捕获InterruptedException 异常直接返回的方式来结束线程运行,看下log验证

 @Override
        protected void onProgressUpdate(Integer... values) {
            mMyAsyncTask.cancel(true);
            mDialog.setProgress(values[0]);
        }

 @Override
        protected Void doInBackground(Void... params) {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(80);
                } catch (InterruptedException e) {
                    Log.d(TAG, "InterruptedException : ");
                    return null;
                }
                publishProgress(i);
                Log.d(TAG, "doInBackground : " + i);
            }
            Log.d(TAG, "doInBackground : 结束");
            return null;
        }

如果注释了return null;日志如下,很明显,虽然调用了cancel,他也确实调用了interrupt,但是其线程并没有停止

.........
doInBackground : 91
doInBackground : 92
doInBackground : 93
doInBackground : 94
doInBackground : 95
doInBackground : 96
doInBackground : 97
doInBackground : 98
doInBackground : 99
doInBackground : 结束
main onCancelled 

如果解开return null的注释:很明显线程立即就停止执行并调用了onCancelled方法

 doInBackground : 0
 InterruptedException : 
 main onCancelled 

总结:THREAD_POOL_EXECUTOR(线程池) Future Worker AsyncTask
1.线程池只负责执行runnable(Future),
2. 而Future则负责在子线程中调用Callable(Worker),并提供了一些其他操作方法,比如cancel方法,可以中断正在执行的线程
3.worker(Callable)只需要在call中接收doInBackground返回的结果即可,
4.AsyncTask统领全局,根据任务是否取消的状态来决定调用onPostExecute亦或onCancelled,当然AsyncTask还做了其他很多的工作,
——–四者分工明确


碎片时间文章不太工整,有错误的地方请指出

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值