本篇内容介绍的是Glide关于安全方面的内容——生命周期监听。
Glide作为一个图片加载工具,拥有非常强大的功能,其中一项能力就是对生命周期的监听。简单来说,在你的ImageView所在的fragment或者Activity被回收或主动退出后,Glide能够自动进行响应的处理,以防止内存泄露以及不必要的网络和性能的浪费。
话不多说,开始整篇,我们从Glide最常用的代码入手:
Glide
.with(this)
.load(imageUrl)
.placeholder(com.avatr.ivi.common.widget.R.drawable.ic_image_default)
.error(com.avatr.ivi.common.widget.R.drawable.ic_image_default)
.transform(transformation, new RoundedCorners(10))
.into(image);
点进去看with
方法
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getContext()).get(fragment);
}
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
可以看到with
有多个重载方法,但不管哪个方法,调用逻辑都是一样的,首先看看getRetriever(context:Context)
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
这个方法就是从Glide实例中获取RequestManagerRetriever
,接着看Glide.get(context)
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
这段代码很简单,double check 初始化并返回Glide实例;
接着往下看checkAndInitializeGlide
经过基层调用,最终到了下面
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
List manifestModules = Collections.emptyList();
//....省略部分代码.....
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
try {
module.registerComponents(applicationContext, glide, glide.registry);
} catch (AbstractMethodError e) {
throw new IllegalStateException(
"Attempting to register a Glide v3 module. If you see this, you or one of your"
+ " dependencies may be including Glide v3 even though you're using Glide v4."
+ " You'll need to find and remove (or update) the offending dependency."
+ " The v3 module name is: "
+ module.getClass().getName(),
e);
}
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
重点是builder.build(applicationContext)
Glide build(@NonNull Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor,
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList();
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
GlideExperiments experiments = glideExperimentsBuilder.build();
//这里,创建的RequestManagerRetriever
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory, experiments);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
experiments);
}
这里创建了Glide的一系列组件模块,然后调用构造函数传入。当然这里不是我们本章的终点,继续往下看RequestManagerRetriever.get()
方法
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(
fragment.getContext(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getContext().getApplicationContext());
} else {
// In some unusual cases, it's possible to have a Fragment not hosted by an activity. There's
// not all that much we can do here. Most apps will be started with a standard activity. If
// we manage not to register the first frame waiter for a while, the consequences are not
// catastrophic, we'll just use some extra memory.
if (fragment.getActivity() != null) {
frameWaiter.registerSelf(fragment.getActivity());
}
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else if (activity instanceof FragmentActivity) {
return get((FragmentActivity) activity);
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
//...省略部分代码及注释
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get((FragmentActivity) activity);
}
// Standard Fragments.
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
get()
方法有多个多态方法,但到现在为止,我们常用的其实只有两个get(@NonNull FragmentActivity activity)
和get(@NonNull Fragment fragment)
,我们以前者为重点继续分析
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
//终点是这里
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
继续看supportFragmentGet
:
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// This is a bit of hack, we're going to start the RequestManager, but not the
// corresponding Lifecycle. It's safe to start the RequestManager, but starting the
// Lifecycle might trigger memory leaks. See b/154405040
if (isParentVisible) {
requestManager.onStart();
}
current.setRequestManager(requestManager);
}
return requestManager;
}
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
// If we have a pending Fragment, we need to continue to use the pending Fragment. Otherwise
// there's a race where an old Fragment could be added and retrieved here before our logic to
// add our pending Fragment notices. That can then result in both the pending Fragmeng and the
// old Fragment having requests running for them, which is impossible to safely unwind.
SupportRequestManagerFragment current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
- 首先,这段代码的意义很简单,构造一个
SupportRequestManagerFragment
类型的空的Fragment
,注入当前Activity,并确保该Activity有且只有一个该类型Fragment(为什么只需要一个,以及如何保证只有一个,大家可以自行探索);
- 然后,将
GlideLifecycle
实例注入到空Fragment;因为fragment会响应宿主activity的生命周期,这样就可以做到不侵入宿主(组件外部)activity的前提下监听其生命周期;进而在生命周期发生变化时,触发GlideLifecycle回调,控制GlideRequest工作状态。
OK,Glide图片加载的任务如何关联生命周期的流程我们已经明白,这里再次总结一下:
fragment可以响应宿主activity的生命周期回调,并且会先回调fragment;
这样做有一个好处,当我们的代码作为SDK时,可以在不侵入使用者代码的情况下,达到对生命周期的监听
fragment不仅可以响应activity的生命周期回调,而且可以响应其它例如
onActivityResult
,onConfigurationChange
,onRequestPermissionsResult
等等,同时,它还可以调用Activity的方法,例如startActivity
,getResources()
等其它方法
就是没有UI布局,不会显示出来的Fragment,它作为Activity的一部分,可以响应宿主Activity的所有生命周期以及其它的Activity回调;
利用空Fragment代理Activity实际上有很多实用的应用场景,以下就简单举例两种
class ContextHelper private constructor() {
private lateinit var mApp: Application
private val mStack = Stack()
companion object {
val INSTANCE: ContextHelper by lazy {
ContextHelper()
}
}
fun init(context: Context) {
if (context is Activity) {
mStack.add(context)
}
mApp = context.applicationContext as Application
mApp.registerActivityLifecycleCallbacks(object : ActivityLifecycleAdapter() {
override fun onActivityCreated(activity: Activity) {
mStack.add(activity)
notifyActivityChange(activity)
}
override fun onActivityDestroyed(activity: Activity) {
super.onActivityDestroyed(activity)
mStack.remove(activity)
if (mStack.isNotEmpty()) {
notifyActivityChange(mStack.peek())
} else {
notifyActivityChange(null)
}
}
})
}
private fun notifyActivityChange(activity: Activity?) {
for (each in mActivityChangeListeners) {
each.onTopActivityChange(activity)
}
}
private val mActivityChangeListeners = ArrayList()
fun addTopActivityChangeListener(listener: IOnTopActivityChangeListener) {
mActivityChangeListeners.add(listener)
}
fun removeTopActivityChangeListener(listener: IOnTopActivityChangeListener) {
mActivityChangeListeners.remove(listener)
}
/**
* TopActivity 变化监听
*/
interface IOnTopActivityChangeListener {
fun onTopActivityChange(activity: Activity?)
}
public fun getTopActivity(): Activity? {
try {
return mStack.peek()
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
public fun getApp(): Application {
return mApp
}
abstract class ActivityLifecycleAdapter : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
onActivityCreated(activity)
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
abstract fun onActivityCreated(activity: Activity)
}
基本道理大家都懂,就是在application
注册ActivityLifecycleCallbacks
,首先在Activity或者Application中初始化ContextHelper
,
这就获取到了TopActivity;
然后,在TopActivity中注入空Fragment
class AnySdkManager{
fun init(context:Context){
val topActivity = ContextHelper.INSTANCE.getTopActivity()
topActivity?.let {
addFragment(it)
}
ContextHelper.INSTANCE.addTopActivityChangeListener(object : IOnTopActivityChangeListener{
override fun onTopActivityChange(activity: Activity?) {
topActivity?.let {
addFragment(it)
}
}
})
}
private fun addFragment(activity: Activity) {
val sfm = (activity as AppCompatActivity).supportFragmentManager
var fragment = sfm.findFragmentByTag(TINDER_FRAGMENT)
if (fragment == null) {
fragment = ActivityProxyFragment()
mFragment = fragment as ActivityProxyFragment
sfm.beginTransaction().add(fragment, TINDER_FRAGMENT).commit()
Log.i(TAG, "addFragment: ")
}
}
}
监听到Activity变化后,就可以注入我们的空Fragment
public static class ConfigurationChangeFragment extends Fragment {
private static final String TAG = "ConfigurationChangeFrag";
private AssetManager mLastAssetManager;
private int mLastUiMode;
private IOnUiModeChangeListener mListener;
public ConfigurationChangeFragment() {
}
public void setOnUiModeChangeListener(IOnUiModeChangeListener listener) {
this.mListener = listener;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mLastAssetManager = context.getAssets();
mLastUiMode = context.getResources().getConfiguration().uiMode;
CommonUtilsLog.i(TAG, "onAttach: assetManager:" + context.getAssets());
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
CommonUtilsLog.i(TAG, "onConfigurationChanged: mLastUiMode:" + mLastUiMode + ",newUiMode:" + newConfig.uiMode + ",mLastAssetManager:" + mLastAssetManager + ",assetManager:" + getContext().getAssets());
if (mLastUiMode != newConfig.uiMode) {
mLastAssetManager = getContext().getAssets();
mLastUiMode = newConfig.uiMode;
if (mListener != null) {
mListener.onUiModeChange(newConfig);
}
}
}
public void release() {
mListener = null;
}
}
interface IOnUiModeChangeListener {
void onUiModeChange(Configuration newConfig);
}
这样就达到了利用空Fragment监听configurationChange
的目的;注入空Fragment要注意,一个Activity不要重复注入,还有多个Activity
也不要重复注入,否则会造成资源的浪费和回调重复等问题,这里只是简单示例,大家自行研究怎么解决这些问题;
监听/获取TopActivity和添加Fragment方法同上,但Fragment实现有所不同,如下:
class ActivityProxyFragment : Fragment() {
private lateinit var launcher: ActivityResultLauncher
private lateinit var mOnActivityResult: IOnActivityResultCallback
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val contract = object : ActivityResultContract() {
override fun createIntent(context: Context, input: Intent): Intent {
Log.i(TAG, "createIntent: ")
return input
}
override fun parseResult(resultCode: Int, intent: Intent?): ActivityResult {
Log.i(TAG, "parseResult: ")
return ActivityResult(resultCode, intent)
}
}
launcher = registerForActivityResult(contract) {
it.data
Log.i(TAG, "onActivityResult: resultCode:$it.resultCode")
mOnActivityResult.onActivityResult(it.resultCode, it.data)
}
}
fun startActivityForResult(
intent: Intent,
callback: IOnActivityResultCallback
) {
mOnActivityResult = callback
launcher.launch(intent)
}
interface IOnActivityResultCallback {
fun onActivityResult(resultCode: Int, data: Intent?)
}
companion object {
private const val TAG = "ActivityProxyFragment"
}
}
这样,在你的SDK代码中,就可以通过该ProxyFragment调用startActivityForResult
mFragment.startActivityForResult(
anyIntent,
object : ActivityProxyFragment.IOnActivityResultCallback {
override fun onActivityResult(resultCode: Int, data: Intent?) {
//do sth
}
})
实际fragment的应用场景还有很多,我这里只是简单举两个例子,实际场景可以让ProxyFragment尽可能的实现多种Activity的功能,然后注入。
此次分享到此结束,如有问题,希望大家批评指正,非常感谢。
恭祝各位看官老爷身体健康,诸事顺利,好运连绵