美文网首页Android进阶之路Android技术知识Android开发
Android性能优化——Glide巨图加载原理分析,为何微信疯

Android性能优化——Glide巨图加载原理分析,为何微信疯

作者: 谁动了我的代码 | 来源:发表于2022-08-26 21:34 被阅读0次

    简介

    Glide 是目前非常流行的图片加载第三方开源库,而且功能极其强大,内部代码复杂程度也极其大。

    Glide是纯Java写的Android端开源图片加载库,能够帮助我们下载、缓存、展示多种格式图片,也包括GIF格式 。

    特点:

    • (1)使用简单
    • (2)可配置度高,自适应程度高
    • (3)支持常见图片格式 Jpg png gif webp
    • (4)支持多种数据源 网络、本地、资源、Assets 等
    • (5)高效缓存策略 支持Memory和Disk图片缓存 默认Bitmap格式采用RGB_565内存使用至少减少一半
    • (6)生命周期集成 根据Activity/Fragment生命周期自动管理请求
    • (7)高效处理Bitmap 使用Bitmap Pool使Bitmap复用,主动调用recycle回收需要回收的Bitmap,减小系统回收压力

    原理

    Glide库的资源复用:

    • Android的内存申请几乎都在new的时候发生,而new较大对象(比如Bitmap时),更加容易触发GC_FOR_ALLOW。所以Glide尽量的复用资源来防止不必要的GC_FOR_ALLOC引起卡顿。
    • 最显著的内存复用就是内存LruResourceCache(第一次从网络或者磁盘上读取到Resource时,并不会保存到LruCache当中,当Resource被release时,也就是View不在需要此Resource时,才会进入LruCache当中)
    • 还有BitmapPool(Glide会尽量用图片池来获取到可以复用的图片,获取不到才会new,而当LruCache触发Evicted时会把从LruCache中淘汰下来的Bitmap回收,也会把transform时用到的中间Bitmap加以复用及回收)

    Glide库图片池:

    • 4.4以前是Bitmap复用必须长宽相等才可以复用
    • 4.4及以后是Size>=所需就可以复用,只不过需要调用reconfigure来调整尺寸
    • Glide用AttributeStategy和SizeStrategy来实现两种策略
    • 图片池在收到传来的Bitmap之后,通过长宽或者Size来从KeyPool中获取Key(对象复用到了极致,连Key都用到了Pool),然后再每个Key对应一个双向链表结构来存储。每个Key下可能有很多个待用Bitmap
    • 取出后要减少图片池中记录的当前Size等,并对Bitmap进行eraseColor(Color.TRANSPAENT)操作确保可用

    Glide加载发起流程:

    1. Glide.with(context)创建RequestManager
      • RequestManager负责管理当前context的所有Request
      • Context可以传Fragment、Activity或者其他Context,当传Fragment、Activity时,当前页面对应的Activity的生命周期可以被RequestManager监控到,从而可以控制Request的pause、resume、clear。这其中采用的监控方法就是在当前activity中添加一个没有view的fragment,这样在activity发生onStart onStop onDestroy的时候,会触发此fragment的onStart onStop onDestroy。
      • RequestManager用来跟踪众多当前页面的Request的是RequestTracker类,用弱引用来保存运行中的Request,用强引用来保存暂停需要恢复的Request。
    2. Glide.with(context).load(url)创建需要的Request
      • 通常是DrawableTypeRequest,后面可以添加transform、fitCenter、animate、placeholder、error、override、skipMemoryCache、signature等等
      • 如果需要进行Resource的转化比如转化为Byte数组等需要,可以加asBitmap来更改为BitmapTypeRequest
      • Request是Glide加载图片的执行单位
    3. Glide.with(context).load(url).into(imageview)
      • 在Request的into方法中会调用Request的begin方法开始执行
      • 在正式生成EngineJob放入Engine中执行之前,如果并没有事先调用override(width, height)来指定所需要宽高,Glide则会尝试去获取imageview的宽和高,如果当前imageview并没有初始化完毕取不到高宽,Glide会通过view的ViewTreeObserver来等View初始化完毕之后再获取宽高再进行下一步

    Glide加载资源:

    • GlideBuilder在初始化Glide时,会生成一个执行机Engine
    • Engine中包含LruCache缓存及一个当前正在使用的active资源Cache(弱引用)
    • activeCache辅助LruCache,当Resource从LruCache中取出使用时,会从LruCache中remove并进入activeCache当中
    • Cache优先级LruCache>activeCache
    • Engine在初始化时要传入两个ExecutorService,即会有两个线程池,一个用来从DiskCache获取resource,另一个用来从Source中获取(通常是下载)
    • 线程的封装单位是EngineJob,有两个顺序状态,先是CacheState,在此状态先进入DiskCacheService中执行获取,如果没找到则进入SourceState,进到SourceService中执行下载

    Glide常用的加载方法

    1、加载图片到imageView

    1. Glide.with(Context context).load(Strint url).into(ImageView imageView); 
    

    2、各种形式的图片加载到ImageView

    1. // 加载本地图片 
    2. File file = new File(getExternalCacheDir() + "/image.jpg"); 
    3. Glide.with(this).load(file).into(imageView); 
    4. // 加载应用资源 
    5. int resource = R.drawable.image; 
    6. Glide.with(this).load(resource).into(imageView); 
    7. // 加载二进制流 
    8. byte[] image = getImageBytes(); 
    9. Glide.with(this).load(image).into(imageView); 
    10. // 加载Uri对象 
    11. Uri imageUri = getImageUri(); 
    12. Glide.with(this).load(imageUri).into(imageView); 
    

    3、加载带有占位图

    1. Glide.with(this).load(url).placeholder(R.drawable.loading).into(imageView); 
    

    占位图目的为在目的图片还未加载出来的时候,提前展示给用户的一张图片;

    4、加载失败 放置占位符

    1. Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error) 
    2.    .diskCacheStrategy(DiskCacheStrategy.NONE)//关闭Glide的硬盘缓存机制 
    3.    .into(imageView); 
    4. //DiskCacheStrategy.NONE:表示不缓存任何内容。 
    5. //DiskCacheStrategy.SOURCE:表示只缓存原始图片。 
    6. //DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。 
    7. //DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。 
    

    5、加载指定格式的图片--指定为静止图片

    1. Glide.with(this) 
    2.    .load(url) 
    3.    .asBitmap()//只加载静态图片,如果是git图片则只加载第一帧。 
    4.    .placeholder(R.drawable.loading) 
    5.    .error(R.drawable.error) 
    6.    .diskCacheStrategy(DiskCacheStrategy.NONE) 
    7.    .into(imageView); 
    

    6、加载动态图片

    1. Glide.with(this) 
    2.    .load(url) 
    3.    .asGif()//加载动态图片,若现有图片为非gif图片,则直接加载错误占位图。 
    4.    .placeholder(R.drawable.loading) 
    5.    .error(R.drawable.error) 
    6.    .diskCacheStrategy(DiskCacheStrategy.NONE) 
    7.    .into(imageView); 
    

    7、加载指定大小的图片

    1. Glide.with(this) 
    2.    .load(url) 
    3.    .placeholder(R.drawable.loading) 
    4.    .error(R.drawable.error) 
    5.    .diskCacheStrategy(DiskCacheStrategy.NONE) 
    6.    .override(100, 100)//指定图片大小 
    7.    .into(imageView); 
    

    8、关闭框架的内存缓存机制

    1. Glide.with(this) 
    2.    .load(url) 
    3.    .skipMemoryCache(true) //传入参数为false时,则关闭内存缓存。 
    4.    .into(imageView); 
    

    9、关闭硬盘的缓存

    1. Glide.with(this) 
    2.    .load(url) 
    3.    .diskCacheStrategy(DiskCacheStrategy.NONE)   //关闭硬盘缓存操作 
    4.    .into(imageView); 
    5. //其他参数表示: 
    6. //DiskCacheStrategy.NONE:表示不缓存任何内容。 
    7. //DiskCacheStrategy.SOURCE:表示只缓存原始图片。 
    8. //DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。 
    9. //DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。 
    

    10、当引用的 url 存在 token 时解决方法

    1. public class MyGlideUrl extends GlideUrl { 
    2.   private String mUrl; 
    3.   public MyGlideUrl(String url) { 
    4.     super(url); 
    5.     mUrl = url; 
    6.   } 
    7.   @Override 
    8.   public String getCacheKey() { 
    9.     return mUrl.replace(findTokenParam(), ""); 
    10.   } 
    11.   private String findTokenParam() { 
    12.     String tokenParam = ""; 
    13.     int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token="); 
    14.     if (tokenKeyIndex != -1) { 
    15.       int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1); 
    16.       if (nextAndIndex != -1) { 
    17.         tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1); 
    18.       } else { 
    19.         tokenParam = mUrl.substring(tokenKeyIndex); 
    20.       } 
    21.     } 
    22.     return tokenParam; 
    23.   } 
    24. } 
    

    然后加载图片的方式为:

    1. Glide.with(this) 
    2.    .load(new MyGlideUrl(url)) 
    3.    .into(imageView); 
    

    11、利用Glide将图片加载到不同控件或加载成不同使用方式

    (1)、拿到图片实例

    1. //1、通过自己构造 target 可以获取到图片实例 
    2. SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() { 
    3.   @Override 
    4.   public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 
    5.     imageView.setImageDrawable(resource); 
    6.   } 
    7. }; 
    8. //2、将图片实例记载到指定的imageview上,也可以做其他的事情 
    9. public void loadImage(View view) { 
    10.   String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg"; 
    11.   Glide.with(this) 
    12.      .load(url) 
    13.      .into(simpleTarget); 
    14. } 
    

    (2)、将图片加载到任何位置

    1. /* 
    2. *将图片加载为控件背景 
    3. */ 
    4. public class MyLayout extends LinearLayout { 
    5.   private ViewTarget<MyLayout, GlideDrawable> viewTarget; 
    6.   public MyLayout(Context context, AttributeSet attrs) { 
    7.     super(context, attrs); 
    8.     viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) { 
    9.       @Override 
    10.       public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 
    11.         MyLayout myLayout = getView(); 
    12.         myLayout.setImageAsBackground(resource); 
    13.       } 
    14.     }; 
    15.   } 
    16.   public ViewTarget<MyLayout, GlideDrawable> getTarget() { 
    17.     return viewTarget; 
    18.   } 
    19.   public void setImageAsBackground(GlideDrawable resource) { 
    20.     setBackground(resource); 
    21.   } 
    22. } 
    23. //引用图片到指定控件作为背景 
    24. public class MainActivity extends AppCompatActivity { 
    25.   MyLayout myLayout; 
    26.   @Override 
    27.   protected void onCreate(Bundle savedInstanceState) { 
    28.     super.onCreate(savedInstanceState); 
    29.     setContentView(R.layout.activity_main); 
    30.     myLayout = (MyLayout) findViewById(R.id.background); 
    31.   } 
    32.   public void loadImage(View view) { 
    33.     String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg"; 
    34.     Glide.with(this) 
    35.        .load(url) 
    36.        .into(myLayout.getTarget()); 
    37.   } 
    38. } 
    

    12、Glide 实现预加载

    1. //a、预加载代码 
    2. Glide.with(this) 
    3.    .load(url) 
    4.    .diskCacheStrategy(DiskCacheStrategy.SOURCE) 
    5.    .preload(); 
    6. //preload() 有两种重载 
    7.  // 1、带有参数的重载,参数作用是设置预加载的图片大小; 
    8. //2、不带参数的表示加载的图片为原始尺寸; 
    9. //b、使用预加载的图片 
    10. Glide.with(this) 
    11.    .load(url) 
    12.    .diskCacheStrategy(DiskCacheStrategy.SOURCE) 
    13.    .into(imageView); 
    

    总结流程图

    相关文章

      网友评论

        本文标题:Android性能优化——Glide巨图加载原理分析,为何微信疯

        本文链接:https://www.haomeiwen.com/subject/qhjxnrtx.html