当前位置: 首页 > 工具软件 > AsynTask > 使用案例 >

AsynTask总结

何海
2023-12-01

除了我们常用的Handler进行耗时操作,最后更新到UI线程中,虽然Handler比较简单,但是如果有多个任务同时执行代码就会增多。所以我们可以使用一下AsynTask,它使得异步任务实现起来更加简单。

首先我们介绍一下如何使用:

我在这里实现了一个进度条的加载

//首先需要继承AsynTask这个抽象类,并指定三个参数类型
class MyTask extends AsyncTask<String,Integer,String>{
        //执行线程任务前的操作
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            textView.setText("加载中");
        }
        //耗时操作
        @Override
        protected String doInBackground(String... strings) {
            try {
                int count=0;
                int length=1;
                while (count<99){
                    count+=length;
                    //调用该方法显示进度,之后将执行onProgressUpdate
                    publishProgress(count);
                    Thread.sleep(50);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            progressBar.setProgress(values[0]);
            textView.setText("loading..."+values[0]);
        }
        //执行完毕返回结果
        @Override
        protected void onPostExecute(String s) {
            textView.setText("加载完毕");
        }
        //取消任务
        @Override
        protected void onCancelled() {
            textView.setText("已取消");
            progressBar.setProgress(0);
        }
    }

然后调用execute()时必须在主线程中调用:

myTask=new MyTask();

接下来我们看一看AsynTask源码吧!

public abstract class AsyncTask<Params, Progress, Result> {
    ...
}

三个参数:

Params:参数类型,一般就是我们需要输入的参数类型

Progress:任务执行进度的参数

Result:返回结果的类型

如果不需要其中的某个参数可将其设为void类型。

我们一般要实现的方法(这几个方法在源码中都是空实现):

onPreExecute():任务执行前的准备工作,在主线程执行,则可以进行UI操作。

doInBackground(Params...params):准备好之后开始进行的耗时任务操作,在线程池中执行,在执行中通过调用publishProgress(Progress...values)来进行更新进度。

onProgressUpdate(Progress...values)在主线程中执行,当调用publishProgress方法时会将进度更新到UI组件中。

onPostExecute(Result result):后台任务执行完毕,得到doInBackground中的结果,在主线程执行,能够进行UI操作。

onCancelled在主线程执行,当需要取消异步任务时调用,需要在ondoInBackground去判断状态

 

AsynTask在3.0之前的版本与3.0之后的版本有较大改动,我们需要知道主要改动在哪。


private static final int CORE_POOL_SIZE = 5;  
private static final int MAXIMUM_POOL_SIZE = 128;  
private static final it KEEP_ALIVE = 10;  
……  
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
        MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);  

Android3.0之前的版本中核心线程数为5,最大线程数为128,非核心线程空闲等待新任务最长时间为10s。

    private static final int CORE_POOL_SIZE = 1;
    private static final int MAXIMUM_POOL_SIZE = 20;
    private static final int BACKUP_POOL_SIZE = 5;
    private static final int KEEP_ALIVE_SECONDS = 3;

我现在的版本中只有一个核心线程,最大线程数为20,备份池大小为5,非核心线程空闲等待时间为3s。

我么看一下AsynTask的构造函数:

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

public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        //关键代码1
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };
        //关键代码2
        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);
                }
            }
        };
    }

关键代码1:WorkerRunnable实现了Callable接口,并实现了call方法,在call中调用了doInBackground(mParams)处理任务得到结果,最终调用postResult提交结果。

关键代码2:FutureTask是一个可管理的异步任务,实现了Runnable和Future这两个接口。因此可以包装Runnable和Callable,并提供给Executor执行。在这里WorkerRunnable作为参数传递给FutureTask,这两个变量会保存到内存中,之后会用到。

 

在执行时我们在主线程调用execute()方法:

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    //这里传入sDefaultExecutor跟params
    return executeOnExecutor(sDefaultExecutor, params);
}

@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;
        //将mFuture传入
        exec.execute(mFuture);

        return this;
    }

在实际执行中先将状态变成RUNNING,之后调用onProExecute(),onProExecute中到底做了什么?,并将Params赋值给WorkerRunnable中。之前我们知道WorkerRunnable又传递给FutureTask,因此参数被封装到FutureTask中。之后会调用exec.execute(mFuture),exec又是什么?

首先我们看一下onProExecute中的源码:

@MainThread
protected void onPreExecute() {
}

是一个空实现的方法,这就需要我们重写该方法,并实现准备的操作,运行在主线程,可进行UI操作。

我们在exectueOnExecutor()中传入了sDefaultExecutor和params。

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//sDefaultExecutor实际就是SerialExecutor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();


private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //关键代码1
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        //关键代码2
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

SerialExector是一个串行的线程池,当调用exectue方法时会将FutureTask加入到mTask中,当前任务执行完或者当前没有任务时会执行scheduleNext方法,会将任务交给THREAD_POOL_EXECUTOR来处理。

关键代码2处执行了FutureTask的run方法,最终会调用WorkerRunnable的call方法,call又会最终调用postResult。

    public void run() {
        ...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    //c实际上就是WorkerRunnable
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

可以看出FutureTask中的run就是调用传进来的WorkerRunnable的call方法,我们在AsynTask的构造函数中可以看到call中最后有调用的是postResult。

private final Handler mHandler;


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

private Handler getHandler() {
    return mHandler;
}

似乎看到了点Handler的影子,没错,AsyTask内部通过创建消息,并将结果赋值给Message,通过getHandler得到Handler对象,并通过sendToTarget发送消息。

其实在上面构造函数代码中就已经初始化了Handler:

//构造函数中获取mHandler
//如果callbackLooper为null或者==Looper.getMainLooper就直接获取主线程Handler,否则会重新创建
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

//获取主线程的Handler
private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                //关键代码
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

一般情况下都是调用的getMainHandler(),内部实际创建的是InternalHandler()。

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

在AsynTask中定义了一个私有内部类用来处理消息。接收到MESSAGE_POST_RESULT之后会调用AsynTask的finish()方法。

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

如果AsynTask任务被取消了则会调用onCancelled方法,否则就调用onPostExecute。最后我们就是同个onPostExectue获取结果并进行更新UI操作。

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

在AsynTask中内部有两个线程池,一个就是之前我们说到的SerialExecutor,一般默认情况下都会使用这个线程池,使用时同一时间只能执行一个任务,一个任务执行完之后才会执行另一个任务。它使用了ArrayQueue队列,如果我们一次性启动很多任务,就会调用队列中的offer方法将任务加入到队列的尾部,并判断mActive是否为null,第一次运行时为null会执行scheduleNext,在finally中也调用了sceduleNext,这样就保证了每次执行完一个任务下一个任务才执行,该线程主要是将任务进行排列。

我么看一下scheduleNext源码:

protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
实际上使用的THREAD_POOL_EXECUTOR
    private static final int CORE_POOL_SIZE = 1;
    private static final int MAXIMUM_POOL_SIZE = 20;
    private static final int BACKUP_POOL_SIZE = 5;
    private static final int KEEP_ALIVE_SECONDS = 3;

    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), sThreadFactory);
        threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
        //赋值
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    //线程工厂
    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());
        }
    };

最后赋值操作将threadPoolExecutor赋值给THREAD_POOL_EXECUTOR。其实该线程池才是任务的真正执行者。

注意事项

1.生命周期

AsynTask不与任何组件生命周期进行绑定,在使用的对象中销毁时最好要调cancel()方法。

2.内存泄漏

一般我们声明都是一个内部类形式,内部类默认持有外部类的引用,当外部类使用完毕但是AsynTask中耗时任务还没执行完毕导致外部类无法回收,进而造成内存泄露

3.结果丢失

在进行屏幕旋转或者Activity进程被杀死之后会重新创建Activity,之前运行的AsynTask会持有之前Activity的引用,但该引用已经无效,导致onPostExectuor无法更新结果。

4.AsynTask并行执行

我们可以通过AsynTask的executeOnExecutor传入我们自定义的线程池,也可以传入AsynTask自身的THREAD_POOL_EXECTOR线程池。

 

总结

首先我们要创建AsynTask对象,在创建时会先去获取Handler对象,之后回去创建一个WorkerRunnable对象,之后会将WorkerRunnable当作参数传入到FutureTask中,创建好AsynTask对象之后就可以调用execute方法,实际上调用的是executeOnExecutor(sDefaultExectuor,mParams),内部是一个SerialExecutor对象,SerialExecutor是一个串行线程池,所有任务都在这里排队执行,之后先去执行onPreExecute(),该方法需要我们自己实现,之后线程池开始执行,并将FutureTask传入,并执行SerialExecutor中的execute()内部调用了FutureTask的run方法,实际就是调用了我们之前创建的WorkerRunnable对象的call方法,在call内部执行了doInBackground方法,我们只需要将耗时操作写在ondoInBackground中即可),从这里就从主线程切换到了子线程,首先判断任务中有没有正在活动的任务,如果没有则会调用scheduleNext获取一个任务,当任务执行完毕之后在finally中再去调用scheduleNext,保证了该任务执行完毕之后再调用下一个任务,在scheduleNext内部通过THREAD_POOL_EXECUTOR进行任务执行。而在内部还有一个静态Handler对象,他的作用就是将子线程切换到主线程,为了能够进行更新UI操作,只要保证AsynTask在主线程创建即可。我们在doInBackground中通过publishProgress()创建消息,并通过Handler将消息发送出去,AsynTask中的InternalHandler中handleMessage调用onProgressUpdate()方法进行进度更新。当任务执行完毕doInBackground会返回结果到postResult中,再去发送消息给Handler,handler中会调用finish(),这样任务就执行完毕。

 类似资料: