图片加载之Glide使用

Glide是一个广泛应用于Android的图片加载框架,由谷歌在泰国开发者论坛上介绍。它提供简单的图片加载和展示方式,支持网络、本地、资源等多种图片来源。本文涵盖了Glide的依赖、使用、方法、缓存策略、生命周期管理、自定义配置等方面,详细讲解了如何在Android中使用Glide加载和处理图片。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简介

在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫Glide的图片加载库,作者是bumptech。这个库被广泛的运用在Google的开源项目中,包括2014Google I/O大会上发布的官方App

Glide是一款由Bump Technologies开发的图片加载框架,使得我们可以在Android平台上以极度简单的方式加载和展示图片。Glide默认使用HttpUrlConnection进行网络请求,为了让App保持一致的网络请求形式,可以让Glide使用我们指定的网络请求形式请求网络资源。

带你全面了解Glide 4的用法

二、依赖

1.jar

Github地址:https://github.com/bumptech/glide/releases

2.Gradle

dependencies {  
    compile 'com.github.bumptech.glide:glide:3.7.0'  
    compile 'com.android.support:support-v4:23.3.0'  
}

三、权限

<uses-permission android:name="android.permission.INTERNET" />

四、混淆

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
    **[] $VALUES;
    public *;
}

五、使用

查看Glide最新版本:http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22glide%22

Glide.with(this).load(url).into(view);

Glide.with()方法用于创建一个加载图片的实例。with()方法可以接收ContextActivityFragment或者FragmentActivity类型的参数,因此可供我们选择的范围非常广。在ActivityFragment或者FragmentActivity中调用with()方法时都可以直接传this,不在这些类中时可获取当前应用程序的ApplicationContext传入with()方法中。特别需要注意的是with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是ActivityFragment或者FragmentActivity的实例,那么当其被销毁时图片加载也会停止,如果传入的是ApplicationContext时只有当应用程序被杀掉的时候图片加载才会停止。

 Glide.with(Context context);// 绑定Context
 Glide.with(Activity activity);// 绑定Activity
 Glide.with(FragmentActivity activity);// 绑定FragmentActivity
 Glide.with(Fragment fragment);// 绑定Fragment

load()方法用于指定待加载的图片资源。Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等。

into()方法用于图片显示的对应ImageView

Glide支持加载gif图片,其内部会自动判断图片格式,并且可以正确的将它解析并显示出来。

使用Glide加载图片不用担心内存浪费,甚至是内存溢出的问题。因为Glide不会直接将图片的完整尺寸全部加载到内存中,而是用多少加载多少。Glide会自动判断ImageView的大小,然后只将这么大的图片像素加载到内存当中,帮助我们节省内存开支。

六、方法

1.指定图片格式

如果调用了.asBitmap()方法,则.load()中的参数指向的可以是一个静态图片也可以是GIF图片,如果是一张GIF图片,则加载之后只会展示GIF图片的第一帧。

如果调用的.asGif()方法,则.load()方法中的参数指向的必须是一个GIF图片,如果是一张静态图片,则图片加载完成之后展示的只是图片占位符(如果没有设置图片占位符,则什么也不展示)。

//显示静态图片(若加载的是gif图那么就会显示第一帧的图片)
.asBitmap()
//显示动态图片(若加载的是静态图会加载失败)
.asGif()

2.指定占位图显示

//加载时显示的图片
.placeholder(R.drawable.image_load)
//加载失败时显示的图片
.error(R.drawable.image_error)

3.设置缓存

//禁止内存缓存
.skipMemoryCache(true)
//禁止磁盘缓存(Glide默认缓存策略是:DiskCacheStrategy.RESULT)
.diskCacheStrategy(DiskCacheStrategy.NONE)
//缓存参数
//ALL:缓存源资源和转换后的资源(即缓存所有版本图像,默认行为)
//NONE:不作任何磁盘缓存,然而默认的它将仍然使用内存缓存
//SOURCE:仅缓存源资源(原来的全分辨率的图像)
//RESULT:缓存转换后的资源(最终的图像,即降低分辨率后的或者是转换后的)

4.设置加载尺寸

以下方法可以设置图片加载之后展示的宽度值和高度值,前提是目标ImageView的宽度和高度都设置为wrap_content

//加载图片为100*100像素的尺寸
.override(100, 100)

5.设置图片缩放

如果调用了.centerCrop()方法,则显示图片的时候短的一边填充容器,长的一边跟随缩放;如果调用了.fitCenter()方法,则显示图片的时候长的一边填充容器,短的一边跟随缩放;这两个方法可以都调用,如果都调用,则最终显示的效果是后调用的方法展示的效果。

//它是一个裁剪技术,即缩放图像让它填充到ImageView界限内并且裁剪额外的部分,ImageView可能会完全填充,但图像可能不会完整显示
.centerCrop()
//它是一个裁剪技术,即缩放图像让图像都测量出来等于或小于ImageView的边界范围,该图像将会完全显示,但可能不会填满整个ImageView
.fitCenter()

6.设置资源加载优先级

.priority(Priority.HIGH)

7.设置圆角或圆形图片

//圆角图片
.transform(new GlideRoundTransform(this))
//圆形图片
.transform(new GlideCircleTransform(this))

8.设置缩略图

设置以下方法后会先加载这张图片的sizeMultiplier倍的缩略图到目标ImageView中,然后再慢慢加载完整的图片,sizeMultiplier值的范围是0~1

//方法一:系数需在(0,1)之间,0.5f为原图的1/2,这样会先加载缩略图然后在加载全图
.thumbnail(0.5f)

//方法二:自定义资源图片为缩略图
DrawableRequestBuilder<Integer> thumbnailRequest = Glide
        .with(context)
        .load(R.drawable.image_example);
Glide.with(context)
        .load(url)
        .thumbnail(thumbnailRequest)
        .into(view);

9.设置动画

加载图片时所展示的动画,可以是Animator类型的属性动画,也可以是int类型的动画资源。这个动画只在第一次加载的时候会展示,以后都会从缓存中获取图片,因此也就不会展示动画了。

//设置加载动画
.animate(R.anim.alpha_in)
//淡入淡出动画,也是默认动画,动画默认的持续时间是300毫秒
.crossFade()
//移除所有动画
.dontAnimate()

10.加载本地视频(相当于一张缩略图)

//只能加载本地视频(显示的只是视频的第一帧图像,相当于一张缩略图,不能播放视频),网络视频无法加载
String files = Environment.getExternalStorageDirectory().getAbsolutePath() + "/glide.avi";
Glide.with(this).load(files).into(view);

11.设置加载内容

//示例一
Glide.with(context).load(url).into(new SimpleTarget<GlideDrawable>() {
    @Override
    public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
        //图片加载完成
        view.setImageDrawable(resource);
    }
});

//示例二
Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>() {
    @Override
    public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
        //图片加载完成
        view.setImageBitmap(bitmap);
    }
});

12.设置监听请求接口

Glide.with(this).load(url).listener(new RequestListener<String, GlideDrawable>() {
    @Override
    public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
        //加载异常
        return false;
    }

    @Override
    public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
        //加载成功
        //view.setImageDrawable(resource);
        return false;
    }
}).into(view);

13.设置取消或恢复请求

以下两个方法是为了保证用户界面的滑动流畅而设计的。当在ListView中加载图片的时候,如果用户滑动ListView的时候继续加载图片,就很有可能造成滑动不流畅、卡顿的现象,这是由于Activity需要同时处理滑动事件以及Glide加载图片。Glide为我们提供了这两个方法,让我们可以在ListView等滑动控件滑动的过程中控制Glide停止加载或继续加载,可以有效的保证界面操作的流畅。

//当列表在滑动的时候可以调用pauseRequests()取消请求
Glide.with(context).pauseRequests();
//当列表滑动停止时可以调用resumeRequests()恢复请求
Glide.with(context).resumeRequests();
// ListView滑动时触发的事件
lv.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
            case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
            case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
                // 当ListView处于滑动状态时,停止加载图片,保证操作界面流畅
                Glide.with(MainActivity.this).pauseRequests();
                break;
            case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
                // 当ListView处于静止状态时,继续加载图片
                Glide.with(MainActivity.this).resumeRequests();
                break;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    }
});

14.获取缓存大小

new GetDiskCacheSizeTask(textView).execute(new File(getCacheDir(), DiskCache.Factory.DEFAULT_DISK_CACHE_DIR));
private class GetDiskCacheSizeTask extends AsyncTask<File, Long, Long> {

    private final TextView resultView;

    public GetDiskCacheSizeTask(TextView resultView) {
        this.resultView = resultView;
    }

    @Override
    protected void onPreExecute() {
        resultView.setText("Calculating...");
    }

    @Override
    protected void onProgressUpdate(Long... values) {
        super.onProgressUpdate(values);
    }

    @Override
    protected Long doInBackground(File... dirs) {
        try {
            long totalSize = 0;
            for (File dir : dirs) {
                publishProgress(totalSize);
                totalSize += calculateSize(dir);
            }
            return totalSize;
        } catch (RuntimeException ex) {
            final String message = String.format("Cannot get size of %s: %s", Arrays.toString(dirs), ex);
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    resultView.setText("error");
                    Toast.makeText(resultView.getContext(), message, Toast.LENGTH_LONG).show();
                }
            });
        }
        return 0L;
    }

    @Override
    protected void onPostExecute(Long size) {
        String sizeText = android.text.format.Formatter.formatFileSize(resultView.getContext(), size);
        resultView.setText(sizeText);
    }

    private long calculateSize(File dir) {
        if (dir == null) return 0;
        if (!dir.isDirectory()) return dir.length();
        long result = 0;
        File[] children = dir.listFiles();
        if (children != null)
            for (File child : children)
                result += calculateSize(child);
        return result;
    }
}

15.清除内存缓存

//可以在UI主线程中进行
Glide.get(this).clearMemory();

16.清除磁盘缓存

//需要在子线程中执行
Glide.get(this).clearDiskCache();

17.图片裁剪、模糊、滤镜等处理

图片处理库:https://github.com/wasabeef/glide-transformations

依赖:

compile 'jp.wasabeef:glide-transformations:2.0.2'
// If you want to use the GPU Filters
compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'

可使用GenericRequestBuilder或其子类的transform()bitmapTransform()方法设置图片处理。

示例:圆角处理

 Glide.with(mContext)
    .load(R.drawable.image_example)
    .bitmapTransform(new RoundedCornersTransformation(mContext, 30, 0, RoundedCornersTransformation.CornerType.BOTTOM))
    .into(imageView);

可实现Transformation接口,进行更灵活的图片处理,如进行简单地圆角处理。

public class RoundedCornersTransformation implements Transformation<Bitmap> {

    private BitmapPool mBitmapPool;
    private int mRadius;

    public RoundedCornersTransformation(Context context, int mRadius) {
        this(Glide.get(context).getBitmapPool(), mRadius);
    }

    public RoundedCornersTransformation(BitmapPool mBitmapPool, int mRadius) {
        this.mBitmapPool = mBitmapPool;
        this.mRadius = mRadius;
    }

    @Override
    public Resource<Bitmap> transform(Resource<Bitmap> resource, int outWidth, int outHeight) {
        //从其包装类中拿出Bitmap
        Bitmap source = resource.get();
        int width = source.getWidth();
        int height = source.getHeight();
        Bitmap result = mBitmapPool.get(width, height, Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        }
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
        canvas.drawRoundRect(new RectF(0, 0, width, height), mRadius, mRadius, paint);
        //返回包装成Resource的最终Bitmap
        return BitmapResource.obtain(result, mBitmapPool);
    }

    @Override
    public String getId() {
        return "com.wiggins.glide.widget.GlideCircleTransform(radius=" + mRadius + ")";
    }
}

自定义图片处理时为了避免创建大量Bitmap以及减少GC,可以考虑重用Bitmap,这就需要使用BitmapPool,例如从Bitmap池中取一个Bitmap,用这个Bitmap生成一个Canvas, 然后在这个Canvas上画初始的Bitmap并使用MatrixPaint或者Shader处理这张图片。为了有效并正确重用Bitmap需要遵循以下三条准则:

  1. 永远不要把transform()传给你的原始resource或原始Bitmaprecycle()了,更不要放回BitmapPool,因为这些都自动完成了。值得注意的是,任何从BitmapPool取出的用于自定义图片变换的辅助Bitmap,如果不经过transform()方法返回,就必须主动放回BitmapPool或者调用recycle()回收。
  2. 如果你从BitmapPool拿出多个Bitmap或不使用你从BitmapPool拿出的一个Bitmap,一定要返回extrasBitmapPool
  3. 如果你的图片处理没有替换原始resource(例如由于一张图片已经匹配了你想要的尺寸,你需要提前返回),transform()方法就返回原始resource或原始Bitmap

例如:

private static class MyTransformation extends BitmapTransformation {

    public MyTransformation(Context context) {
        super(context);
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Bitmap result = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);
        // 如果BitmapPool中找不到符合该条件的Bitmap,get()方法会返回null,就需要我们自己创建Bitmap了
        if (result == null) {
            // 如果想让Bitmap支持透明度,就需要使用ARGB_8888
            result = Bitmap.createBitmap(outWidth, outHeight, Bitmap.Config.ARGB_8888);
        }
        //创建最终Bitmap的Canvas.
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setAlpha(128);
        // 将原始Bitmap处理后画到最终Bitmap中
        canvas.drawBitmap(toTransform, 0, 0, paint);
        // 由于我们的图片处理替换了原始Bitmap,就return我们新的Bitmap就行。
        // Glide会自动帮我们回收原始Bitmap。
        return result;
    }

    @Override
     public String getId() {
         // Return some id that uniquely identifies your transformation.
         return "com.wiggins.glide.MyTransformation";
     }
}

18.GlidePalette调色板

GlidePalettehttps://github.com/florent37/GlidePalette

七、GlideModule使用

GlideModule是一个抽象方法,全局改变Glide行为的一种方式,通过全局GlideModule配置Glide,用GlideBuilder设置选项,用Glide注册ModelLoader等。所有的GlideModule实现类必须是public的,并且只拥有一个空的构造器,以便在Glide延迟初始化时,可以通过反射将它们实例化。

1.自定义一个GlideModule

public class MyGlideModule implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        // Apply options to the builder here.
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        // register ModelLoaders here.
    }
}

2.AndroidManifest.xml注册

<meta-data
    android:name="com.wiggins.glide.MyGlideModule"
    android:value="GlideModule" />

3.混淆处理

-keepnames class com.wiggins.glide.MyGlideModule
# or more generally
#-keep public class * implements com.bumptech.glide.module.GlideModule

4.多个GlideModule冲突问题

GlideModule不能指定调用顺序,所以应该避免不同的GlideModule之间有冲突的选项设置,可以考虑将所有的设置都放到一个GlideModule里面,或者排除掉某个manifest文件的某个Module

<meta-data
    android:name="com.wiggins.glide.MyGlideModule"
    tools:node="remove" />

5.GlideBuilder设置选项

5.1 设置Glide内存缓存大小

MemoryCache用来把resources缓存在内存里,以便能马上能拿出来显示。默认情况下Glide使用LruResourceCache,我们可以通过它的构造器设置最大缓存内存大小。

//获取系统分配给应用的总内存大小
int maxMemory = (int) Runtime.getRuntime().maxMemory();
//设置图片内存缓存占用八分之一
int memoryCacheSize = maxMemory / 8;
//设置内存缓存大小
builder.setMemoryCache(new LruResourceCache(memoryCacheSize));

获取默认的使用内存

//MemoryCache和BitmapPool的默认大小由MemorySizeCalculator类决定,MemorySizeCalculator会根据给定屏幕大小可用内存算出合适的缓存大小,这也是推荐的缓存大小,我们可以根据这个推荐大小做出调整
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();

5.2 设置Glide磁盘缓存大小

//方式一
//指定的是数据的缓存地址
File cacheDir = context.getExternalCacheDir();
//最多可以缓存多少字节的数据
int diskCacheSize = 1024 * 1024 * 30;
//设置磁盘缓存大小
builder.setDiskCache(new DiskLruCacheFactory(cacheDir.getPath(), "glide", diskCacheSize));

//方式二
//存放在data/data/xxxx/cache/
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "glide", diskCacheSize));

//方式三
//存放在外置文件
builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, "glide", diskCacheSize));

5.3 设置图片解码格式

默认格式RGB_565相对于ARGB_88884字节/像素可以节省一半的内存,但是图片质量就没那么高了,而且不支持透明度。

builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);

5.4 设置BitmapPool缓存内存大小

Bitmap池用来允许不同尺寸的Bitmap被重用,这可以显著地减少因为图片解码像素数组分配内存而引发的垃圾回收。默认情况下Glide使用LruBitmapPool作为Bitmap池,LruBitmapPool采用Lru算法保存最近使用的尺寸的Bitmap,我们可以通过它的构造器设置最大缓存内存大小。

builder.setBitmapPool(new LruBitmapPool(memoryCacheSize));

5.5 设置用来检索cache中没有的ResourceExecutorService

//为了使缩略图请求正确工作,实现类必须把请求根据Priority优先级排好序
builder.setDiskCacheService(ExecutorService service);
builder.setResizeService(ExecutorService service);

6.集成网络框架

Glide包含一些小的、可选的集成库,目前Glide集成库当中包含了访问网络操作的VolleyOkHttp,也可以通过GlideModelLoader接口自己写网络请求。

6.1 将OkHttp集成到Glide当中

6.1.1 添加依赖

dependencies {
    //OkHttp 2.x
    compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'
    compile 'com.squareup.okhttp:okhttp:2.7.5'

    //OkHttp 3.x
    compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
    compile 'com.squareup.okhttp3:okhttp:3.2.0'
}

结尾的@aar可以将库中的AndroidManifest.xml文件一起导出,Gradle自动合并必要的GlideModuleAndroidManifest.xml,然后使用所集成的网络连接,所以不用再将以下文本添加到项目的AndroidManifest.xml文件中:

<meta-data
    android:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule"
    android:value="GlideModule" />

6.1.2 创建OkHttp集成库的GlideModule

<meta-data
    android:name="com.wiggins.glide.okhttp.OkHttpGlideModule"
    android:value="GlideModule" />

6.1.3 混淆配置

-keep class com.wiggins.glide.okhttp.OkHttpGlideModule
#or
-keep public class * implements com.bumptech.glide.module.GlideModule

注意:
1.OkHttp 2.xOkHttp 3.x需使用不同的集成库;
2. Gradle会自动将OkHttpGlideModule合并到应用的manifest文件中;
3. 如果你没有对所有的GlideModule配置混淆规则(即没有使用-keep public class * implements com.bumptech.glide.module.GlideModule),则需要把OkHttpGlideModule进行混淆配置:-keep class com.wiggins.glide.okhttp.OkHttpGlideModule

6.2 将Volley集成到Glide当中

6.2.1 添加依赖

dependencies {
    compile 'com.github.bumptech.glide:volley-integration:1.4.0@aar'
    compile 'com.mcxiaoke.volley:library:1.0.8'
}

6.2.2 创建Volley集成库的GlideModule

<meta-data
    android:name="com.wiggins.glide.volley.VolleyGlideModule"
    android:value="GlideModule" />

6.2.3 混淆配置

-keep class com.wiggins.glide.volley.VolleyGlideModule
#or
-keep public class * implements com.bumptech.glide.module.GlideModule

7.使用ModelLoader自定义数据源

如果需要根据不同的要求请求不同尺寸不同质量的图片,这时我们就可以使用自定义数据源。

7.1 定义处理URL接口

public interface IDataModel {
    String buildDataModelUrl(int width, int height);
}

7.2 实现不同的处理URL接口

public class JpgDataModel implements IDataModel {

    private String dataModelUrl;

    public JpgDataModel(String dataModelUrl) {
        this.dataModelUrl = dataModelUrl;
    }

    @Override
    public String buildDataModelUrl(int width, int height) {
        //http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg?imageView2/1/w/200/h/200/format/jpg
        return String.format("%s?imageView2/1/w/%d/h/%d/format/jpg", dataModelUrl, width, height);
    }
}

7.3 实现ModelLoader

public class MyDataLoader extends BaseGlideUrlLoader<IDataModel> {

    public MyDataLoader(Context context) {
        super(context);
    }

    public MyDataLoader(ModelLoader<GlideUrl, InputStream> urlLoader) {
        super(urlLoader, null);
    }

    @Override
    protected String getUrl(IDataModel model, int width, int height) {
        return model.buildDataModelUrl(width, height);
    }

    public static class Factory implements ModelLoaderFactory<IDataModel, InputStream> {

        @Override
        public ModelLoader<IDataModel, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new MyDataLoader(factories.buildModelLoader(GlideUrl.class, InputStream.class));
        }

        @Override
        public void teardown() {
        }
    }
}

7.4 根据不同的要求采用不同的策略加载图片

//加载jpg图片
Glide.with(this).using(new MyDataLoader(this)).load(new JpgDataModel(imageUrl)).into(imageView);

7.5 如何跳过.using()

public class MyGlideModule implements GlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {

    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        glide.register(IDataModel.class, InputStream.class, new MyDataLoader.Factory());
    }
}
//加载jpg图片
Glide.with(this).load(new JpgDataModel(imageUrl)).into(imageView);

八、特点

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

九、优点

  1. 多样化媒体加载,支持GifWebPVideo及缩略图以等类型;
  2. 生命周期集成,提供多种方式与生命周期绑定,可以更好的让加载图片请求的生命周期动态管理起来;
  3. 高效的缓存策略
    3.1 支持MemoryDisk图片缓存;
    3.2 缓存相应大小的图片尺寸(Picasso只会缓存原始尺寸图片,而Glide会根据你ImageView的大小来缓存相应大小的图片尺寸,因此Glide会比Picasso加载的速度要快);
    3.3 内存开销小(Picasso默认的是ARGB_8888格式,而Glide默认的Bitmap格式是RGB_565格式,这个内存开销大约可以减小一半)。

Android关于图片内存计算共有四种,分别是:

  1. ALPHA_8:每个像素占用1byte内存;
  2. ARGB_4444:每个像素占用2byte内存;
  3. RGB_565:每个像素占用2byte内存;
  4. ARGB_8888:每个像素占用4byte内存(默认、色彩最细腻、显示质量最高、占用的内存也最大、8bit = 1byte);

举例:一个32位的PNG = ARGB_8888 = 1204 x 1024,那么占用空间是:1024 x 1024 x (32/8) = 4,194,304kb = 4M左右,在解析图片的时候为了避免OOM和节省内存,最好使用ARGB_4444模式(节省一半的内存空间)。

十、缺点

  1. 使用方法复杂:由于Glide功能强大,所以使用的方法非常多,其源码也相对的复杂;
  2. 包较大:Glide(v3.7.0)的大小约465kb

十一、使用场景

  1. 需要更多的内容表现形式(如Gif、缩略图等);
  2. 更高的性能要求(缓存、加载速度等)。

十二、特别说明

1.ImageViewsetTag问题

问题描述:如果使用Glideinto(imageView)ImageView设置图片的同时使用ImageViewsetTag(final Object tag)方法,将会导致java.lang.IllegalArgumentException: You must not call setTag() on a view Glide is targeting异常。因为GlideViewTarget中通过view.setTag(tag)view.getTag()标记请求的,由于Android 4.0之前Tag存储在静态map里,如果Glide使用setTag(int key, final Object tag)方法标记请求则可能会导致内存泄露,所以Glide默认使用view.setTag(tag)标记请求,你就不能重复调用了。

解决办法:如果你需要为ImageView设置Tag,必须使用setTag(int key, final Object tag)getTag(int key)方法,其中key必须是合法的资源id以确保key的唯一性,典型做法就是在资源文件中声明type="id"item资源。

2.placeholder()导致的图片变形问题

问题描述:使用.placeholder()方法在某些情况下会导致图片显示的时候出现图片变形的情况。这是因为Glide默认开启的crossFade动画导致的TransitionDrawable绘制异常,具体描述可以查看https://github.com/bumptech/glide/issues/363。根本原因就是你的placeholder图片和你要加载显示的图片宽高比不一样,而AndroidTransitionDrawable无法很好地处理不同宽高比的过渡问题,这是Android也是GlideBug

解决办法:使用.dontAnimate()方法禁用过渡动画,或者使用animate()方法自己写动画,再或者自己修复TransitionDrawable的问题。

3.ImageView的资源回收问题

问题描述:默认情况下Glide会根据with()使用的ActivityFragment的生命周期自动调整资源请求以及资源回收。但是如果有很占内存的FragmentActivity不销毁而仅仅是隐藏视图,那么这些图片资源就没办法及时回收,即使是GC的时候。

解决办法:可以考虑使用WeakReference,如:

final WeakReference<ImageView> imageViewWeakReference = new WeakReference<>(imageView);
ImageView target = imageViewWeakReference.get();
if (target != null) {
    Glide.with(context).load(uri).into(target);
}

4.由于Bitmap复用导致的在某些设备上图片错乱的问题

问题描述: Glide默认使用BitmapPool的方式对应用中用到的Bitmap进行复用,以减少频繁的内存申请和内存回收,而且默认使用的Bitmap模式为RGB565以减少内存开销。但在某些设备上(通常在Galaxy系列5.X设备上很容易复现)某些情况下会出现图片加载错乱的问题,具体详见https://github.com/bumptech/glide/issues/601。原因初步确定是OpenGL纹理渲染异常。

解决办法:GlideModule使用PREFER_ARGB_8888Glide4.X已经默认使用该模式了),虽然内存占用比RGB565更多一点,但可以更好地处理有透明度Bitmap的复用问题,或者禁用Bitmap复用setBitmapPool(new BitmapPoolAdapter())来修复这个问题(不推荐这种处理方式)。

5.异步线程完成后加载图片的崩溃问题

问题描述:通常情况下异步线程会被约束在Activity生命周期内,所以异步线程完成后使用Glide加载图片是没有问题的。但如果你的异步线程在Activity销毁时没有取消掉,那么异步线程完成后Glide就无法为一个已销毁的Activity加载图片资源,抛出的异常如下(在with()方法中就进行判断并抛出异常):

java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
    at com.bumptech.glide.manager.RequestManagerRetriever.assertNotDestroyed(RequestManagerRetriever.java:134)
    at com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:102)
    at com.bumptech.glide.Glide.with(Glide.java:653)
    at com.frank.glidedemo.TestActivity.onGetDataCompleted(TestActivity.java:23)
    at com.frank.glidedemo.TestActivity.access$000(TestActivity.java:10)
    at com.frank.glidedemo.TestActivity$BackgroundTask.onPostExecute(TestActivity.java:46)
    at com.frank.glidedemo.TestActivity$BackgroundTask.onPostExecute(TestActivity.java:28)
    at android.os.AsyncTask.finish(AsyncTask.java:632)
    at android.os.AsyncTask.access$600(AsyncTask.java:177)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:157)
    at android.app.ActivityThread.main(ActivityThread.java:5356)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
    at dalvik.system.NativeStart.main(Native Method)

解决办法:正确管理Background Threads(异步线程),当Activity停止或销毁时,停止所有相关的异步线程及后续的UI操作,或者加载前使用isFinishing()isDestroyed()进行限制(不建议这种处理方式)。

项目地址 ☞ 传送门

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值