Android多线程系列(一) AsyncTask基本使用以及源码解析

前言

Android中,线程是操作系统调度的最小单位。线程分为主线程和子线程。主线程用来处理界面的交互,而耗时操作(网络请求,复杂的数据库查询)必须在子线程中来完成。通过Handler消息机制完成主线程和子线程之间的通信。

每个任务都需要一个线程去执行,但是不可能每个任务的执行都是伴随着线程的销毁和重新创建,十分的耗费性能。所以用线程池去缓存一定数目的线程,由线程池来管理执行任务的线程,避免了频繁的创建和销毁。Android中提供了AsyncTask类,内部就是由线程池Handler实现的。各位大佬,这篇看了包会!

这里写图片描述

(一) AsyncTask使用

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一个抽象类,如果使用的话需要自定义一个类继承它。它提供了4个核心的方法。这里用一个示例来做介绍(Kotlin环境下)。

一.使用和核心方法介绍

1.1 自定义类继承AsyncTask

/*静态属性声明处,此处MyAsyncTask为静态类*/
 companion object {

         class MyAsyncTask(private var taskName: String) : AsyncTask<String, Int, String>() {
            /*核心方法1*/
             override fun onPreExecute() {
                super.onPreExecute()
                 Log.d("MyAsyncTask", taskName + " OnPreExecute")
            }
            /*核心方法2*/
            override fun doInBackground(vararg params: String?): String {

                try {
                    Thread.sleep(5000)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                /*辅助方法*/
                publishProgress(50)

                Log.d("MyAsyncTask", taskName + " doInBackground")

                return taskName
            }
            /*核心方法3*/
            override fun onPostExecute(result: String?) {
                super.onPostExecute(result)
                Log.d("MyAsyncTask",
                        "I am Task" + result + " executed at  " + SimpleDateFormat("yyyy-mm-dd HH:mm:ss").format(Date()))

            }
            /*核心方法4*/
            override fun onProgressUpdate(vararg values: Int?) {
                super.onProgressUpdate(*values)

                Log.d("MyAsyncTask", taskName + " onProgressUpdate")
            }
        }
    }

声明好了静态内部类MyAsyncTask后,小手一点Button执行异步任务。

/*execute是Button控件的id,这里没有使用findViewById()*/
execute!!.setOnClickListener({
     MyAsyncTask("AsyncTask1").execute()
 })

介绍一下上面点击按钮执行任务操作,在项目中是没有写findViewById()去初始化Button,通过Kotlin Android Extensions拓展插件直接可以使用xml布局里Button的id来调用相应的方法,两个字:简便。有兴趣的同学可以去Kotlin官网了解一波。下面是MyAsyncTask执行的结果。

这里写图片描述

根据图示介绍一下AsyncTask的核心方法

1-2.onPreExecute()

在主线中执行,用于异步任务执行之前做一些准备工作,比如初始化加载进度条之类。

1-3.doInBackground(vararg params: String?)

在子线程中执行,真正执行异步任务的方法,参数是异步任务输入参数。在此方法中可以通过 publishProgress()方法来更新异步任务当前的进度,调用publishProgress()方法会同步调用onProgressUpdate()

1-4. onProgressUpdate(vararg values: Int?)

在主线程中执行,此方法用来实时显示当前任务的执行进度。作用是可以在此处更新UI上的进度条进度或者做其他操作。

1-5. onPostExecute(result: String?)

在主线程中执行,异步任务执行结束之后会执行此方法。参数是doInBackground()的返回值。

AsyncTask的核心方法就是上面所述,执行任务是在子线程中,而任务准备工作,进度更新等全是在主线程中完成。AsyncTask实例的创建也是要在主线程中去执行。

二.源码解析

根据代码执行顺序,我们首先看下AsyncTask的 execute()方法。

/*注解,主线程中执行*/
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... 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 后台执行任务所需参数*/
        mWorker.mParams = params;
        /*线程池执行线程任务,mFuture封装了开启线程的mWorker*/
        exec.execute(mFuture);

        return this;
    }

我们看下上面executeOnExecutor()方法中几个重要的属性。exec →* SerialExecutor*,mWorker → WorkerRunnable和mFuture → FutureTask

WorkerRunnable:任务执行返回的结果

这里mWorker对象的创建是在AsyncTask的构造函数中,本质是开启一个线程,call()方法将任务执行完的结果返回。

  mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //标记任务已经被调用过
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //将线程的优先级设置为后台,为了执行完任务方便回收
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //后台执行任务
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    //有异常将此任务取消
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //任务结束
                    postResult(result);
                }
                return result;
            }
        };

FutureTask:线程任务(理解为要执行的任务)

这里将mWorker任务的参数封装成FutureTask对象,按照中式英语理解的话就是将来要执行的任务。本质是一个Runnable对象。

mFuture = new FutureTask<Result>(mWorker) {
       @Override
       /*任务完成后的回调*/
       protected void done() {
          try {
              /*调用下方postResult()方法*/
               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) {
               /*调用下方postResult()方法*/
               postResultIfNotInvoked(null);
        }
     }
};

当线程任务执行结束后,调用postResult(Result result)通过InternalHandler将线程池(子线程任务)切换到主线程中。

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

getHandler()获取的就是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;
         }
    }
}

exec:线程池执行类

execSerialExecutor串行线程池对象,主要作用是将要执行的任务(mFutureTask)通过此类被插入到任务队列mTasks中,整合任务执行顺序作用。会不断从队列中取出任务来执行,如果当前任务执行完毕,mTasks会查找队列顶端是否有mFutureTask对象。有的话就执行。没有任务结束。

/*源码中不是这样写的,这里为了方便理解*/
public static final Executor exec = new 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() {
            /*队列最上方的一个元素不为null即队列顶端有空闲的任务*/
            if ((mActive = mTasks.poll()) != null) {
                /*真正执行任务的线程池:THREAD_POOL_EXECUTOR核心类ThreadPoolExecutor对象*/
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

其实真正执行任务的线程池是:ThreadPoolExecutor核心类的对象THREAD_POOL_EXECUTOR。

THREAD_POOL_EXECUTOR.execute(mActive);

目前 AsyncTask在Android3.0以上版本的执行顺序是串行的,有个多个任务添加到mTasks队列中,执行的顺序是从队列顶端往下依次执行。如果想实现多线程的并行任务。只要将

 MyAsyncTask("AsyncTask1").execute()

替换为下述即可:

 MyAsyncTask("AsyncTask1").executeOnExecutor()

结尾

Android中多线程任务虽然没有Java中使用频率高,但是是一个比较重要的知识点。AsyncTask可以在很多多线程任务中发挥好的作用,比如项目中的在线更新等。线程池也是特别重要的知识点,几乎是面试必问,接下来将会介绍线程池的工作原理等相关知识。

猜你喜欢

转载自blog.csdn.net/qq_17470165/article/details/80629492