Android之 AsyncTask异步任务类与机制原理

AsyncTask简单介绍:

AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.为了将Handler、静态Thread等封装为一个异步执行框架,方便调用。主要目的是为了“在其他线程中执行一个耗时操作,并随时报告执行进度给UI线程,执行完成后将结果报告给UI线程”。通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程.。

我们知道,Android中只有主线程才能进行对UI的更新操作,其他线程是不能直接操作UI的。但总不能把所有的任务都放在主线程中进行实现,所以就出现了AsyncTask。

在AsyncTask中可以执行一些耗时操作,网络请求,可以很好的避免ANR错误。反之全部放到主线程去执行,就可能会造成后面任务的阻塞。当阻塞时间太长的时候,就会抛出Application Not Responsed(ANR)错误.所以我们需要将这些耗时操作放在非主线程中去执行.这样既避免了Android的单线程模型,又避免了ANR.

源码详细:https://blog.csdn.net/m0_37700275/article/details/83546140


AsyncTask具体介绍:

继承AsyncTask的类指定了三个泛型参数:

  • Params:启动任务时输入的参数类型.
  • Progress:后台任务执行中返回进度值的类型.
  • Result:后台任务执行完成后返回结果的类型.

 AsyncTask中有三个方法:

  • doInBackground:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成.
  • onPreExecute:执行后台耗时操作前被调用,通常用于进行初始化操作.
  • onPostExecute:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新.
  • onProgressUpdate:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度.
class MyAsyncTask extends AsyncTask<Integer, Integer, Integer> {
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Log.i(TAG, "onPreExecute...(开始执行后台任务之前)");
    }

    @Override
    protected void onPostExecute(Integer i) {
        super.onPostExecute(i);
        Log.i("TAG", "onPostExecute...(开始执行后台任务之后)");
    }

    @Override
    protected Integer doInBackground(Integer... params) {
        Log.i(TAG, "doInBackground...(开始执行后台任务)");
        return 0;
    }
}

 开始执行异步方法:

new MyAsyncTask().execute();

 AsyncTask机制原理:

我们先从他Execute方法,其中有一个ExecuteOnExcuteor的方法,通过Status.Pending来判断当前的任务状态。如果为正在执行这个任务,或者是已经完成任务的情况下,就会出现异常,只有是未完成的情况下,开始执行OnPerExecute()的方法。以及把我们的params赋值给mworker.mparams(),并执行execute方法,把mFurture方法传过去!

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

AsyncTask中有两个线程池,和一个Handler,其中有一个SerialExector()的方法是用来任务排队,THREAD_POLL_EXECUTOR的方法是用来真正的执行任务!

那么接下来就用SerialExector()的方法来看看其中是如何排队的?那么这里用了锁来限制,首先它实现了ExeCutor的接口,实现了接口中的Call()方法。

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

接下来在SerialExector类中实现runnable方法,用一个r.run(),间接的调用了Call方法中的DoInBackground(),再调用其中的oScheduleNext()方法,判断当前任务是否为空,不为空则执行THREAD_POLL_EXECUTOR方法,处理任务!

  public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
  private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
  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);
            }
        }
    }

处理完后,就要用到Handler来将线程环境转换为主线程也就是要执行onPostExecute()方法

AsyncTask和Handler对比:

1.AsyncTask:使用的优点: 简单,快捷、过程可控.

  • 使用的缺点: 在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.
  • 使用AsyncTask类,以下是几条必须遵守的准则:
    • Task的实例必须在UI thread中创建;
    • execute方法必须在UI thread中调用;
    • 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
    • 该task只能被执行一次,否则多次调用时将会出现异常;
  • AsyncTask注意事项:
    • 注意内存泄漏
    •  生命周期:AsyncTask生命周期不随着Activity,必须把它在onDestory()方法中finish掉,才会销毁掉。
    • 结果丢失:当发生屏幕旋转等问题,会重新构造Activity,所以AsyncTask持有的也是之前Activity的引用,所以引用无效,只能用AsyncTask中的onPostExcute()方法更新界面就不会失效了,数据也就不会对视

2.Handler:

源码了解:https://blog.csdn.net/LoverLeslie/article/details/85267367

  • 使用的优点:结构清晰,功能定义明确、  对于多个后台任务时,简单,清晰
  •  使用的缺点:在单个后台异步处理时,显得代码过多,结构过于复杂(相对性)

AsyncTask和Handler内存泄漏处理方法: 

1.AsyncTask:

解决办法:将AsyncTask设置为静态内部类、在Activity的onDestory()方法中finish掉 (AsyncTask.cancle() )、将内部改为持有外部类的弱引用。

2.Handler:

原因:handler如果是是非静态内部类 ,那么就会引用外部类的匿名引用,导致Activity无法释放

解决办法:将Handler设置为静态内部类、在Activity的onDestory()方法中remove掉( handler.removeCallback() )、将内部改为持有外部类的弱引用。

猜你喜欢

转载自blog.csdn.net/LoverLeslie/article/details/85288678