Asynctask那些事

耗时操作?更新UI?这些都是我们经常听到的词汇了,最常用的方法就是Thread+Handler的方法,今天就来聊聊另一个熟悉的Asynctask,或许你没有听过,别着急,通过本次(API23)的源码进行讲解,你就明白了。

首先,通过一个简单的例子来看下Asynctask的写法:

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    private ProgressDialog mDialog;
    private TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.id_tv);
        mDialog = new ProgressDialog(this);
        mDialog.setMax(100);
        mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mDialog.setCancelable(false);
        new MyAsyncTask().execute();
    }
    private class MyAsyncTask extends AsyncTask<Void, Integer, Void>
    {
        @Override
        protected void onPreExecute()
        {
            mDialog.show();
            Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");
        }
        @Override
        protected Void doInBackground(Void... params)
        {
            // 模拟数据的加载,耗时的任务
            for (int i = 0; i < 100; i++)
            {
                try
                {
                    Thread.sleep(80);
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                publishProgress(i);
            }
            Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");
            return null;
        }
        @Override
        protected void onProgressUpdate(Integer... values)
        {
            mDialog.setProgress(values[0]);
            Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");
        }
        @Override
        protected void onPostExecute(Void result)
        {
            // 进行数据加载完成后的UI操作
            mDialog.dismiss();
            mTextView.setText("LOAD DATA SUCCESS ");
            Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");
        }
    }
}

进入某个Activity,Activity中需要的数据来自于网络或者其它耗时操作,可以在AsyncTask中onPreExecute完成一些准备操作。

比如上例中显示进度对话框;然后在doInBackground完成耗时操作,

在进行耗时操作时还能不时的通过publishProgress给onProgressUpdate中传递参数,然后在onProgressUpdate中可以进行UI操作。

比如上例更新进度条的进度;当耗时任务执行完成后,最后在onPostExecute进行设置控件数据更新UI等操作,例如隐藏进度对话框。

真正实现就是一个new MyAsyncTask().execute();通过execute方法实现。下面就从源码的角度聊聊AsyncTask。

既然我们调用的是execute方法,那就先从execute方法

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

在执行execute方法中,将status状态更改为running的状态。

mStatus = Status.RUNNING;设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。

onPreExecute();执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。

mWorker.mParams = params;将我们传入的参数赋值给了mWorker.mParams

exec.execute(mFuture);

相信大家会对mWorker,mFuture有疑问,别急,容我慢慢道来

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

mworker可以看到是Callable的子类,且包含一个mParams用于保存我们传入的参数,

下面看初始化mWorker的代码:

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            return postResult(result);
        }
    };
}

构造方法中我们看到mworker的初始化操作,通过上面的代码我们知道一个抽象类,因此new了一个实现类,实现了call方法,call方法中设置mTaskInvoked=true,且最终调用doInBackground(mParams)方法,并返回Result值作为参数给postResult方法.可以看到我们的doInBackground出现了。

最后我们来看看postResult方法

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

到此我们看到了一个熟悉的东东,Handler,Handler,Handler,?,重要的事情说三遍。

postResult中出现了我们熟悉的异步消息机制,传递了 message.what为MESSAGE_POST_RESULT的消息;

message.object= new AsyncTaskResult(this,result);

AsyncTaskResult实际上就是一个携带参数的对象而已

private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;

    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

现在让我们找找handler相关的代码,可以看到,在接收到MESSAGE_POST_RESULT消息时,执行了result.mTask.finish(result.mData[0]);其实就是我们的AsyncTask.this.finish(result)

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

下面了解下finish方法

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

可以看到,如果我们调用了cancel(),则执行onCancelled回调;

正常执行的情况下调用我们的onPostExecute(result);

主要这里的调用是在handler的handleMessage中,所以是在UI线程中。最后将status的状态改为finished。

mworker聊的差不多了,现在我们来聊聊下一个东西mFuture,同样的,mFuture也是在构造方法中完成初始化的

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

如果mTaskInvoked不为true,则执行postResult; 但是在mWorer初始化时就已经将mTaskInvoked为true,所以一般这个postResult执行不到

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        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;
}

其实执行到最后都是Handler身影的存在。到目前为止都只是介绍了这mWorker和mFuture的实例化而已。

下面说说真正的调用

exec.execute(mFuture);

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

sDefaultExecutor的本质是

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的一个实例,其内部维持一个任务队列;

直接看其execute(Runnable runnable)方法,将runnable放入mTasks队尾; 判断当前mActive是否为空,为空则调用scheduleNext方法 scheduleNext,则直接取出任务队列中的队首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。

看看THREAD_POOL_EXECUTOR是何方神圣?

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

其实就是一个可自定义的线程池。源码中配置的参数如下:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

看到这里,大家就会知道背后原来有一个线程池,且最大支持CPU_COUNT * 2 + 1的线程并发,加上长度为128的阻塞队列,可能会觉得就是在快速调用MAXIMUM_POOL_SIZE+128个以内的AsyncTask子类的execute方法不会出现问题,而大于MAXIMUM_POOL_SIZE+128则会抛出异常。

到此为止,源码分析讲解已经结束,希望大家能对Asynctask有更好的了解。如果你有更好的想法欢迎留言

原文发布于微信公众号 - 猿份到(sparkcliff)

原文发表时间:2017-10-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Spark学习技巧

SparkMLLib中基于DataFrame的TF-IDF

一 简介 假如给你一篇文章,让你找出其关键词,那么估计大部分人想到的都是统计这个文章中单词出现的频率,频率最高的那个往往就是该文档的关键词。实际上就是进行了词...

2367
来自专栏iOSer成长记录

iOS-识别图片中二维码

在iOS的CoreImage的Api中,有一个CIDetector的类,Detector的中文翻译有探测器的意思,那么CIDetector是用来做哪些的呢?它可...

571
来自专栏潇涧技术专栏

Python Algorithms - C3 Counting 101

原书主要介绍了一些基础数学,例如排列组合以及递归循环等,但是本节只重点介绍计算算法的运行时间的三种方法

624
来自专栏null的专栏

优化算法——拟牛顿法之DFP算法

一、牛顿法    image.png image.png 二、DFP拟牛顿法 1、DFP拟牛顿法简介         DFP拟牛顿法也称为DFP校正方法,DFP...

2856
来自专栏人工智能LeadAI

TF使用例子-LSTM实现序列标注

本文主要改写了一下"Sequence Tagging with Tensorflow"(https://link.jianshu.com?t=https://g...

6408
来自专栏Venyo 的专栏

pHash的Java实现

此算法中的DCT变换是从 http://blog.csdn.net/luoweifu/article/details/8214959抄过来的,因为这种需要大量的...

6127
来自专栏计算机视觉life

自识别标记(self-identifying marker) -(5) 用于相机标定的CALTag图案设计

前面介绍了CALTag的工作原理、应用领域。如果我们想在实际项目中应用自识别标记,通常需要根据项目的特点来设计不同尺寸,不同数目,不同排列的图案,那么如何设计属...

1917
来自专栏吉浦迅科技

Tensor Core

Tensor Core,也是Volta架构里面最重磅的特性。 ? Tensor Core实际上是一种矩阵乘累加的计算单元。矩阵乘累加计算在Deep Learn...

4498
来自专栏成长道路

文本型数据的向量化:TF-IDF

1.对于文本型数据的分类处理(或者其他的处理),根据ik和jcseg等分词器先对它们进行分词处理之后,大家都知道,计算机是处理不了汉字的,对于文本型的词我们如何...

2070
来自专栏贾志刚-OpenCV学堂

基于梯度下降算法求解线性回归

基于梯度下降算法求解线性回归 一:线性回归(Linear Regression) 梯度下降算法在机器学习方法分类中属于监督学习。利用它可以求解线性回归问题,计算...

33211

扫码关注云+社区