Android AsynTask

作者: Young_Allen | 来源:发表于2018-11-09 08:07 被阅读28次

每天一篇系列:
强化知识体系,查漏补缺。
欢迎指正,共同学习!

AsynTask是Android提供的一个轻量级异步任务类。
比如写一个异步下载应用APK的AsynTask,源码如下:

class ApkDownloadTask extends AsyncTask<String, Integer, Boolean>{
        
        private String filepath;
        private String packagename;
        
        public ApkDownloadTask(String filepath,String packagename){
            this.filepath = filepath;
            this.packagename = packagename;
        }
        
        @Override
        protected Boolean doInBackground(String... params) {
            // TODO Auto-generated method stub
            publishProgress(0);
            HttpURLConnection conn = null;
            String url;
            InputStream stream = null;
            FileOutputStream fileOutputStream = null;
            File file = null;
            try {
                url = params[0];
                file = new File(filepath);
                if (file.exists()) {
                    file.delete();
                }
                fileOutputStream = new FileOutputStream(file);
                conn = HttpUtil.createDownloadConnection(url, 0);
                byte[] bytes = new byte[1024];
                if (conn == null) {
                    return false;
                }
                int contentLen = conn.getContentLength();
                stream = conn.getInputStream();
                int readLen = 0;
                int downloadsize = 0;
                int index = 0;
                while (true) {
                    readLen = stream.read(bytes);
                    downloadsize += readLen;
                    if(readLen <= 0){
                        break;
                    }
                    fileOutputStream.write(bytes, 0, readLen);
                    fileOutputStream.flush();
                    //20KB update UI
                    if((index%20) == 0){
                        publishProgress((int)((downloadsize*100.0f)/contentLen));
                    }
                    index++;
                }
                publishProgress(100);  
            }catch (Exception e) {
                DebugUtil.Debug(DebugUtil.DEBUG_TVPosterCell,TAG, e.toString());
                return false;
            }finally {
                try {
                    if (stream != null) {
                        stream.close();
                    }
                    if (conn != null) {
                        conn.disconnect();
                    }
                    if (fileOutputStream != null) {
                        fileOutputStream.close();
                    }
                } catch (IOException e) {
                    DebugUtil.Debug(DebugUtil.DEBUG_TVPosterCell,TAG, e.toString());
                    return false;
                }
            }
            return true;
        }
        
        @Override
        protected void onProgressUpdate(Integer... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);
            DownloadChangeEvent(values[0],true);
        }
        
        @Override
        protected void onPostExecute(Boolean result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            if(!result){
                Intent intent   = new Intent();
                intent.setAction("com.blinds.launcher.download.fail");
                mContext.sendBroadcast(intent);
                DownloadChangeEvent(0,false);
                return;
            }
            //2.install
            FileUtil.chmodFile(filepath);
            //AppUtil.getInstance(mContext).installApk(filepath);
            AppUtil.getInstance(mContext).installApkPeaceful(filepath,packagename, new AppUtil.ApkInstallObserver(){
                //delect the apk when install callback ? or delect files when aplication init
                @Override
                public void packageInstalled(String packagename,int resultcode){
                    try {
                        super.packageInstalled(packagename, resultcode);
                        if(resultcode == PackageManager.INSTALL_SUCCEEDED){
                            DebugUtil.Debug(DebugUtil.DEBUG_TVPosterCell,TAG, "-------------->postercell install apk sucess:"+packagename);
                            //3.start apk
                            //doJump2APK();
                            //toast install success
                            Intent intent   = new Intent();
                            intent.setAction("com.blinds.launcher.install.success");
                            mContext.sendBroadcast(intent);
                            return;
                        }
                    } catch (Exception e) {
                        DebugUtil.Debug(DebugUtil.DEBUG_TVPosterCell,TAG, "-------------->packageInstalled :"+e);
                    }
                    DebugUtil.Debug(DebugUtil.DEBUG_TVPosterCell,TAG, "-------------->postercell install apk fail:"+packagename+" return code:"+resultcode);
                    //toast install fail
                    Intent intent   = new Intent();
                    intent.setAction("com.blinds.launcher.install.fail");
                    mContext.sendBroadcast(intent);
                }
            });
        }
    }

其中,doInBackground在新的线程中作耗时任务(比如说下载文件),并且还可以通过publishProgress随时通知下载进度,而onPostExecute是在UI线程中回调的,因此可以处理UI事件。
启动AsynTask代码如下:

ApkDownloadTask downloadTask = new ApkDownloadTask(apkFilePath, packageName);
downloadTask.execute(apk_downloadurl);

既然知道AsynTask如何使用了,那么从源码的角度来看一看AsynTask是如何实现的。

public abstract class AsyncTask<Params, Progress, Result> {
    ...
    private static final InternalHandler sHandler = new InternalHandler();

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

    private volatile Status mStatus = Status.PENDING;
    
    //原子操作
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
    ...
    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
    ...
    private static class InternalHandler extends Handler {
        @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;
            }
        }
    }
    ...
}

在AsyncTask的构造函数中创建了两个任务类WorkerRunnable、FutureTask,并且AsyncTask内部创建了一个Handler,注意这个Handler并没有使用其他线程的Looper,而是使用了创建AsyncTask的线程的Looper,也就是说如果在UI线程创建了AsyncTask,那么这个Handler的Looper还是UI线程的Looper。

WorkerRunnable实现了Callable的接口,是用来实际执行doInBackground任务,并且把执行结果通过Handler转发:

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

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

doInBackground是抽象方法,利用继承的特性,子类继承该方法后,可以完成耗时操作的处理。
FutureTask实现Future,Runnable类,充当Runnable的角色,执行异步操作的功能。

    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }
    private void postResultIfNotInvoked(Result result) {
        //因为是原子操作,保证任务的安全性
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

以上只是粗略的了解AsyncTask相关属性和方法。
在应用中使用AsyncTask有以下几种方式execute,我们分别来分析:

    public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
    }

在AsyncTask中有一个默认的线程池:

    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    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);
            }
        }
    }

线程池通过ThreadPoolExecutor创建,SerialExecutor实现了Executor接口,对内部方法添加了synchronized,把新的Runnable任务每次添加到队列尾,每次把队列中的首个任务放置到线程池中执行,因此实际上默认任务是串行执行的。
而另外的方法如下:

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

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

实现上作了状态安全检查,另外回调了onPreExecute(),注意这里还是在创建者线程内,所以如果在UI线程创建的AsyncTask,那么onPreExecute()就是在UI线程中,子类就可以继承onPreExecute()并处理需要做的事情。而这里把参数params赋值给WorkerRunnable,把FutureTask作为了SerialExecutor的参数,也就是说会把FutureTask丢入到线程池中,可以看到WorkerRunnable是FutureTask实际需要执行的任务,根据WorkerRunnable的定义,doInBackground是在新的工作线程中处理的,因此不会阻塞主线程,实现异步处理。

再看看publishProgress的处理:

    protected final void publishProgress(Progress... values) {
        //这里有一个原子操作的处理
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

直接使用了工作线程的Handler发送消息:

    private static class InternalHandler extends Handler {
        @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;
            }
        }
    }

AsyncTaskResult是AsyncTask的内部类,用来保存Task任务和与之对应的数据,而在发消息时指向的mTask都是this:

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

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

所以根据消息任务的处理,可以看到调用publishProgress会通过Handler中转再回调onProgressUpdate,子类在继承AsyncTask重写onProgressUpdate就可以获得进度信息。
而耗时任务结束后会通过Handler中转再回调finish方法:

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

根据状态的检查,如果是用户cancle的,将会回调onCancelled方法,而正常结束的处理将会回调onPostExecute方法,因为都是在Handler内部回调的,因此onProgressUpdate、onCancelled和onPostExecute都在工作在创建AsyncTask线程,所以通常我们在UI线程创建了AsyncTask,在onProgressUpdate、onCancelled和onPostExecute都可以处理UI控件的变化。

相关文章

  • Android 线程和线程池

    一,Android中的线程形态 AsyncTask AsynTask在具体的使用中的限制(1)AsynTask的类...

  • Android AsynTask

    每天一篇系列:强化知识体系,查漏补缺。欢迎指正,共同学习! AsynTask是Android提供的一个轻量级异步任...

  • 【Android 基础】AsynTask 异步任务

    AsynTask 异步任务 Android机制,不允许子线程更新UI界面,耗时操作需要开辟新的Thread执行; ...

  • AsynTask

    AsyncTask为什么要设计为只能够一次任务? 因为要同一个实例执行多次很麻烦,没必要。 假设允许多次 exec...

  • AsynTask源码分析

    AsynTask介绍 AsyncTask是一个抽象类,有三个泛型参数,四个方法。 Params需要传入的参数 Pr...

  • 2019.6 android面试总结

    hander,AsynTask,okhttp,retrofit原理,尤其是okhttp的几个拦截器作用 strin...

  • Android常见的多线程

    1.继承Thread类; 2.实现Runnable接口。 3.Handler; 4.AsynTask; 5.Han...

  • Handler分析和AsynTask分析

    因为线程简通信经常用到Handler所以这里分析一下,顺便说一下AsynTask。 先说说两个概念: 进程 进程操...

  • android中的异步机制(Handler、Looper、Mes

    关系 AsynTask是获得后台线程的简单方式,但不适合重复且长时间运行的任务。 线程使用的收件箱叫做消息队列(M...

  • 流行框架源码分析(2)-AsynTask源码分析

    主目录见:Android高级进阶知识(这是总目录索引) 国庆的假期刚刚过去,今天就用一篇比较简单的文章来收收心,A...

网友评论

    本文标题:Android AsynTask

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