AysncTask必须在UI线程中创建吗?

一、说明

我们知道AysncTask是Android提供的给我们进行异步操作的类,我们可以利用这个类在子线程中执行耗时操作,并将结果更新到UI线程,网上很多说法是AysncTask必须在UI线程中创建并执行器execute方法,其实我觉得这个说法不太对。

二、源码分析

AsyncTask具体源码可以参考:https://mp.csdn.net/postedit/81234625

这里仅仅说一些和本篇内容有关的代码:

    public AsyncTask() {
        this((Looper) null);
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }

    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper); // 最终都是调用这句来创建Handler
        ...
    }

如果我们调用无参的构造函数,即传入的Looper默认为空,则mHandler将会赋值为getMainHandler()方法返回的值:

    private static InternalHandler sHandler;    
    
    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

通过单例模式创建一个InternalHandler对象,并且传入的Looper为主线程的Looper,InternalHandler中定义了处理消息的逻辑,主要会调用AsyncTask的onProgressUpdate、onPostExecute方法回调更新进度和结果:

    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
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

因为InternalHandler对象构造时传入的Looper是主线程的Looper,所以其处理消息会在主线程中处理,即AsyncTask的onProgressUpdate、onPostExecute会在主线程中调用,从这里也可以看出,我们可以在子线程中创建AysncTask对象,再来看一下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) {
   
        ...
        onPreExecute(); // 回调onPreExecute方法

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

execute方法主要有两个操作:

1、回调onPreExecute()方法;

2、通过线程池执行任务;

一般的,我们会在onPreExecute方法中执行异步任务开始前的操作,从代码中可以看出,这个方法是在调用execute方法的线程中执行的,所以,如果execute方法是在子线程中执行的,onPreExecute方法也会在子线程中执行,不能进行UI相关的操作,从这个角度来说,我们不能在非UI线程中调用execute方法开始异步任务,有两种情况例外:

1、在onPreExecute方法中不需要进行UI相关的操作;

2、如果在onPreExecute方法中需要进行UI相关的操作,可以通过Handler将其切换到主线程执行。

请看如下代码:

   new Thread() {
                    @Override
                    public void run() {
                        String imagePath = "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1641460231,985790943&fm=27&gp=0.jpg";
                        mDownloadImageAsyncTask = new DownloadImageAsyncTask();
                        mDownloadImageAsyncTask.execute(imagePath);
                    }
                }.start();
    public class DownloadImageAsyncTask extends AsyncTask<String, Integer, byte[]> {
        // 在UI线程中执行,任务开始前需要进行的UI操作,比如弹出加载框
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            runOnUiThread(new Runnable() { // 在非UI线程中通过Handler更新UI
                @Override
                public void run() {
                    if (mProgressDialog == null) {
                        mProgressDialog = new ProgressDialog(AsyncTaskTestActivity.this);
                        mProgressDialog.setTitle("提示信息");
                        mProgressDialog.setMessage("正在下载中,请稍后......");
                        // 设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失
                        mProgressDialog.setCancelable(false);
                        // 设置ProgressDialog样式为水平的样式
                        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                    }
                    mProgressDialog.show();
                }
            });
        }
        ...    
    }

我们在子线程中创建了AysncTask实例并调用其execute方法,AysncTask的onPostExecute方法、onProgressUpdate等方法还是会在UI线程中调用,这是因为创建InternalHandler对象传入的是主线程的Looper;但是onPreExecute方法会在调用execute方法的线程中调用,所以在onPreExecute方法中如果要更新UI,需要通过Handler来完成。

三、结论

AsyncTask在某些情况下可以在子线程中执行execute,在子线程中执行execute并不会影响onPostExecute方法、onProgressUpdate等方法的执行线程,但是会影响onPreExecute方法的执行线程,所以如果我们不需要在onPreExecute方法中更新UI,或者将UI相关的操作通过Handler交给主线程处理,我们是可以在子线程中执行execute方法的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值