AsyncTask源码详解

70 阅读7分钟

线程在Android中是一个很重要的概念,从用途上说,线程分为主线程和子线程,主线程主要处理和界面相关的事情,而子线程则往往用于执行耗时操作。

AsyncTask封装了线程池和Handler,主要是方便开发者在子线程中更新UI。

HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler。

IntentService是一个服务,系统对其进行了封装使其可以更方便的执行后台任务,IntentService内部采用了HandleThread来执行任务,当任务执行完毕后IntentService会自动退出。

AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。AsyncTask背后的实现原理也是基于异步消息处理机制的,只是Android帮我们做了很好的封装而已。

由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须创建一个子类去继承它。在继承时我们可以为AsyncTask类指定3个泛型参数,这3个参数的用途如下。

Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。

Progress:在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。

Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

因此,一个最简单的自定义AsyncTask就可以写成如下形式:

class DownloadTask : AsyncTask<Unit, Int, Boolean>() {

...

}

这里我们把AsyncTask的第一个泛型参数指定为Unit,表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Int,表示使用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。

需要重写的方法和作用:

  1. onPreExecute()

这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

  1. doInBackground(Params...)

这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成,就可以通过return语句将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Unit,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用**publishProgress (Progress...)**方法来完成。

  1. onProgressUpdate(Progress...)

当在后台任务中调用了publishProgress(Progress...)方法后,onProgressUpdate (Progress...)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

  1. onPostExecute(Result)

当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如说提醒任务执行的结果,以及关闭进度条对话框等。

看到这里我有个非常冲动的想法,改写DownloadDialog里面下载apk的逻辑!

原代码:

private fun downloadAPK(apk_file_url: String) {
    Thread {
        try {
            if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
                Log.i(TAG, "downloadAPK: ")
                Log.i(TAG, "appName: $appName")
                val sdPath: String = Environment.getExternalStorageDirectory().toString() + "/"
                //文件保存路径
                mSavePath = sdPath + "${appName}.apk"
                val dir = File(mSavePath!!)
                if (!dir.exists()) {
                    dir.mkdir()// 创建此抽象路径名指定的目录
                }
                // 下载文件
                val conn: HttpURLConnection =
                    URL(apk_file_url).openConnection() as HttpURLConnection
                conn.connect()
                val `is`: InputStream = conn.inputStream
                val length: Int = conn.contentLength
                val apkFile = File(mSavePath, "${appName}.apk")
                val fos = FileOutputStream(apkFile)
                var count = 0
                val buffer = ByteArray(1024)
                while (!mIsCancel) {
                    val numread: Int = `is`.read(buffer)
                    count += numread
                    // 计算进度条的当前位置
                    mProgress = (count.toFloat() / length * 100).toInt()
                    // 更新进度条
                    mUpdateProgressHandler.sendEmptyMessage(1)

                    // 下载完成
                    if (numread < 0) {
                        mUpdateProgressHandler.sendEmptyMessage(2)
                        break
                    }
                    fos.write(buffer, 0, numread)
                }
                fos.close()
                `is`.close()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }.start()
}
private val mUpdateProgressHandler: Handler = object : Handler() {
    override fun handleMessage(msg: Message) {
        when (msg.what) {
            1 -> // 设置进度条
            {
                proBar.progress = mProgress
                proBar_text_about.text="${mProgress}%"
            }
            2 -> {
                Toast.makeText(context,"下载完成", Toast.LENGTH_SHORT).show()
                // 安装 APK 文件
                installAPK()
                dismiss()
            }
        }
    }
}

使用AsyncTask进行改写后的代码:

inner class DownLoadApkTask : AsyncTask<Unit, Int, Boolean>(){
    override fun onPreExecute() {
        show()
    }

    override fun doInBackground(vararg params: Unit?): Boolean = try {
        if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
            val sdPath: String = Environment.getExternalStorageDirectory().toString() + "/"
            //文件保存路径
            mSavePath = sdPath + "LowCarbonApp.apk"
            val dir = File(mSavePath!!)
            if (!dir.exists()) {
                dir.mkdir()
            }
            // 下载文件
            val conn: HttpURLConnection =
                URL(downloadUrl).openConnection() as HttpURLConnection
            conn.connect()
            val `is`: InputStream = conn.inputStream
            val length: Int = conn.contentLength
            val apkFile = File(mSavePath, "LowCarbonApp.apk")
            val fos = FileOutputStream(apkFile)
            var count = 0

            while (mIsCancel){
                val buffer = ByteArray(1024)
                val numread: Int = `is`.read(buffer)
                count += numread
                // 计算进度条的当前位置
                mProgress = (count.toFloat() / length * 100).toInt()
                publishProgress(mProgress)
                // 下载完成
                if (numread < 0) {
                    break
                }
                fos.write(buffer, 0, numread)
            }
            fos.close()
            `is`.close()
        }
        true
    }catch (e: Exception){
        false
    }

    override fun onProgressUpdate(vararg values: Int?) {
        proBar.progress = mProgress
        proBar_text_about.text="${mProgress}%"
    }

    override fun onPostExecute(result: Boolean?) {
        if (mIsCancel){
            Toast.makeText(context,"下载完成", Toast.LENGTH_SHORT).show()
            // 安装 APK 文件
            installAPK()
        }
        dismiss()
    }

}

想要执行该下载任务直接DownLoadApkTask().execute()

也可以给execute()方法传入任意数量的参数,这些参数将会传递到DownloadTask的doInBackground()方法当中。

需要注意的是AsyncTask类的加载和对象的创建,execute方法必须在主线程中完成,一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行异常。

接下来我们一起来看下AsyncTask的源码:

我们首先看下AsyncTask的构造函数,整个实现方法就是Executor框架的体现,第一步创建继承Callable接口的抽象类WorkerRunnable。第二步实现该抽象类mWorker并重写call方法,这里使用Callable接口而不是Runnable接口是因为需要返回值,在call方法里面返回result,而result就是doInBackground方法的返回值。第三步初始化FutureTask变量,将mWorker封装到FutureTask类里面。

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

public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);

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

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

如果想要启动某一个任务,就需要调用该任务的execute()方法,因此现在我们来看一看execute()方法的源码,execute()方法里面调用的是executeOnExecutor()方法。在executeOnExecutor方法中,onPreExecute方法最先执行,然后才开始执行线程池中的任务exec.execute(mFuture),exec是上面传进来的一个串行的线程池sDefaultExecutor,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行,注意到这里执行的任务是mFuture。

正常来说,线程池中执行的应该是线程才对,这里为什么执行的为什么是FutureTask类,我们进到FutureTask里面看到它的继承关系最后继承的接口就是Runnable,关于Future类可以看下blog.csdn.net/tongdanping…

这里将线程(Callable)封装成FutureTask供线程池执行。

@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 = params;
    exec.execute(mFuture);

    return this;
}

我们再看下线程池sDefaultExecutor的定义,可以看到SerialExecutor类中也有一个execute()方法,这个方法里的所有逻辑就是在子线程中执行的了,注意这个方法有一个Runnable参数,那么目前这个参数的值是什么呢?当然就是mFuture对象了,也就是说r.run()调用的是FutureTask类的run()方法

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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) {
        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);
        }
    }
}

接着进到FutureTask类看下run方法,可以看到最后调用了callable的call方法,这个callable对象其实就是在初始化mFuture对象时传入的mWorker对象了,也就调用了mWorker重写的call方法。

public void run() {
    if (state != NEW ||
        !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                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);
    }
}

重写的call会返回result(就是doInBackground(mParams)返回值),最后将result作为参数传进postResult()方法里面,目前仍然运行在子线程当中,在postResult()方法中使用Handler将result暴露出去并在UI线程中显示result。

FutureTask重写的down方法是FutureTask执行完毕后的回调,也是将result传进在postResult()方法中,这里涉及到FutureTask的get()方法是获取mWorker的返回值的作用。

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

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

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

publishProgress方法将任务执行过程中的result传进Handler里面,Handler就是将子线程切换到UI线程的手段,Handler向外部暴露onProgressUpdate方法,将任务执行过程中获取到结果数据出传递出去。

protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

线程池在执行任务完成后调用postResult(result)方法将result传进Handler里面,最后进入finish方法向外部暴露onPostExecute方法将任务执行的结果传递出去。


private Handler getHandler() {
    return mHandler;
}

private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return sHandler;
    }
}

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

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