AsyncTask学习笔记

AsyncTask定位

AsyncTask是Android封装的一个用于线程切换的方便的工具类,属于一个抽象类,通过继承它来达到后台执行任务,并且把任务细节通知给前台view刷新显示的目的。如果用Java写Android,依然是推荐使用的,但是如果是用Kotlin官方则是推荐使用Kotlin的协程类来完成这类的任务。

AsyncTask优点

对于一个后台任务,是自己使用线程池还是自己新建线程还是用AsyncTask,这三种方式到底谁更好呢,从原理上来讲:
AsyncTask的执行原理其实等价于自己维护线程池来执行,而线程池和AsyncTask在性能上优于新建线程,这点很好理解,因为:新建线程会有新建线程实例,销毁线程实例的频繁的开销,而线程池没有

AsyncTask几个基本方法

AsyncTask实际上是调用线程池来完成自己的工作,也就是doInBackground方法。然后再通过主线程的Handler把结果返回给UI线程。

AsyncTask的主要的函数包含4个,doInBackground,onPreExecute,onPostExecute,onProgressUpdate

  • doInBackground:必须重写的方法,执行后台任务,其中的代码在子线程执行
  • onPreExecute:非必须重写方法,在开启子线程执行任务之前,在主线程执行的方法
  • onPostExecute:非必须重写的方法,由封装后的后台任务(WorkerRunnable)通过Handler的post方法发送结果给主线程,在主线程执行
  • onProgressUpdate:如果调用publish方法,则会发送当前任务进度给主线程,通过主线程调用该方法

AsyncTask内部的执行机制

之前也讲了,AsyncTask内部是通过线程池来执行后台任务的,那么具体怎么执行的呢。从它的execute看起:

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

看来原理都在executeOnExecutor方法里,那么看executeOnExecutor方法的具体实现:

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

方法里就做了三件事

  1. .检查当前状态,如果不是Pending状态1就抛异常
  2. 执行onPreExecute方法,请注意,这里还没有开新线程,所以这里是在主线程跑
  3. 给WorkRunnable(mWorker)设置参数,然后丢给Executor执行,这里的mFuture是对WorkRunnable(Callable)的包装,不再赘述

Executor只是一个接口规范,具体是不是开线程还要看实现,这里的Executor就是前面传入的sDefaultExecutor

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

可以看到这里的Executor的实现,实际上是SerialExecutor

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

而SerialExecutor 的execute方法做了两件事:

  1. 封装传入的Runnable(使任务执行完后取下一个任务交给THREAD_POOL_EXECUTOR执行),然后把它添加到队列里,双端队列不是线程安全的,所以加了synchronized关键字
  2. 如果当前没有开始,就手动调用开始(取下一个任务交给THREAD_POOL_EXECUTOR执行)

可以看到,这里没有开新线程执行,所以这里的execute是主线程执行的,SerialExecutor 也只是实现了Executor接口,并没有开启新的线程执行任务,具体的开始新线程的地方还是在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;
}

看到这里,总算看到开启新线程的玩意儿了,ThreadPoolExecutor。
在这里通过线程池开启子线程来执行我们分下来的任务。

那SERIAL_EXECUTOR 究竟起了个什么作用呢,可以看到实际上他的最大作用就是让任务串行执行,所以,AsyncTask的任务执行实际上是串行的

所以这里的线程池核心线程数实际上是1,因为有SERIAL_EXECUTOR 的存在,使得THREAD_POOL_EXECUTOR执行的任务最多就只有2个一个正在执行的任务和一个接下来要执行的任务。

需要注意的是,不管是SERIAL_EXECUTOR 还是THREAD_POOL_EXECUTOR,他们都是绑定在AsyncTask类上的静态属性,所以不管新建多少个AsyncTask的子类,他们的存在都是唯一的,而且是静态的,所以声明周期就特别长。这里来分析一下持有关系:

  • WorkerRunnable内部执行AsyncTask的doInBackground,所以WorkerRunnable持有了AsyncTask的this实例。
  • mFuture包裹了WorkerRunnable,所以相当于mFuture持有了AsyncTask的实例。
  • SerialExecutor 个双端队列,持有一系列Runnable·,而这些Runnable会调用mFuture的run方法,所以他们持有mFuture,相当于队列持有mFuture,最后得出SerialExecutor持有了AsyncTask

那如果AsynTask持有了Context(Activity,Service)这类生命周期比较短的实例,那就会造成Context无法释放的情况。

最后执行的结果实际上在AsynTask的构造函数里就已经暴露了。

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

可以看到我们的doInBackground执行完毕后,在finally 里会执行postResult,而postResult最终会调用主线程的(不传非主线程的looper则默认是主线程)Handler把结果传回给主线程执行其onPostExecute方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用中提到了在使用AsyncTask时,可以在onPostExecute方法中进行线程完成后的操作。这个方法通常被用来将结果传递回线程。例如,可以在这个方法中更新UI界面。AsyncTask的使用可以通过创建一个继承于AsyncTask的类来实现,然后在需要的地方调用execute方法执行任务。引用给出了一个使用AsyncTask的示例,其中定义了一个继承于AsyncTask的InsertAsyncTask类。这个类可以用来在后台线程执行数据库操作。引用提到了一个使用AsyncTask来加载网络图片的例子,其中使用了LruCache算法进行图片的缓存。综上所述,AsyncTask可以用于数据库操作,通过继承AsyncTask并在doInBackground方法中进行数据库操作,然后在onPostExecute方法中进行结果的处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Jetpack学习笔记(一):使用Room和AsyncTask来处理数据库](https://blog.csdn.net/XiaoYunKuaiFei/article/details/105639153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Android Lrucache加载图片(AsyncTask )](https://download.csdn.net/download/u011846345/9184327)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值