Android | 适可而止!看Glide如何把生命周期安排得明明白白

简介: Android | 适可而止!看Glide如何把生命周期安排得明明白白

前言


  • 图片模块是 App 中非常重要的一个组件,而 Glide 作为官方和业界双重认可的解决方案,其学习价值不必多言;
  • 在这篇文章里,我将分析 Glide 生命周期管理,主要分为三个层次的生命周期:Activity & 网络 & 内存。如果能帮上忙,请务必点赞加关注,这真的对我非常重要。


相关文章


提示: 本文源码基于Glide 4.11


目录

image.png


1. 概述


使用 Glide 加载图片非常简单,类似这样:


Glide.with(activity)
    .load(url)
    .into(imageView)
复制代码

相对地,取消加载也很简单,类似这样:


Glide.with(activity).clear(imageView)
复制代码


一般认为,应该及时取消不必要的加载请求,但这并不是必须的操作。因为 Glide 会在页面生命周期 / 网络变化时,自动取消加载或重新加载。


  • 页面生命周期

当页面不可见时暂停请求;页面可见时恢复请求;页面销毁时销毁请求。在 第 2 节,我将详细分析 Glide 的生命周期模块,主要包括了 Activity / Fragment 两个主体。

  • 网络连接状态

如果从 URL 加载图片,Glide 会监听设备的连接状态,并在重新连接到网络时重启之前失败的请求。在 第 3 节,我将详细分析 Glide 的网络连接状态监听模块

  • 内存状态

Glide 会监听内存状态,并根据不同的 level 来释放内存。在 第 4 节,我将详细分析 Glide 的内存状态监听模块


2. Activity / Fragment 生命周期监听


2.1 为什么要监听页面生命周期?


主要基于以下两个目的:


  • 以确保优先处理前台可见的 Activity / Fragment,提高资源利用率;
  • 在有必要时释放资源以避免在应用在后台时被杀死,提高稳定性。


提示: Low Memory Killer 会在合适的时机杀死进程,杀死优先级为:空进程->后台进程->服务进程->可见进程->前台进程。


2.2 三种生命周期作用域


首先,我们从 Glide 的入口方法入手:

Glide.java


private final RequestManagerRetriever requestManagerRetriever;
入口方法:
public static RequestManager with(Context context) {
    return getRetriever(context).get(context);
}
入口方法:
public static RequestManager with(Activity activity) {
    return getRetriever(activity).get(activity);
}
此处省略参数为 FragmentActivity、Fragment、View 的类似方法...
private static RequestManagerRetriever getRetriever(Context context) {
    其中,Glide.get(context) 基于 DCL 单例
    return Glide.get(context).getRequestManagerRetriever();
}
public RequestManagerRetriever getRequestManagerRetriever() {
    return requestManagerRetriever;
}
复制代码


可以看到,with(...)方法的返回值是RequestManager,而真正创建的地方在RequestManagerRetriever#get(...)中。


image.png

先说结论,根据传入的参数不同,将对应于 Application & Activity & Fragment 的作用域,具体如下:


线程 参数 作用域
子线程 / Application
主线程(下同) ApplicationContext/
ServiceContext
Application
/ FragmentActivity Activity
/ Activity Activity
/ Fragment Fragment
/ View Activity / Fragment
  • 1、Application 作用域


对于 Application 作用域的请求,它的生命周期是全局的,不与具体页面绑定。

RequestManagerRetriever.java


已简化
Application 域请求管理
private volatile RequestManager applicationManager;
private RequestManager getApplicationManager(@NonNull Context context) {
    源码基于 DCL 单例
    return applicationManager;
}
public RequestManager get(@NonNull Context context) {
    if (Util.isOnMainThread() && !(context instanceof Application)) {
        2、FragmentActivity
        if (context instanceof FragmentActivity) {
            return get((FragmentActivity) context);
        } 
        3、Activity
        else if (context instanceof Activity) {
            return get((Activity) context);
        }
    }
    1、Application
    return getApplicationManager(context);
  }
public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
        return get(activity.getApplicationContext());
    } else {
        见下文 ...
    }
}
复制代码


上面的代码已经非常简化了,主要关注以下几点:


1、Application 域对应的是 applicationManager,它是与RequestManagerRetriever 对象绑定的;


2、在子线程调用get(...),或者传入参数是 ApplicationContext & ServiceContext 时,对应的请求是 Application 域。


  • 2、Activity 作用域


RequestManagerRetriever.java


已简化,并略去子线程的分支
public RequestManager get(FragmentActivity activity) {
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(activity, fm, null, isActivityVisible(activity));
  }
public RequestManager get(Activity activity) {
    android.app.FragmentManager fm = activity.getFragmentManager();
    return fragmentGet(activity, fm, null, isActivityVisible(activity));
}
复制代码


可以看到,这里先获得了 FragmentActivity 的 FragmentManager,之后调用supportFragmentGet(...)获得 RequestManager。


提示:Activity 分支与 FragmentActivity 分支类似,我不重复分析了。


  • 3、Fragment 作用域

RequestManagerRetriever.java


已简化,并略去子线程的分支
public RequestManager get(Fragment fragment) {
    FragmentManager fm = fragment.getChildFragmentManager();
    return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}
复制代码


可以看到,这里先获得了 Fragment 的 FragmentManager(getChildFragmentManager()),之后调用supportFragmentGet(...)获得 RequestManager。

image.png


2.3 生命周期绑定


从上一节的分析知道,Activity 域和 Fragment 域都会调用supportFragmentGet(...)来获得 RequestManager,这一节我们专门分析这个方法:


RequestManagerRetriever.java


已简化(提示:这个方法必在主线程执行)
用于临时记录 FragmentManager - SupportRequestManagerFragment 的映射关系
final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>();
private RequestManager supportFragmentGet(
      Context context,
      FragmentManager fm,
      Fragment parentHint,
      boolean isParentVisible) {
    1、从 FragmentManager 中获取 SupportRequestManagerFragment
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    2、从该 Fragment 中获取 RequestManager
    RequestManager requestManager = current.getRequestManager();
    3、首次获取,则实例化 RequestManager
    if (requestManager == null) {
        3.1 实例化
        Glide glide = Glide.get(context);
        requestManager = factory.build(...);
        3.2 设置 Fragment 对应的 RequestMananger
        current.setRequestManager(requestManager);
    }
    return requestManager;
}
->  1、从 FragmentManager 中获取 SupportRequestManagerFragment
private SupportRequestManagerFragment getSupportRequestManagerFragment(FragmentManager fm, Fragment parentHint, boolean isParentVisible) {
    1.1 尝试获取 FRAGMENT_TAG 对应的 Fragment
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
        1.2 尝试从临时记录中获取 Fragment
        current = pendingSupportRequestManagerFragments.get(fm);
        1.3 实例化 Fragment
        if (current == null) {
            1.3.1 创建对象
            current = new SupportRequestManagerFragment();
            current.setParentFragmentHint(parentHint);
            1.3.2 如果父层可见,则调用 onStart() 生命周期
            if (isParentVisible) {
                current.getGlideLifecycle().onStart();
            }
            1.3.3 临时记录映射关系
            pendingSupportRequestManagerFragments.put(fm, current);
            1.3.4 提交 Fragment 事务
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            1.3.5 post 一个消息
            handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
        }
    }
    return current;
}
-> 1.3.5 post 一个消息
case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
    1.3.6 移除临时记录中的映射关系
    FragmentManager supportFm = (FragmentManager) message.obj;
    key = supportFm;
    removed = pendingSupportRequestManagerFragments.remove(supportFm);
break;
复制代码


上面的代码已经非常简化了,主要关注以下几点:


  • 1、从 FragmentManager 中获取 SupportRequestManagerFragment;
  • 2、从该 Fragment 中获取 RequestManager;
  • 3、首次获取,则实例化 RequestManager,后续从同一个 SupportRequestManagerFragment 中都获取的是这个 RequestManager。


其中,获取 SupportRequestManagerFragment 的方法更为关键:


  • 1.1 尝试获取FRAGMENT_TAG对应的 Fragment
  • 1.2 尝试从临时记录中获取 Fragment
  • 1.3 实例化 Fragment
  • 1.3.1 创建对象
  • 1.3.2 如果父层可见,则调用 onStart() 生命周期
  • 1.3.3 临时记录映射关系
  • 1.3.4 提交 Fragment 事务
  • 1.3.5 post 一个消息
  • 1.3.6 移除临时记录中的映射关系


其实一步步看下来,逻辑上不算复杂了,只有 “临时记录” 比较考验源码框架理解度。即:在提交 Fragment 事务之前,为什么需要先保存记录?


这是 为了避免 SupportRequestManagerFragment 在一个作用域中重复创建。 因为commitAllowingStateLoss()是将事务 post 到消息队列中的,也就是说,事务是异步处理的,而不是同步处理的。假设没有临时保存记录,一旦在事务异步等待执行时调用了Glide.with(...),就会在该作用域中重复创建 Fragment。


image.png

提示: 需要注意的是,异步不一定需要多线程,异步往往伴随着并发,但不是必须的,近期我将会分享一篇 Kotlin 协程的文章,会具体谈到这个观点,请关注!


2.4 生命周期监听


从上面的分析我们得知,Glide 为每个Activity 和 Fragment 作用域创建了一个无界面的 Fragment,这一节我们来分析 Glide 如何监听这个无界面 Fragment 的生命周期。

SupportRequestManagerFragment.java


private final ActivityFragmentLifecycle lifecycle;
public SupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
}
@Override
public void onStart() {
    super.onStart();
    lifecycle.onStart();
}
@Override
public void onStop() {
    super.onStop();
    lifecycle.onStop();
}
@Override
public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
}
@NonNull
ActivityFragmentLifecycle getGlideLifecycle() {
    return lifecycle;
}
复制代码


RequestManagerRetriever.java


-> 3.1 实例化 RequestManager
Glide glide = Glide.get(context);
requestManager = factory.build(glide, current.getGlideLifecycle(), 
    current.getRequestManagerTreeNode(), context);
RequestManager 工厂接口
public interface RequestManagerFactory {
    RequestManager build(
        Glide glide,
        Lifecycle lifecycle,
        RequestManagerTreeNode requestManagerTreeNode,
        Context context);
    }
默认 RequestManager 工厂接口实现类
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
    @Override
    public RequestManager build(
        Glide glide,
        Lifecycle lifecycle,
        RequestManagerTreeNode requestManagerTreeNode,
        Context context) {
            return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
        }
    };
}
复制代码

RequestManager.java

已简化
final Lifecycle lifecycle;
RequestManager(Glide glide, Lifecycle lifecycle, ...){
    ...
    this.lifecycle = lifecycle;
    添加监听
    lifecycle.addListener(this);
}
@Override
public synchronized void onDestroy() {
    ...
    移除监听
    lifecycle.removeListener(this);
}
复制代码


可以看到,实例化 RequestManager 时需要一个 com.bumptech.glide.manager.Lifecycle对象,这个对象是在无界面 Fragment 中创建的。当 Fragment 的生命周期变化时,就是通过这个 Lifecycle 对象将事件分发到 RequestManager。


ActivityFragmentLifecycle.java


class ActivityFragmentLifecycle implements Lifecycle {
    private final Set<LifecycleListener> lifecycleListeners =
      Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
    ...
}
复制代码


2.5 生命周期回调


现在我们来看 RequestManager 收到生命周期回调后的处理。

LifecycleListener.java


public interface LifecycleListener {
    void onStart();
    void onStop();
    void onDestroy();
}
复制代码

RequestManager.java


private final RequestTracker requestTracker;
public class RequestManager
    implements ComponentCallbacks2, LifecycleListener, ... {
    @Override
    public synchronized void onStop() {
        1、onStop() 时暂停任务(页面不可见)
        pauseRequests();
        targetTracker.onStop();
    }
    @Override
    public synchronized void onStart() {
        2、onStart() 时恢复任务(页面可见)
        resumeRequests();
        targetTracker.onStart();
    }
    @Override
    public synchronized void onDestroy() {
        3、onDestroy() 时销毁任务(页面销毁)
        targetTracker.onDestroy();
        for (Target<?> target : targetTracker.getAll()) {
            clear(target);
        }
        targetTracker.clear();
        requestTracker.clearRequests();
        lifecycle.removeListener(this);
        lifecycle.removeListener(connectivityMonitor);
        mainHandler.removeCallbacks(addSelfToLifecycle);
        glide.unregisterRequestManager(this);
    }
    public synchronized void pauseRequests() {
        requestTracker.pauseRequests();
    }
    public synchronized void resumeRequests() {
        requestTracker.resumeRequests();
    }
}
复制代码


主要关注以下几点:

  • 1、页面不可见时暂停请求(onStop() )
  • 2、页面可见时恢复请求(onStart() )
  • 3、页面销毁时销毁请求(onDestroy() )


3. 网络连接状态监听


Glide 会监听网络连接状态,并在网络重新连接时重新启动失败的请求。具体分析如下:


3.1 广播监听器


RequestManager.java


private final ConnectivityMonitor connectivityMonitor;
RequestManager(...){
    ...
    监听器
    connectivityMonitor = factory.build(context.getApplicationContext(), new RequestManagerConnectivityListener(requestTracker));
    ...
}
复制代码


可以看到,在 RequestManager 的构造器中,会构建一个ConnectivityMonitor对象。其中,默认的构建工厂是:


DefaultConnectivityMonitorFactory.java


public class DefaultConnectivityMonitorFactory implements ConnectivityMonitorFactory {
    private static final String TAG = "ConnectivityMonitor";
    private static final String NETWORK_PERMISSION = "android.permission.ACCESS_NETWORK_STATE";
    @Override
    public ConnectivityMonitor build(Context context, ConnectivityMonitor.ConnectivityListener listener) {
        1、检查是否授予监控网络状态的权限
        int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION);
        boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED;
        2、实例化不同的 ConnectivityMonitor
        return hasPermission
            ? new DefaultConnectivityMonitor(context, listener)
            : new NullConnectivityMonitor();
  }
}
复制代码


如果有网络监听权限,则实例化DefaultConnectivityMonitor


已简化
final class DefaultConnectivityMonitor implements ConnectivityMonitor {
    private final Context context;
    final ConnectivityListener listener;
    boolean isConnected;
    private boolean isRegistered;
    1、广播监听器
    private final BroadcastReceiver connectivityReceiver =
        new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                boolean wasConnected = isConnected;
                isConnected = isConnected(context);
                5、状态变化,回调
                if (wasConnected != isConnected) {
                    listener.onConnectivityChanged(isConnected);
                }
            }
        };
    DefaultConnectivityMonitor(Context context, ConnectivityListener listener) {
        this.context = context.getApplicationContext();
        this.listener = listener;
    }
    private void register() {
        if (isRegistered) {
            return;
        }
        2、注册广播监听器
        isConnected = isConnected(context);
        context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        isRegistered = true;
    }
    private void unregister() {
        if (!isRegistered) {
            return;
        }
        3、注销广播监听器
        context.unregisterReceiver(connectivityReceiver);
        isRegistered = false;
    }
    4、检查网络连通性
    boolean isConnected(Context context) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        return networkInfo.isConnected();
    }
    @Override
    public void onStart() {
        页面可见时注册
        register();
    }
    @Override
    public void onStop() {
        页面不可见时注销
        unregister();
    }
    @Override
    public void onDestroy() {
        // Do nothing.
    }
}
复制代码


可以看到,在页面可见时注册广播监听器,而在页面不可见时注销广播监听器。


3.2 网络连接变化回调


现在我们看 RequestManager 中是如何处理网络连接状态变化的。

RequestManager.java


private class RequestManagerConnectivityListener
      implements ConnectivityMonitor.ConnectivityListener {
    private final RequestTracker requestTracker;
    RequestManagerConnectivityListener(RequestTracker requestTracker) {
        this.requestTracker = requestTracker;
    }
    @Override
    public void onConnectivityChanged(boolean isConnected) {
        网络连接,重新开启请求
        if (isConnected) {
            synchronized (RequestManager.this) {
                requestTracker.restartRequests();
            }
        }
    }
}
复制代码


小结:如果应用有监控网络状态的权限,那么 Glide 会监听网络连接状态,并在网络重新连接时重新启动失败的请求。


4. 内存状态监听


这一节我们来分析 Glide 的内存状态监听模块,具体分析如下:

Glide.java


private static void initializeGlide(...) {
    ...
    applicationContext.registerComponentCallbacks(glide);
}
1、内存紧张级别
@Override
public void onTrimMemory(int level) {
    trimMemory(level);
}
2、低内存状态
@Override
public void onLowMemory() {
    clearMemory();
}
public void trimMemory(int level) {
    1.1 确保是主线程
    Util.assertMainThread();
    1.2 每个 RequestManager 处理内存紧张级别
    for (RequestManager manager : managers) {
        manager.onTrimMemory(level);
    }
    1.3 内存缓存处理内存紧张级别
    memoryCache.trimMemory(level);
    bitmapPool.trimMemory(level);
    arrayPool.trimMemory(level);
}
public void clearMemory() {
    1.2 确保是主线程
    Util.assertMainThread();
    1.2 内存缓存处理低内存状态
    memoryCache.clearMemory();
    bitmapPool.clearMemory();
    arrayPool.clearMemory();
}
复制代码


RequestManager.java


@Override
public void onTrimMemory(int level) {
    if (level == TRIM_MEMORY_MODERATE && pauseAllRequestsOnTrimMemoryModerate) {
        暂停请求
        pauseAllRequestsRecursive();
    }
}
复制代码


小结:在构建 Glide 时,会调用registerComponentCallbacks()进行全局注册, 系统在内存紧张的时候回调onTrimMemory(level)。而 Glide 则根据系统内存紧张级别(level)进行 memoryCache / bitmapPool / arrayPool 的回收,而 RequestManager 在 TRIM_MEMORY_MODERATE 级别会暂停请求。


5. 总结


  • 页面生命周期


当页面不可见时暂停请求;页面可见时恢复请求;页面销毁时销毁请求。

image.png


  • 网络连接状态

如果应用有监控网络状态的权限,那么 Glide 会监听网络连接状态,并在网络重新连接时重新启动失败的请求。


  • 内存状态

Glide 则根据系统内存紧张级别(level)进行 memoryCache / bitmapPool / arrayPool 的回收,而 RequestManager 在 TRIM_MEMORY_MODERATE 级别会暂停请求

目录
相关文章
|
6月前
|
数据库 Android开发 开发者
Android基础知识:请解释Activity的生命周期。
Android基础知识:请解释Activity的生命周期。
75 2
|
Android开发
Android一些特殊情况下的生命周期分析
Android一些特殊情况下的生命周期分析
63 0
|
4月前
|
Java Android开发
Android面试题经典之Glide取消加载以及线程池优化
Glide通过生命周期管理在`onStop`时暂停请求,`onDestroy`时取消请求,减少资源浪费。在`EngineJob`和`DecodeJob`中使用`cancel`方法标记任务并中断数据获取。当网络请求被取消时,`HttpUrlFetcher`的`cancel`方法设置标志,之后的数据获取会返回`null`,中断加载流程。Glide还使用定制的线程池,如AnimationExecutor、diskCacheExecutor、sourceExecutor和newUnlimitedSourceExecutor,其中某些禁止网络访问,并根据CPU核心数动态调整线程数。
132 2
|
1月前
|
Android开发 开发者 UED
深入理解安卓应用开发中的生命周期管理
本文旨在探讨安卓应用开发中生命周期管理的重要性,以及如何有效利用生命周期解决常见问题。通过分析安卓应用生命周期的不同阶段及其特点,提供实用的代码示例和调试技巧,帮助开发者优化应用性能,提升用户体验。
39 8
|
1月前
|
Java Android开发 UED
深入探索安卓应用开发中的生命周期管理:从创建到销毁的全过程
在安卓应用开发中,理解并妥善管理应用及活动(Activity)的生命周期至关重要。本文将详细解析从应用创建到销毁的整个生命周期过程,以及如何通过高效管理提升应用性能与用户体验。
68 4
|
3月前
|
Android开发 UED 开发者
探索安卓应用的生命周期管理
【8月更文挑战第31天】在开发安卓应用时,理解并妥善处理应用的生命周期是至关重要的。本文将通过浅显易懂的方式,带你了解安卓应用的生命周期,并通过代码示例展示如何在实际应用中进行有效的生命周期管理。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和实用技巧。
|
3月前
|
Java Android开发 Kotlin
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
36 0
|
4月前
|
缓存 编解码 安全
Android经典面试题之Glide的缓存大揭秘
Glide缓存机制包括内存和硬盘缓存。内存缓存使用弱引用的ActiveResources和LRU策略,硬盘缓存利用DiskLruCache。Engine.load方法首先尝试从内存和弱引用池加载,然后从LRU缓存中加载图片,增加引用计数并移出LRU。若缓存未命中,启动新任务或加入现有任务。内存大小根据设备内存动态计算,限制在0.4以下。DiskLruCache使用自定义读写锁,保证并发安全,写操作通过锁池管理,确保高效。
119 0
|
5月前
|
Android开发 UED
Android Activity的生命周期详解
Android Activity的生命周期详解
|
6月前
|
Android开发
Android中Glide加载Https图片失败的解决方案
Android中Glide加载Https图片失败的解决方案
441 1