美文网首页
AsyncTask分析

AsyncTask分析

作者: Gavin_2020 | 来源:发表于2020-10-30 17:36 被阅读0次

    简介

    Android中只能在主线程中进行UI操作,如果是其它子线程,需要借助异步消息处理机制Handler。除此之外,还有个非常方便的AsyncTask类,这个类内部封装了Handler和线程池。本文先简要介绍AsyncTask的用法,然后分析具体实现。

    基本用法

    AsyncTask是一个抽象类,我们需要创建子类去继承它,并且重写一些方法。AsyncTask接受三个泛型参数:
    -1. Params: 指定传给任务执行时的参数的类型
    -2. Progress: 指定后台任务执行时将任务进度返回给UI线程的参数类型
    -3. Result: 指定任务完成后返回的结果的类型
    除了指定泛型参数,还需要根据需要重写一些方法,常用的如下:
    -4. onPreExecute(): 这个方法在UI线程调用,用于在任务执行前做一些初始化操作,如在界面上显示加载进度控件
    -5. doInBackground: 在onPreExecute()结束之后立刻在后台线程调用,用于耗时操作。在这个方法中可调用publishProgress方法返回任务的执行进度
    -6. onProgressUpdate: 在doInBackground调用publishProgress后被调用,工作在UI线程
    -7. onPostExecute: 后台任务结束后被调用,工作在UI线程

    源码分析

    下面分析这个类的实现,主要有线程池以及Handler两部分。
    线程池
    当执行一个AsyncTask的时候调用的是execute()方法,就从这个开始看:

    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
        onPreExecute();  
      
        mWorker.mParams = params;  
      
        exec.execute(mFuture);  
        return this; 
    } 
    

    execute方法会调用executeOnExecutor。在这个方法中先检查任务是否已经执行或者执行结束,然后把任务标记为running。最开始执行的是onPreExecute,接着把参数赋值给mWorker对象。这个mWorker是一个Callable对象,最终被包装为FutureTask,代码如下:

    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
        Params[] mParams;  
    } 
    
    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);  
            }  
        }  
    }; 
    

    从上面的代码可以看出,在mWorker对象中的call()方法会调用doInbackground,返回值交给postResult方法,这个方法通过Handler发送消息,这一点稍后再详细分析。
    在mWorker对象被封装成FutureTask之后交由线程池执行,从execute方法可以看出,使用的是sDefaultExecutor,它的值默认为SERIAL_EXECUTOR,也就是串行执行器,实现如下:

     private static class SerialExecutor implements Executor {  
        //线性双向队列,用来存储所有的AsyncTask任务  
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  
        //当前正在执行的AsyncTask任务  
        Runnable mActive;  
    
        public synchronized void execute(final Runnable r) {  
            //将新的AsyncTask任务加入到双向队列中  
            mTasks.offer(new Runnable() {  
                public void run() {  
                    try {  
                        //执行AsyncTask任务  
                        r.run();  
                    } finally {  
                        //当前任务执行结束后执行下一个任务
                        scheduleNext();  
                    }  
                }  
            });    
            if (mActive == null) {  
                scheduleNext();  
            }  
        }  
    
        protected synchronized void scheduleNext() {  
            //从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行  
            if ((mActive = mTasks.poll()) != null) {  
                THREAD_POOL_EXECUTOR.execute(mActive);  
            }  
        }  
    }
    public static final Executor THREAD_POOL_EXECUTOR  
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,  
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);    
    

    在上面的代码中,如果有任务执行,那么SerialExecutor的execute方法会被调用,它的逻辑是把Runnable对象加入ArrayDeque队列中,然后判断mActivie是否为空。第一次执行时mActive当然为空,所以执行scheduleNext,其实就是取出任务队列中的第一个任务交给线程池(THREAD_POOL_EXECUTOR)执行。加入mTask队列的Runnable对象的run方法里最终一定会调用scheduleNext,那么又会从任务队列中取出队头任务执行。这样便实现了单线程顺序执行任务,所以在AsyncTask中默认启用的是单线程执行,只有上一个任务执行后才会执行下一个任务。如果想要启用多线程执行任务,可以直接调用 executeOnExecutor(Executor exec, Params... params),这里的Executor参数可以使用AsyncTask自带的THREAD_POOL_EXECUTOR,也可以自己定义。
    Handler
    AsyncTask内部用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;  
            }  
        }  
    } 
    

    如果消息类型是任务执行后的返回值(MESSAGE_POST_RESULT)将调用finish()方法:

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

    从上面可以知道,如果任务取消了,将调用onCancelled,否则调用onPostExecute,所以一个AsyncTask任务如果取消了,那么onPostExecute将不会得到执行。
    如果消息类型是执行进度(MESSAGE_POST_PROGRESS)将调用onProgressUpdate,这个方法默认是空方法,我们可以根据自己的需要重写。
    总结
    AsyncTask的主要逻辑就如上面所分析的,总结几个需要注意的地方:
    ·AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)
    ·AsyncTask对象必须在UI线程创建
    ·execute方法必须在UI线程调用
    ·不要手动调用onPreExecute()、doInBackground、onProgressUpdate方法
    ·一个任务只能被调用一次(第二次调用会抛出异常)
    ·多个 AsyncTask 默认是串行执行,可以改为并发执行,但要注意资源同步的问题。
    ·大量 AsyncTask 任务填满线程池的队列会抛出异常。

    相关文章

      网友评论

          本文标题:AsyncTask分析

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