开源项目绑定生命周期的一些思考

Android生命周期,这是一个老生常谈的问题。相信这是所有初入门Android的朋友们的第一节必修课。当然这篇文章写的不是什么生命周期函数,lauchMode这种问题。这篇文章我想写的是关于一些常用的开源框架在生命周期上巧妙的一些用法总结与一些思考,通过对一些常用的开源框架源码的思考与总结,将来工作的时候如果出现一些问题的时候我觉得能给你带来不一样的启发。

进入页面时的异步操作绑定生命周期的思路

图片加载框架肯定大家都不陌生,Glide、Fresco、ImageLoader……很多很多。Glide之前我也写过两篇文章具体跟过Glide的源码,这里对Glide的分析也是从第一篇文章里拎出来的,感兴趣的朋友可以到这里去看点击打开链接 和 点击打开链接

Glide绑定生命周期的理由很简单,图片加载这是一个异步操作,正常操作是这样的,我们进入到一个页面,页面某个节点让Glide去加载图片,如果出现加载还没完成的时候,页面突然onDestroy了的话,理想情况下Glide当前请求应该取消。

先来看Glide的最基本的基本用法

Glide.with(context).load(url).into(imageView);

ok,这里的with(Context context)就是我们这里要分析的地方,Glide通过这个with方法来绑定生命周期,来看源码:

public static RequestManager with(Context context) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(context);
    }


    public static RequestManager with(Activity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }


    public static RequestManager with(FragmentActivity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }

with()方法有很多重构方法,其实也就是参数不同而已,实现里可以看到都是跟RequestManagerRetriever这个类有关,RequestManagerRetriever.get()方法从代码里可以看到是为了得到它的单例,那就来看看RequestManagerRetriever这个类,先看下面这三个我抽取出来的方法:

public RequestManager get(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else if (Util.isOnMainThread() && !(context instanceof Application)) {
            if (context instanceof FragmentActivity) {
                return get((FragmentActivity) context);
            } else if (context instanceof Activity) {
                return get((Activity) context);
            } else if (context instanceof ContextWrapper) {
                return get(((ContextWrapper) context).getBaseContext());
            }
        }

        return getApplicationManager(context);
    }


public RequestManager get(FragmentActivity activity) {
        if (Util.isOnBackgroundThread()) {
            return get(activity.getApplicationContext());
        } else {
            assertNotDestroyed(activity);
            FragmentManager fm = activity.getSupportFragmentManager();
            return supportFragmentGet(activity, fm);
        }
    }

private RequestManager getApplicationManager(Context context) {
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    applicationManager = new RequestManager(context.getApplicationContext(),
                            new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                }
            }
        }

        return applicationManager;
    }

第一个方法中context传进来以后把FragmentActivity,Activity,ContextWrapper都分流出去,除了这三种情况之外(其实也就是application context)都return getApplicationManager(context)也就是上面代码中的第三个方法。从上述代码可以看出,当传入的context是application或者在子线程中,那么最后都会调用getApplicationManager,在getApplicationManager(Context context)方法中,创建了一个RequestManager对象,因为Application的生命周期是跟着app的生命周期的,所以当传入的context为application的时候Glide没有进行任何关于生命周期的判断。先来看另外一种也就是当传入Activity这些的时候:

RequestManager supportFragmentGet(Context context, FragmentManager fm) {
        SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
    RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
        RequestManagerFragment current = getRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

RequestManager supportFragmentGet(Context context, FragmentManager fm) {
        SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

ok,这里就已经契合到我们今天要写的主题了。从后面两个方法可以看到,这里不管context传入的是Activity还是FragmentActivity,Glide都会在Activity的基础上创建一个Fragment。(这个就是这篇文章要讲的Glide绑定生命周期的核心思想)

创建这个Fragment的目的是什么呢?很显然,因为Glide无法监听到Activity的生命周期,所以往Activity或者FragmentActivity里添加一个Fragment来监听Fragment的生命周期,Fragment跟Activity是绑定在一起的,如果Activity onDestroy了,那么添加的Fragment对象当然也就onDestroy了。继续跟踪Glide源码你就会发现,所有的操作代码都是在RequestTracker这个类里的,这个类主要负责了跟踪,取消,重启在进程中的或者已经完成了或者已经失败了的请求。

拓展:像volley,retrofit,okhttp网络框架对页面消亡的时候都是没有取消操作的,都是要手动取消的,类似glide一样的手法就给了我们想要在页面消亡时取消该页面网络请求的需求得到类似的思路。当然网上我也看到了其他思路,以Activity为tag,在页面onDestroy的时候手动调用获取该tag下的网络请求,然后再调用cancle方法。这个就类似eventbus的register()和unregister()的思路了。后面我会试下能不能模仿Glide这样封装一个okhttp,就不用手动的去操作,框架里面就做好了页面消亡请求取消的操作。

绑定全局生命周期

Leakcanary大家应该不会陌生,一个用来快速的发现内存泄漏的工具。

直入正题,Leakcanary使用起来很简单,直接在Application里写这么一句话就可以了:

LeakCanary.install(this);

ok,我们进入install(Application application)方法

  /**
   * Creates a {@link RefWatcher} that works out of the box, and starts watching activity
   * references (on ICS+).
   */
  public static RefWatcher install(Application application) {
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
  }

因为我们这里只关注生命周期,前面两个方法也不过是初始化配置,做了一些排除系统遭逢的泄漏啊,绑定结果分析的服务啊什么的配置,所以我们直接进入buildAndInstall()方法。

/**
 * Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).
 */
public RefWatcher buildAndInstall() {
  RefWatcher refWatcher = build();
  if (refWatcher != DISABLED) {
    LeakCanary.enableDisplayLeakActivity(context);
    ActivityRefWatcher.install((Application) context, refWatcher);
  }
  return refWatcher;
}

这里初始化了一个RefWatcher对象,用来判断泄漏对象,我们这里跟着context继续走,install()方法:

进入到了ActivityRefWatcher类:

public static void install(Application application, RefWatcher refWatcher) {
    new ActivityRefWatcher(application, refWatcher).watchActivities();
  }

  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new Application.ActivityLifecycleCallbacks() {
        @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }

        @Override public void onActivityStarted(Activity activity) {
        }

        @Override public void onActivityResumed(Activity activity) {
        }

        @Override public void onActivityPaused(Activity activity) {
        }

        @Override public void onActivityStopped(Activity activity) {
        }

        @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override public void onActivityDestroyed(Activity activity) {
          ActivityRefWatcher.this.onActivityDestroyed(activity);
        }
      };

//省略一部分代码

  
public void watchActivities() {
  // Make sure you don't get installed twice.
  stopWatchingActivities();
  application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}

ok,这里我们发现了一个Application提供的方法,registerActivityLifecycleCallbacks()。至于这个ActivityLifecycleCallbacks类则起到一个统一管理所有activity的生命周期的职责,ActivityRefWatcher这里则在每个Activity onDestroy的时候调用他的对应方法。其实也可以理解,检查内存泄漏本来就是在一个页面消亡的时候看弱引用对象是否被回收,如果没有被回收的话则怀疑是内存泄漏,然后再去通过分析快照文件等操作来确定是否内存泄漏。具体源码操作感兴趣的话可以自行跟踪,并不复杂。

因为Leakcanary是全局监听,所以ActivityLifecycleCallbacks这个方法就给了我们思路。

带生命周期的MVP模式提供的思路

MVP模式大家应该都很熟悉,Presenter作为具体业务逻辑的负责方,从View层彻底解藕了出来。但是有一个痛点就是Presenter无法感知View层的生命周期,当然你可以跟我说,我可以在BasePresenter里定义onCreate(),onResume()等等的接口,然后在view层的实现类里一个个去实现调用。这样不仅麻烦,而且麻烦,是真的麻烦。尤其MVP模式下基本上每个View相关都要写一个Presenter实现类、接口,View的接口,实现也就是Activity或者Fragment,很麻烦。

去年年底的时候Google给了我们一个好方法,Lifecycle组件。

我们可以将BasePresenter接口即成DefaultLifecycleObserver接口,然后在Activity实现类里调用

presenter = new Presenter(this);
getLifecycle().addObserver(presenter);

方法即可。看到addObserver方法凭直觉就应该知道这应该跟观察者模式有关。下一篇我打算总结一篇Android开源框架里关于观察者模式的总结,我发现Android模式中观察者模式出现的次数挺多的,还有一些框架在普通观察者模式的基础上做了一些优化,Lifecycle的源码我会在下一篇总结观察者模式的时候放到一起研究,这一篇重点在生命周期。

总结

Android开源项目这么多,看源码的目的主要还是为了了解其中的核心思想,我希望能把这么多的框架一些类似方向上的思想进行总结,希望对你有用。奋斗奋斗奋斗

猜你喜欢

转载自blog.csdn.net/jieqiang3/article/details/79839711
今日推荐