参考资料:Glide全面解析 面试提问之Glide
一、Glide与其他图片加载框架的对比
Glide:
- 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
- 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)
- 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
- 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),
- 加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
Fresco:
- 最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)
- 大大减少OOM(在更底层的Native层对OOM进行处理,图片将不再占用App的内存)
- 适用于需要高性能加载大量图片的场景
对于一般App来说,Glide完全够用,而对于图片需求比较大的App,为了防止加载大量图片导致OOM,Fresco 会更合适一些。并不是说用Glide会导致OOM,Glide默认用的内存缓存是LruCache,内存不会一直往上涨。
二、Glide的基本使用
1、添加依赖
api 'com.github.bumptech.glide:glide:4.11.0'
2、AndroidManifest.xml中声明网络权限
<uses-permission android:name="android.permission.INTERNET" />
3、展示图片的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_glide_demo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"/>
</LinearLayout>
4、代码处理展示图片
public class GlideDemoActivity extends AppCompatActivity {
@BindView(R.id.iv_glide_demo)
ImageView ivGlideDemo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo_glide);
ButterKnife.bind(this);
String url = "http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg";
Glide.with(this)
.load(url)
.into(ivGlideDemo);
}
}
以上是最基础的用法,也就是关键的三步走:先with(),再load(),最后into()。
这一行代码,包括加载网络上的图片、加载手机本地的图片、加载应用资源中的图片等。
- Glide.with()方法用于创建一个加载图片的实例。with()方法可以接收Context、Activity或者Fragment类型的参数。
- 如果调用的地方既不在Activity中也不在Fragment中呢?也没关系,我们可以获取当前应用程序的ApplicationContext,传入到with()方法当中。
注意:
with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。
如果传入的是ApplicationContext,只有当应用程序被杀掉的时候,图片加载才会停止。
5、Glide的扩展内容
String url = "http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg";
String gifUrl ="http://p1.pstatp.com/large/166200019850062839d3";
Glide.with(this)
.load(url)
.placeholder(R.mipmap.pic_loading)// 占位符
.diskCacheStrategy(DiskCacheStrategy.NONE)//禁用Glide的缓存功能 测试占位图功能的额外配置
.error(R.mipmap.pic_failed)//异常占位图
.override(200, 200)// 指定图片大小
.into(ivGlideDemo);
三、源码分析
1、从上面的用法可知,Glide的基础用法就三个方法,即Glide三部曲(with、load、into)
方法名 | 作用 |
---|---|
with | 空白Fragment管理生命周期机制 |
load | 构建RequestBuilder对象 |
into | 运行队列,等待队列/活动缓存、内存缓存 |
2、with源码分析
首先,有一个结论要知道,Glide内部有一监听onDestroy自动回收,无需手动回收,那这是如何做到的呢?
这就是with方法的作用了,在对应Activity中创建了一个空白Fragment并可随时监听到Activity的生命周期状态并根据状态做对应处理,具体是如何监听处理的看源码分析。
Glide的with分析,可根据以下几大模块来循序渐进的分析:
——生命周期的作用域Application/Fragment/Activity
——生命周期的绑定
——生命周期的监听
——生命周期的回调
2.1 声明周期的作用域
首先,跟着Glide的源码走,with()方法的代码如下:
Glide.java
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
以上with方法有多个重载函数,其中参数分别对应有以下几种:
其中getRetriever调用的却是同一个,只是后面的RequestManagerRetriever.get()的处理有区别
@NonNull
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.get(context) 基于 DCL 单例
@NonNull
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;
}
由此可看出with(…) 方法的返回值是 RequestManager ,而真正创建的地方在RequestManagerRetriever#get(…) 中;
RequestManagerRetriever.java
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
// 子线程走这里
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
@NonNull
public RequestManager get(@NonNull 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
// Only unwrap a ContextWrapper if the baseContext has a non-null application context.
// Context#createPackageContext may return a Context without an Application instance,
// in which case a ContextWrapper may be used to attach one.
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
with是很多函数的重载,以上是以Activity为作用域的源码,根据传入的参数不同,将对应于 Application Activity Fragment 的作用域,具体如下:
——子线程:作用域Application,无空白Fragment
——主线程:加一空白Fragment
传入参数 | 作用域 |
---|---|
view | Activity/Fragment |
Fragment | Fragment |
Activity | Activity |
context | Application |
Activity/Fragment/FragmentActivity作用域属于一类 都是一样的 都会搞一个空白的Fragment去监听Activity/Fragment/FragmentActivity, Application作用域是另外一类,不会搞空白的Fragment去监听;
2.2 生命周期的绑定
上面生命周期作用域的get方法中Activity和Fragment域都会调用supportFragmentGet()方法来获得 RequestManager,就此展开分析:
RequestManagerRetriever.java
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
// 从 FragmentManager 中获取 SupportRequestManagerFragment
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
//从该 Fragment 中获取 RequestManager
RequestManager requestManager = current.getRequestManager();
// 首次获取,则实例化 RequestManager
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);
// 设置 Fragment 对应的 RequestMananger
current.setRequestManager(requestManager);
}
return requestManager;
}
注意:这个方法必须在主线程执行,因为子线程不可能调用到这里来
getSupportRequestManagerFragment方法分析
@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
// 尝试获取 FRAGMENT_TAG 对应的 Fragment
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
// 尝试从临时记录中获取Fragment
current = pendingSupportRequestManagerFragments.get(fm);
// 实例化Fragment
if (current == null) {
// 创建对象
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
// 若父层可见则调用onStart()生命周期
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
// 临时记录映射关系
pendingSupportRequestManagerFragments.put(fm, current);
// 提交Fragment事务
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
// post消息——为了移除临时记录中的映射关系
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
以上代码分析中重点关心下面三点:
第一点:从 FragmentManager 中获取 SupportRequestManagerFragment;
第二点:从该 Fragment 中获取 RequestManager;
第三点:首次获取,则实例化 RequestManager,后续从同一个SupportRequestManagerFragment 中都获取的是这个 RequestManager;
整个的关键核心在 getSupportRequestManagerFragment函数:
第一步:尝试获取 FRAGMENT_TAG 对应的 Fragment
第二步:尝试从临时记录中获取 Fragment
第三步:实例化 Fragment
第一点: 创建对象
第二点:如果父层可见,则调用 onStart() 生命周期
第三点:临时记录映射关系
第四点:提交 Fragment 事务
第五点:post 一个消息
第六点:移除临时记录中的映射关系
问题:在提交Fragment 事务之前,为什么需要先保存记录?
———为了避免 SupportRequestManagerFragment 在一个作用域中重复创建。
因为 commitAllowingStateLoss() 是将事务 post 到消息队列中的,也就是说,事务是异步处理的,而不是同步处理的。假设没有临时保存记录,一旦在事务异步等待执行时调用了 Glide.with(…) ,就会在该作用域中重复创建 Fragment。
2.3 生命周期的监听
框架为每个Activity 和 Fragment 作用域创建了一个无UI的Fragment,现在来分析 Glide 如何监听这个无界面 Fragment 的生命周期
SupportRequestManagerFragment.java
private final ActivityFragmentLifecycle lifecycle;
private final RequestManagerTreeNode requestManagerTreeNode =
new SupportFragmentRequestManagerTreeNode();
private final Set<SupportRequestManagerFragment> childRequestManagerFragments = new HashSet<>();
@Nullable private SupportRequestManagerFragment rootRequestManagerFragment;
@Nullable private RequestManager requestManager;
@Nullable private Fragment parentFragmentHint;
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
@VisibleForTesting
@SuppressLint("ValidFragment")
public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
......
@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();
}
RequesstManagerRetriver.java
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// 实例化 RequestManager
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
// RequestManager 工厂接口
public interface RequestManagerFactory {
@NonNull
RequestManager build(
@NonNull Glide glide,
@NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode requestManagerTreeNode,
@NonNull Context context);
}
// 默认 RequestManager 工厂接口实现类
private static final RequestManagerFactory DEFAULT_FACTORY =
new RequestManagerFactory() {
@NonNull
@Override
public RequestManager build(
@NonNull Glide glide,
@NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode requestManagerTreeNode,
@NonNull Context context) {
return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
}
};
RequestManager.java
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.context = context;
.....
if (Util.isOnBackgroundThread()) {
mainHandler.post(addSelfToLifecycle);
} else {
// 添加监听
lifecycle.addListener(this);
}
.......
}
@Override
public synchronized void onDestroy() {
......
// 移除监听
lifecycle.removeListener(this);
......
}
以上代码流程可知实例化 RequestManager 时需要一个 Lifecycle对象,这个对象是在无界面 Fragment 中创建的,当 Fragment 的生命周期变化时,就是通过这个Lifecycle 对象将事件分发到 RequestManager
2.4 生命周期的回调
当 RequestManager 收到生命周期回调后的处理如下:
public interface Lifecycle {
void addListener(@NonNull LifecycleListener listener);
void removeListener(@NonNull LifecycleListener listener);
}
public interface LifecycleListener {
void onStart();
void onStop();
void onDestroy();
}
故RequestManager会implements LifecycleListener 接口并调用函数做相对应处理:
@Override
public synchronized void onStart() {
// Activity/Fragment 可见时恢复请求
resumeRequests();
targetTracker.onStart();
}
@Override
public synchronized void onStop() {
// Activity/Fragment 不可见时暂停请求
pauseRequests();
targetTracker.onStop();
}
@Override
public synchronized void onDestroy() {
// Activity/Fragment 销毁时销毁请求
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);
}
3 Glide的load分析
4 Glide的into分析(重点)