Android中更安全的使用AsyncTask

来源:互联网 发布:网贷数据交易平台 编辑:程序博客网 时间:2024/06/05 07:45

SafeAsyncTask

Android中更安全的使用AsyncTask

0x00 背景

我们都知道,Android中需要注意内存泄露的操作有:

  • BroadCastReceiver,注册和取消注册要成对存在。
  • BindService,绑定服务和解绑服务要成对出现。
  • Thread,一般在四大组件中创建的Thread在组建销毁的时候要Stop掉。
  • Handler,通过Handler发送的消息未在组件生命周期结束的时候及时移出。
  • AsyncTask,组件生命周期结束,但是其子线程仍旧在执行,导致组件不能及时释放。

我们今天就来针对AsyncTask来解决其有可能导致的内存泄露问题。

0x01 分析

想解决问题,首先得分析问题产生的原因。

AsyncTask之所以可能会产生内存泄露,是因为:

  1. 我们一般在组件内部,以内部类的方式创建AsyncTask,而java里面,内部类是默认持有外部类的引用。
  2. 我们的加载数据是在子线程中执行,但是java里面有没有提供很好的中断机制来中断线程 (有中断方法,但是如果你不针对性的处理,这个方法也没什么卵用),这样就导致组件生命周期已经结束,应该被GC回收,但是由于子线程正在执行,内部类无法回收,进而导致组件无法正常回收,所以造成了内存泄露 (Handler导致内存泄露也是类似的道理)

0x02 问题分析与解决

针对问题一个一个解决

  1. 针对内部类持有外部类引用,解决的方式是用 静态内部类 或者把类单独抽出来(本文中做法是把AsyncTask抽出来)。
  2. 针对组件声明周期结束的时候,子线程仍在执行的问题,应该在组件生命周期结束的时候取消掉(cancel(boolean mayInterruptIfRunning))异步任务, 并且 在子线程执行任务的时候要及时判断当前任务是否被取消了,及时中断异步任务。

上面两个方法解决了导致内存泄露的问题,但是又产生了一个新的问题,组件跟异步任务怎么通讯,异步任务怎么通知组件自己当前的状态(准备执行,执行结束,执行取消等)。

  1. 组件跟异步任务通许,可以通过基类定义构造方法传递数据和pulic方法供组件调用。
  2. 异步任务跟组件通讯可以通过 接口,这里的灵感来自最近大火的 MVP 中的接口隔离的思想(不知道这么说准确不准确)。

构造异步任务的时候传入接口,取消任务的时候把接口置空,这样就切断了异步任务和组件之间的关系。子线程执行的时候及时判断当前状态,如果是取消的话就及时中断任务。

完美解决!!!2333

0x03 代码实现

最终解决需要以下几个要素:

  1. 回调接口 AsyncTaskCallBack

    @UiThreadpublic interface AsyncTaskCallBack<T> {/** * 当异步任务将要开始执行的时候执行 */public void onPreExecute();/** * 当结果返回的时候执行 * @param t 返回的结果 */public void onPostExecute(T t);/** * 当请求被取消的时候执行 */public void onCancled();}
  2. 异步任务基类 BaseAsyncTask

    public abstract class BaseAsyncTask<Params, Result> extendsAsyncTask<Params, Void, Result> {private AsyncTaskCallBack<Result> mLoadDataCallBack;public BaseAsyncTask(AsyncTaskCallBack<Result> callBack) {    mLoadDataCallBack = callBack;}/** * 取消异步任务,一般在Activity或者Fragment的生命周期结束({@link android.app.Activity#onDestroy() onDestroy()})中调用此方法 * <p> * <b>请调用此方法取消任务,而不是 {@link #cancel(boolean)}}方法</b> */final public void cancelTask() {    if (getStatus() != Status.FINISHED) {        cancel(true);    }}@Overridefinal protected void onPreExecute() {    super.onPreExecute();    if (mLoadDataCallBack != null) {        mLoadDataCallBack.onPreExecute();    }}@Overridefinal protected void onPostExecute(Result result) {    super.onPostExecute(result);    if (mLoadDataCallBack != null) {        mLoadDataCallBack.onPostExecute(result);    }}@Overridefinal protected void onCancelled() {    super.onCancelled();    if (mLoadDataCallBack != null) {        mLoadDataCallBack.onCancled();    }    //无法放到 cancelTask()中。    //因为此方法会在cancelTask()后执行,所以如果放到cancelTask()中,则此字段永远是空,也就不会调用 onCancel()方法了    mLoadDataCallBack = null;    }}
  3. 使用

    使用起来也很简单

    1. 实现 AsyncTaskCallBack 接口
    2. 写异步任务继承 BaseAsyncTask

      protected List<Person> doInBackground(String... params) {    List<Person> persons = new ArrayList<Person>();    for (int i = 0; i < 10; i++) {        //如果是取消了,则直接跳出循环,减少无用功。        if (isCancelled()) break;        SystemClock.sleep(500);        Person person = new Person();        person.setName("Name   "+i);        persons.add(person);    }    return persons;}           
    3. 创建异步任务并执行

      //执行异步任务前别忘了先取消上次的任务if (mLoadDataTask != null) mLoadDataTask.cancelTask();mLoadDataTask = new LoadDataTask(this);//AsyncTask的excute方法是历尽转折,详情请参照 其他 中对于他的介绍。mLoadDataTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "url");

具体代码详见Demo,欢迎提出意见。

0x04 其他

使用AsyncTask时发现一个奇怪的现象,即创建多个任务的时候,他是一个一个按顺序执行的,查资料之后发现:

在1.5中初始引入的时候, AsyncTask 执行( AsyncTask.execute() )起来是顺序的,当同时执行多个 AsyncTask的时候,他们会按照顺序一个一个执行。前面一个执行完才会执行后面一个。这样当同时执行多个比较耗时的任务的时候 可能不是您期望的结果,具体情况就像是execute的task不会被立即执行,要等待前面的task执行完毕后才可以执行。

在android 1.6(Donut) 到 2.3.2(Gingerbread)中,AsyncTask的执行顺序修改为并行执行了。如果同时执行多个任务,则这些任务会并行执行。 当任务访问同一个资源的时候 会出现并发问题.

而在Android 3.0(Honeycomb)以后的版本中,AsyncTask又修改为了顺序执行,并且新添加了一个函数 executeOnExecutor(Executor),如果您需要并行执行,则只需要调用该函数,并把参数设置为并行执行即可。

即创建一个单独的线程池(Executors.newCachedThreadPool())。或者最简单的方法法就是使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),这样起码不用等到前面的都结束了再执行了。executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)则与execute()是一样的。

0x05 关于

Author peerless2012

Email peerless2012@126.con

Blog peerless2012.github.io

0 0
原创粉丝点击