zoukankan      html  css  js  c++  java
  • 26、Android--AsyncTask

    AsyncTask

    基本用法

    AsyncTask是一个抽象类,使用时必须创建子类继承它,在继承时我们可以为其指定三个泛型参数:

    泛型参数 描述
    Params 在执行AsyncTask时需要传入的参数,可用于后台任务中使用。
    Progres 后台执行任务时,如果界面上面要显示当前任务进度,则使用该泛型为进度单位。
    Result 当任务执行完毕后,如果需要对结果进行返回,则使用该泛型作为返回值类型。

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

    class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
        ……
    }  
    

    这里我们把AsyncTask的第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使

    用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。

    当然我们目前定义的DownloadTask还是一个空任务,我们还需要重写四个方法即可:

    方法 描述
    onPreExecute() 后台任务开始前调用,用于界面上的一些初始化操作。
    doInBackground() 在子线程中运行,用于处理耗时的操作,任务使用return来返回结果,通过Result泛型来判断是否有结果返回。如果想反馈实时任务进度,可以使用publishProgress()完成。
    onProgressUpdate() 在后台任务调用publishProgress()后调用,该方法可以根据实时进度来更新UI操作。
    onPostExecute() 后台任务执行完毕后调用,会根据返回的结果进行一系列操作。

    因此,一个比较完整的自定义AsyncTask就可以写成如下格式:

    class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
        @Override
        protected void onPreExecute() {
            progressDialog.show();
        }
        @Override
        protected Boolean doInBackground(Void... params) {
            try {
                while (true) {
                    int downloadPercent = doDownload();
                    publishProgress(downloadPercent);
                    if (downloadPercent >= 100) {
                        break;
                    }
                }
            } catch (Exception e) {
                return false;
            }
            return true;
        }
        @Override
        protected void onProgressUpdate(Integer... values) {
            progressDialog.setMessage("当前下载进度:" + values[0] + "%");
        }
        @Override
        protected void onPostExecute(Boolean result) {
            progressDialog.dismiss();
            if (result) {
                Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
            }
        }
    }  
    

    如果想要启动这个任务,只需要简单地调用以下代码即可:

    new DownloadTask().execute();
    

    AsyncTask原理

    Android提供了 AsyncTask,它使得异步任务实现起来更加简单,代码更简洁。下面首先来看AsyncTask的定义,如下所示:

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

    AsyncTask是一个抽象的泛型类,它有3个泛型参数,分别为Params、Progress和Result,其中Params为 参数类型,Progress为后台任务执行进度的类型,

    Result为返回结果的类型。如果不需要某个参数,可以将其设置为Void类型。

    AsyncTask中有4个核心方法,如下所示:

    onPreExecute():在主线程中执行。一般在任务执行前做准备工作,比如对 UI 做一些标记。
    doInBackground(Params...params):在线程池中执行。在 onPreExecute方法执行后运行,用来执 行较为耗时的操作。
    onProgressUpdate(Progress...values):在主线程中执行。当调用 publishProgress(Progress...values)时,此方法会将进度更新到UI组件上。
    onPostExecute(Result result):在主线程中执行。当后台任务执行完成后,它会被执行。

    AsyncTask是对Handler与线程池的封装。使用它的方便之处在于能够更新用户界面,当然这里更新用户界面的操作还是在主线程中完成的,但是由于AsyncTask

    内部包含一个Handler,所以可以发送消息给主线程让它更新UI。(使用到线程池,减少线程的开销)

    AsyncTask的局限性

    AsyncTask的优点在于执行完后台任务后可以很方便的更新UI,然而使用它存在着诸多的限制。先抛开内存泄漏问题,使用AsyncTask主要存在以下局限性:

    1、在Android 4.1版本之前,AsyncTask类必须在主线程中加载,这意味着对AsyncTask类的第一次访问必须发生在主线程中;
    2、在Android 4.1以后不存在这一限制,因为ActivityThread的main方法中会自动加载AsyncTask
    3、AsyncTask对象必须在主线程中创建
    4、AsyncTask对象的execute方法必须在主线程中调用
    5、一个AsyncTask对象只能调用一次execute方法

    接下来,我们从源码的角度去探究一下AsyncTask的工作原理,并尝试着搞清楚为什么会存在以上局限性。

    AsyncTask工作原理

    首先,让我们来看一下AsyncTask类的构造器都做了些什么:

    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        // WorkerRunnable实现了Callable接口,并实现了它的call方法,在call方法中调了doInBackground(mParams)来处理任务并得到结果,
        // 并最终调用postResult将结果投递出去。
        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;
            }
        };
        // FutureTask是一个可管理的异步任务,它实现了Runnable和Futrue这两个接口。
        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);
                }
            }
        };
    }
    

    当要执行AsyncTask时,需要调用它的execute方法,代码如下所示:

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

    execute方法又调用了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();
        // 将AsyncTask的参数传给WorkerRunnable
        mWorker.mParams = params;
        exec.execute(mFuture);
        return this;
    }
    

    从前面可以知道WorkerRunnable作为参数传递给了FutureTask,因此,参数被封装到FutureTask中。

    接下来会调用exec的execute方法,并将mFuture也就是前面讲到的FutureTask传进去。这里exec是传进来的参数 sDefaultExecutor,它是一个串行的线程池 SerialExecutor(通过debug获得),其代码如下所示:

    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        public synchronized void execute(final Runnable r) {
            // 调用SerialExecutor的execute方法时,会将FutureTask加入到mTasks 中。
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        // 执行了FutureTask的run方法,它最终会调 用WorkerRunnable的call方法
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    

    该类是AsyncTask的内部类,AsyncTask的原理比较麻烦,待后续整理。

  • 相关阅读:
    排序算法系列之冒泡排序 (3)
    排序算法系列之选择排序 (2)
    排序算法系列之插入排序 (1)
    深挖 NGUI 基础 之UICamera (二)
    深挖 NGUI 基础 之UIRoot (一)
    JPS寻路和AStar寻路
    旋转矩阵
    [斜边的血条进度]
    UI框架:ui节点控件的绑定方式
    Shader播放序列帧
  • 原文地址:https://www.cnblogs.com/pengjingya/p/5510246.html
Copyright © 2011-2022 走看看