美文网首页Harmoney
Harmony鸿蒙 使用AsyncTask

Harmony鸿蒙 使用AsyncTask

作者: h2coder | 来源:发表于2021-07-17 13:36 被阅读0次

Android 早期的异步模型是AsyncTask,再到现在的RxJava和Kotlin协程,虽然AsyncTask有很多缺点,但AsyncTask的代码量不多,还是值得研读的。

如果对源码感兴趣,可以看这篇文章,AsyncTask源码分析

预览

前置准备

  • AsyncTask的异步和安卓一样是使用Java线程池,唯一区别就是主线程API不同,但很类似。例如Android中使用Handler、Looper、Message来把异步线程中需要主线程要执行的操作封装成一个Message消息,通过Handler发送到主线程

  • 鸿蒙中有对应的概念,例如EventHandler对应Handler,EventRunner对应Looper,InnerEvent对应Message

  • Android中,通过handler.sendMessgae()
    发送消息到主线程,鸿蒙中,则通过eventHandler.sendEvent(),Android中消息Message对象有一个what属性用来标识消息的类型,而鸿蒙InnerEvent则是通过eventId属性来标识,同样Android的Message对象的obj属性用来存放消息的数据,而鸿蒙InnerEvent则是用object属性

  • Android中获取主线程Looper,是通过Looper.getMainLooper()来获取主线程的Looper,而鸿蒙也是通过EventRunner.getMainEventRunner()来获取主线程的EventRunner

AsyncTask修改

  • 把Android中的AsyncTask源码拷贝到鸿蒙鸿蒙,替换Handler的对应部分即可

全部代码

public abstract class AsyncTask<Params, Progress, Result> {
    private static final String LOG_TAG = "AsyncTask";

    /**
     * 获得当前CPU的核心数
     */
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    /**
     * 设置线程池的核心线程数2-4之间,但是取决于CPU核数
     */
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    /**
     * 设置线程池的最大线程数为 CPU核数*2+1
     */
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    /**
     * 设置线程池空闲线程存活时间30s
     */
    private static final int KEEP_ALIVE_SECONDS = 30;

    /**
     * 线程工厂,统一创建线程并配置
     */
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable runnable) {
            //给每个生成的线程命名
            return new Thread(runnable, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    /**
     * 任务队列,最大容量为128,就是最多支持128个任务并发
     */
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * 任务线程池,static修饰,所以是类共享,所以多个AsyncTask会共享一个线程池
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        //定义线程池,使用sPoolWorkQueue作为队列,如果超出任务执行数量,则抛出RejectedExecutionException
        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 final Executor SERIAL_EXECUTOR = new SerialExecutor();

    /**
     * Handler的消息类型Code,发送结果的消息类型
     */
    private static final int MESSAGE_POST_RESULT = 0x1;
    /**
     * Handler的消息类型Code,更新进度的消息类型
     */
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    /**
     * 默认线程池,默认为串行
     */
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    /**
     * 主线程Handler
     */
    private static InternalHandler sHandler;

    /**
     * 任务实现
     */
    private final WorkerRunnable<Params, Result> mWorker;
    /**
     * 任务
     */
    private final FutureTask<Result> mFuture;

    /**
     * 当前的任务状态
     */
    private volatile AsyncTask.Status mStatus = AsyncTask.Status.PENDING;

    /**
     * 是否取消
     */
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    /**
     * 任务是否执行了
     */
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

    /**
     * 回调Handler
     */
    private final EventHandler mHandler;

    /**
     * 顺序执行器,将线程池的执行包裹,所以即使线程池是有容量的线程池,经过SerialExecutor包裹,都是串行执行,相当于单线程执行
     */
    private static class SerialExecutor implements Executor {
        /**
         * 任务队列,让任务串行
         */
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<>();
        /**
         * 当前执行的任务
         */
        Runnable mActive;

        @Override
        public synchronized void execute(final Runnable runnable) {
            //创建一个代理任务,包裹真正的任务,再将代理任务进队
            mTasks.offer(new Runnable() {
                @Override
                public void run() {
                    try {
                        runnable.run();
                    } finally {
                        //执行完一个任务后,自动执行下一个任务
                        scheduleNext();
                    }
                }
            });
            //第一次执行,马上执行任务
            if (mActive == null) {
                scheduleNext();
            }
        }

        /**
         * 执行下一个任务
         */
        protected synchronized void scheduleNext() {
            //获取下一个任务,如果有则执行
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

    /**
     * 运行状态
     */
    public enum Status {
        /**
         * 等待
         */
        PENDING,

        /**
         * 运行
         */
        RUNNING,

        /**
         * 结束
         */
        FINISHED,
    }

    /**
     * 获取主线程Handler
     */
    private static EventHandler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(EventRunner.getMainEventRunner());
            }
            return sHandler;
        }
    }

    private EventHandler getHandler() {
        return mHandler;
    }

    /**
     * 外部配置任务执行器,可外部定制
     *
     * @param exec 执行器
     */
    public static void setDefaultExecutor(Executor exec) {
        sDefaultExecutor = exec;
    }

    //-------------------- AsyncTask构造方法 start --------------------

    /**
     * 空参构造,取主线程的Handler回调
     */
    public AsyncTask() {
        this((EventRunner) null);
    }

    /**
     * 指定回调的Handler
     */
    public AsyncTask(EventHandler handler) {
        this(handler != null ? handler.getEventRunner() : null);
    }

    /**
     * 可指定消息轮训器
     *
     * @param callbackRunner 轮训器
     */
    public AsyncTask(EventRunner callbackRunner) {
        //没指定,或者指定为主线程的轮训器,则构造主线程的Handler,否则使用指定的轮训器创建
        mHandler = callbackRunner == null || callbackRunner == EventRunner.getMainEventRunner()
                ? getMainHandler()
                : new EventHandler(callbackRunner);
        //任务的具体执行体,WorkerRunnable实现了Callable接口,就是说可以返回结果的任务
        mWorker = new WorkerRunnable<Params, Result>() {
            @Override
            public Result call() throws Exception {
                //标识任务为已经执行了
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //执行任务,并获取执行结果
                    result = doInBackground(mParams);
                } catch (Throwable throwable) {
                    //抛出了异常,设置任务已经被取消
                    mCancelled.set(true);
                    throw throwable;
                } finally {
                    //发送结果
                    postResult(result);
                }
                //返回结果
                return result;
            }
        };
        //将WorkerRunnable作为FutureTask任务去执行
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                //任务结束,任务结束了,call还没有被调用
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    //被取消时会抛出异常
                    e.printStackTrace();
                    HiLog.error(new HiLogLabel(HiLog.LOG_APP, 0, LOG_TAG), e.getMessage());
                } catch (ExecutionException e) {
                    //执行发生异常异常
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    //取消失败
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

    //-------------------- AsyncTask构造方法 end --------------------

    /**
     * 发送任务执行结果,如果没有发送过则发送
     */
    private void postResultIfNotInvoked(Result result) {
        //这里有点奇怪,这个方法调用在FutureTask的done方法,而mTaskInvoked标志在call()回调时就会设置为true
        //所以后面的if判断一直都是进不去的,感觉这个方法是多余的,除非call方法执行还没到mTaskInvoked设置成功之前就抛出了异常
        //这个if判断才成立
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

    /**
     * 发送任务执行结果到主线程进行回调
     */
    private Result postResult(Result result) {
        EventHandler handler = getHandler();
        InnerEvent event = InnerEvent.get(MESSAGE_POST_RESULT, new AsyncTaskResult<>(this, result));
        handler.sendEvent(event);
        return result;
    }

    /**
     * 获取任务运行状态
     */
    public final AsyncTask.Status getStatus() {
        return mStatus;
    }

    /**
     * 执行前回调
     */
    protected void onPreExecute() {
    }

    /**
     * 任务执行回调(子线程执行)
     *
     * @param params 任务参数
     * @return 任务结果
     */
    protected abstract Result doInBackground(Params... params);

    /**
     * 执行后回调
     *
     * @param result 执行结果
     */
    @SuppressWarnings({"UnusedDeclaration"})
    protected void onPostExecute(Result result) {
    }

    /**
     * 进度更新
     *
     * @param values 进度
     */
    @SuppressWarnings({"UnusedDeclaration"})
    protected void onProgressUpdate(Progress... values) {
    }

    /**
     * 任务已经执行完毕了,却发现被取消,则回调,并带上结果
     *
     * @param result 结果
     */
    @SuppressWarnings({"UnusedParameters"})
    protected void onCancelled(Result result) {
        onCancelled();
    }

    /**
     * 任务未执行完,被取消,则回调
     */
    protected void onCancelled() {
    }

    /**
     * 是否被取消了
     */
    public final boolean isCancelled() {
        return mCancelled.get();
    }

    /**
     * 取消任务
     *
     * @param mayInterruptIfRunning 是否强制取消,不等待执行完毕后再取消(马上打断)
     */
    public final boolean cancel(boolean mayInterruptIfRunning) {
        //设置任务取消的标志
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }

    /**
     * 获取执行结果
     *
     * @return 执行结果
     */
    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

    /**
     * 获取结果,并设定超时,如果超时还没获取到结果,则抛出异常
     *
     * @param timeout 超时时间
     * @param unit    超时时间单位
     */
    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return mFuture.get(timeout, unit);
    }

    /**
     * 执行任务
     *
     * @param params 执行参数
     */
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    /**
     * 提供执行器,执行任务
     *
     * @param exec   执行器,可以设置为内部的THREAD_POOL_EXECUTOR来实现并行,否则默认使用串行的执行器
     * @param params 任务参数
     */
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
        //处理状态,RUNNING正在执行不允许再调用执行,FINISHED已经结束了也不允许执行,所以AsyncTask只允许执行一次
        if (mStatus != AsyncTask.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)");
                default:
                    break;
            }
        }
        //切换状态为运行中
        mStatus = AsyncTask.Status.RUNNING;
        //任务执行前,回调,onPreExecute是直接调用的,所以如果在子线程中调用AsyncTask的execute
        //onPreExecute的回调也是在子线程!
        onPreExecute();
        //配置任务参数
        mWorker.mParams = params;
        //将任务交给线程池执行
        exec.execute(mFuture);
        return this;
    }

    /**
     * 执行Runnable
     */
    public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
    }

    /**
     * 更新进度,需要手动调用
     *
     * @param values 进度
     */
    protected final void publishProgress(Progress... values) {
        //没有取消才更新
        if (!isCancelled()) {
            EventHandler handler = getHandler();
            InnerEvent event = InnerEvent.get(MESSAGE_POST_PROGRESS, new AsyncTaskResult<>(this, values));
            handler.sendEvent(event);
        }
    }

    /**
     * 结束任务
     *
     * @param result 结果
     */
    private void finish(Result result) {
        //结束任务时,如果被标记取消,则回调取消
        if (isCancelled()) {
            onCancelled(result);
        } else {
            //获取到结果了,并且没有取消,则回调任务结束
            onPostExecute(result);
        }
        //设置状态为结束
        mStatus = AsyncTask.Status.FINISHED;
    }

    /**
     * 内部Handler,负责从子线程中发送消息会主线程进行方法回调
     */
    private static class InternalHandler extends EventHandler {
        InternalHandler(EventRunner runner) {
            super(runner);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) event.object;
            switch (event.eventId) {
                //主线程通知,任务结果,获取到结果
                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;
                default:
                    break;
            }
        }
    }

    /**
     * 任务
     *
     * @param <Params> 参数类型
     * @param <Result> 结果类型
     */
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        /**
         * 任务参数
         */
        Params[] mParams;
    }

    /**
     * 任务执行结果包裹类,包装结果作为Handler发送的数据
     *
     * @param <Data> 任务结果类型
     */
    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        /**
         * @param task 任务对象
         * @param data 执行结果
         */
        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
}

简单使用

例子:通过AsyncTask加载一张网络图片,以及通过AsyncTask请求玩安卓的一个Get请求接口

public class MainAbilitySlice extends AbilitySlice {
    private Image vImage;
    private Button vLoadImg;
    private Button vNativeNetRequest;
    private Button vOkHttpNetRequest;

    private OkHttpClient mOkHttpClient;
    private ShapeLoadingDialog mLoadingDialog;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        mOkHttpClient = new OkHttpClient.Builder().build();
        findView();
        bindView();
    }

    private void findView() {
        vImage = (Image) findComponentById(ResourceTable.Id_image);
        vLoadImg = (Button) findComponentById(ResourceTable.Id_load_img);
        vNativeNetRequest = (Button) findComponentById(ResourceTable.Id_native_net_request);
        vOkHttpNetRequest = (Button) findComponentById(ResourceTable.Id_okhttp_net_request);
    }

    private void bindView() {
        vLoadImg.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component v) {
                String url = "https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1565684573&di=82491d3ea2a4d5195d6f8bd90eba1953&src=http://image.coolapk.com/picture/2016/1210/459462_1481302685_5118.png.m.jpg";
                DownloadImageTask task = new DownloadImageTask(mOkHttpClient, new Callback<PixelMap>() {
                    @Override
                    public void onStart() {
                        vImage.setPixelMap(null);
                        showLoading();
                    }

                    @Override
                    public void onFinish(PixelMap pixelMap) {
                        hideLoading();
                        if (pixelMap != null) {
                            vImage.setPixelMap(pixelMap);
                        } else {
                            toast("下载失败");
                        }
                    }
                });
                task.execute(url);
            }
        });
        vNativeNetRequest.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                HttpURLConnectionNetRequestTask task = new HttpURLConnectionNetRequestTask(new Callback<String>() {
                    @Override
                    public void onStart() {
                        showLoading();
                    }

                    @Override
                    public void onFinish(String result) {
                        hideLoading();
                        if (!TextUtils.isEmpty(result)) {
                            HiLogUtil.info(result);
                            toast(result);
                        } else {
                            toast("网络请求失败");
                        }
                    }
                });
                task.execute("https://wanandroid.com/article/listproject/0/json");
            }
        });
        vOkHttpNetRequest.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                OkHttpNetRequestTask task = new OkHttpNetRequestTask(mOkHttpClient, new Callback<String>() {
                    @Override
                    public void onStart() {
                        showLoading();
                    }

                    @Override
                    public void onFinish(String result) {
                        hideLoading();
                        if (!TextUtils.isEmpty(result)) {
                            HiLogUtil.info(result);
                            toast(result);
                        } else {
                            toast("网络请求失败");
                        }
                    }
                });
                task.execute("https://wanandroid.com/article/listproject/0/json");
            }
        });
    }

    private static class DownloadImageTask extends AsyncTask<String, Void, PixelMap> {
        private final OkHttpClient mClient;
        private final Callback<PixelMap> mCallback;

        public DownloadImageTask(OkHttpClient client, Callback<PixelMap> callback) {
            mClient = client;
            mCallback = callback;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mCallback.onStart();
        }

        @Override
        protected PixelMap doInBackground(String... urls) {
            try {
                String url = urls[0];
                Request request = new Request.Builder()
                        .url(url)
                        .build();
                Call call = mClient.newCall(request);
                Response response = call.execute();
                ResponseBody body = response.body();
                if (body == null) {
                    return null;
                }
                ImageSource imageSource = ImageSource.create(body.byteStream(), null);
                return imageSource.createPixelmap(null);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(PixelMap pixelMap) {
            super.onPostExecute(pixelMap);
            mCallback.onFinish(pixelMap);
        }
    }

    private static class HttpURLConnectionNetRequestTask extends AsyncTask<String, Void, String> {
        private final Callback<String> mCallback;

        public HttpURLConnectionNetRequestTask(Callback<String> callback) {
            this.mCallback = callback;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mCallback.onStart();
        }

        @Override
        protected String doInBackground(String... params) {
            return HttpUtil.httpGet(params[0]);
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            mCallback.onFinish(result);
        }
    }

    private static class OkHttpNetRequestTask extends AsyncTask<String, Void, String> {
        private final OkHttpClient mClient;
        private final Callback<String> mCallback;

        public OkHttpNetRequestTask(OkHttpClient client, Callback<String> callback) {
            this.mClient = client;
            this.mCallback = callback;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mCallback.onStart();
        }

        @Override
        protected String doInBackground(String... params) {
            try {
                String url = params[0];
                Request request = new Request.Builder()
                        .url(url)
                        .build();
                Call call = mClient.newCall(request);
                Response response = call.execute();
                ResponseBody body = response.body();
                if (body == null) {
                    return null;
                }
                return body.string();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            mCallback.onFinish(result);
        }
    }

    /**
     * 回调接口
     */
    public interface Callback<T> {
        /**
         * 执行前回调
         */
        void onStart();

        /**
         * 执行后回调
         *
         * @param result 执行结果
         */
        void onFinish(T result);
    }

    @Override
    protected void onStop() {
        super.onStop();
        hideLoading();
        mLoadingDialog = null;
    }

    private void showLoading() {
        if (mLoadingDialog == null) {
            mLoadingDialog = new ShapeLoadingDialog.Builder(this)
                    .loadText("加载中...")
                    //点击返回键时是否消失Dialog
                    .cancelable(true)
                    //点击Dialog外部时是否消失Dialog
                    .canceledOnTouchOutside(false)
                    .build();
        }
        if (!mLoadingDialog.isShowing()) {
            mLoadingDialog.show();
        }
    }

    private void hideLoading() {
        if (mLoadingDialog != null && mLoadingDialog.isShowing()) {
            mLoadingDialog.hide();
        }
    }

    private void toast(String msg) {
        new ToastDialog(getContext()
                .getApplicationContext())
                .setText(msg)
                .setAlignment(LayoutAlignment.CENTER)
                .show();
    }
}

相关文章

网友评论

    本文标题:Harmony鸿蒙 使用AsyncTask

    本文链接:https://www.haomeiwen.com/subject/abkwpltx.html