Skip to content

Latest commit

 

History

History
450 lines (347 loc) · 18.4 KB

Glide简介(下).md

File metadata and controls

450 lines (347 loc) · 18.4 KB

Glide简介(下)

image

官网:GlideMaven Central

Glide的回调:Targets

假设我们不想将加载的图片显示到ImageView上,而是只想得到对应的BitmapGlide提供了一种可以通过Targets获取Bitmap的方法。 Targetcallback没什么不同,都是在通过Glide的异步线程下载和处理后返回结果。

Glide提供了多个不同目的的targets,我们先从BaseTarget开始。

BaseTarget
private BaseTarget target = new BaseTarget<BitmapDrawable>() {  
  @Override
  public void onResourceReady(BitmapDrawable bitmap, Transition<? super BitmapDrawable> transition) {
    // do something with the bitmap
    // for demonstration purposes, let's set it to an imageview
    imageView1.setImageDrawable(bitmap);
  }

  @Override
  public void getSize(SizeReadyCallback cb) {
    cb.onSizeReady(SIZE_ORIGINAL, SIZE_ORIGINAL);
  }

  @Override
  public void removeCallback(SizeReadyCallback cb) {}
};

private void loadImageSimpleTarget() {  
  GlideApp
    .with(context) // could be an issue!
    .load(eatFoodyImages[0])
    .into(target);
}

需要重写getSize方法,并且调用cb.onSizeReady(SIZE_ORIGINAL, SIZE_ORIGINAL);来保证Glide使用最高质量的值。当然,也可以传递一个具体的值,例如 根据View的大小返回。

Target注意事项

除了刚介绍的Target的回调系统的一个简单版本,你要学会额外两件事。

第一个是BaseTarget对象的定义。Java/Android可以允许在.into()内匿名定义,但这会显著增加在Glide处理完图片请求前Android垃圾回收清理匿名target对象的可能性。最终,会导致图片被加载了,但是回调永远不会被调用。所以,请确保将你的回调定义为一个字段对象,防止被万恶的Android垃圾回收给清理掉。

第二个关键部分是Glide.with(context)。这个问题实际上是Glide一个特性问题:当你传递了一个context,例如当前appactivity,当activity停止后,Glide会自动停止当前的请求。这种整合到app生命周期内是非常有用的,但也是很难处理的。如果你的target是独立于app的生命周期。这里的解决方案是使用application的contextapp自己停止运行的时候,Glide会只取消掉图片的请求。请记住,再次提醒,如果你的请求需要在activity的生命周期以外,使用下面的代码:

private void loadImageSimpleTargetApplicationContext() {  
    GlideApp
        .with(context.getApplicationContext() ) // safer!
        .load(eatFoodyImages[1] 
        .asBitmap()
        .into(target2);
}
设置具体尺寸的Target

通过Target的另一个问题就是它没有具体的尺寸。如果在.into()方法的参数中传递一个ImageView,Glide会根据ImageView的大小来控制图片的尺寸。例如在加载一个1000x1000的 图片时,但是要显示到的ImageView的大小是250x250Glide会把该图片裁剪到小的尺寸来节省处理时间和内存。显示,在使用Targets时,这种方式并没有效果,因为根本不知道所需要的图片的大小。但是,如果你知道图片的最终所需要的尺寸时,你可以在callback中指定图片的尺寸来节省内存。

private BaseTarget target2 = new BaseTarget<BitmapDrawable>() {  
  @Override
  public void onResourceReady(BitmapDrawable bitmap, Transition<? super BitmapDrawable> transition) {
    // do something with the bitmap
    // for demonstration purposes, let's set it to an imageview
    imageView2.setImageDrawable(bitmap);
  }

  @Override
  public void getSize(SizeReadyCallback cb) {
    cb.onSizeReady(250, 250);
  }

  @Override
  public void removeCallback(SizeReadyCallback cb) {}
};

private void loadImageSimpleTargetApplicationContext() {  
  GlideApp
    .with(context.getApplicationContext()) // safer!
    .load(eatFoodyImages[1])
    .into(target2);
}

正如上面通过cb.onSizeReady(250, 250);来指定尺寸。

ViewTarget

我们不直接使用ImageView的原因会有很多。我们可以通过上面的方式直接获取Bitmap。假如现在有一个自定义ViewGlide并不支持加载图片到自定义View中,因为它并不知道要 将图片设置到该View中的哪个位置。然而,Glide提供了ViewTargets来让这种问题变的非常简单。 下面是我们的自定义view:

public class FutureStudioView extends FrameLayout {  
    ImageView iv;
    TextView tv;

    public void initialize(Context context) {
        inflate( context, R.layout.custom_view_futurestudio, this );

        iv = (ImageView) findViewById( R.id.custom_view_image );
        tv = (TextView) findViewById( R.id.custom_view_text );
    }

    public FutureStudioView(Context context, AttributeSet attrs) {
        super( context, attrs );
        initialize( context );
    }

    public FutureStudioView(Context context, AttributeSet attrs, int defStyleAttr) {
        super( context, attrs, defStyleAttr );
        initialize( context );
    }

    public void setImage(Drawable drawable) {
        iv = (ImageView) findViewById( R.id.custom_view_image );

        iv.setImageDrawable( drawable );
    }
}

因为该View并没有继承ImageView所以不能直接使用.into()方法。我们可以创建一个ViewTarget来替代.into()方法进行使用:

FutureStudioView customView = (FutureStudioView) findViewById( R.id.custom_view );

viewTarget = new ViewTarget<FutureStudioView, BitmapDrawable>(customView) {  
  @Override
  public void onResourceReady(BitmapDrawable bitmap, Transition<? super BitmapDrawable> transition) {
    this.view.setImage(bitmap);
  }
};

GlideApp  
    .with(context.getApplicationContext()) // safer!
    .load(eatFoodyImages[2])
    .into(viewTarget);

调试及Debug

可以通过adb命令来开启Glide的调试log adb shell setprop log.tag.GenericRequest DEBUG

获取异常信息

Glide不能直接通过GenericRequest类获取日志,但是我们可以获取异常信息。例如,当一个图片获取不到时,glide会抛出一个异常并且显示.error() 方法设置的错误图片。如果想明确哪里出了异常,可以通过.listener()方法传递一个listener对象进去。

首先,创建一个listener对象作为一个成员变量,避免被垃圾回收:

private RequestListener<Bitmap> requestListener = new RequestListener<Bitmap>() {  
  @Override
  public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
    // todo log exception to central service or something like that

    // important to return false so the error placeholder can be placed
    return false;
  }

  @Override
  public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
    // everything worked out, so probably nothing to do
    return false;
  }
};

GlideApp  
    .with(context)
    .asBitmap()
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .listener(requestListener)
    .error(R.drawable.cupcake)
    .into(imageViewPlaceholder);

配置第三方网络库

  • OkHttp
// image loading library Glide
compile 'com.github.bumptech.glide:glide:4.1.1'  
annotationProcessor 'com.github.bumptech.glide:compiler:4.1.1'

// Glide's OkHttp2 Integration 
compile 'com.github.bumptech.glide:okhttp-integration:4.1.1@aar'  
compile 'com.squareup.okhttp:okhttp:2.7.5'  
  • Volley
// image loading library Glide
compile 'com.github.bumptech.glide:glide:4.1.1'  
annotationProcessor 'com.github.bumptech.glide:compiler:4.1.1'

// Glide's Volley Integration 
compile 'com.github.bumptech.glide:volley-integration:4.1.1@aar'  
compile 'com.android.volley:volley:1.0.0'  
  • OkHttp3
// image loading library Glide
compile 'com.github.bumptech.glide:glide:4.1.1'  
annotationProcessor 'com.github.bumptech.glide:compiler:4.1.1'

// Glide's OkHttp3 Integration 
compile 'com.github.bumptech.glide:okhttp3-integration:4.1.1@aar'  
compile 'com.squareup.okhttp3:okhttp:3.8.1'  

Modules定制Glide

Glide modules是一个全局改变Glide行为的抽象的方式。你需要创建Glide的实例,来访问GlideBuilder。可以通过创建一个公共的类,实现AppGlideModule的接口来定制Glide,这个接口提供了两个调整不同参数的方法,大多数情况下我们会用第一个方法pplyOptions(Context context, GlideBuilder builder).

@GlideModule
public class FutureStudioAppGlideModule extends AppGlideModule {  
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {

    }

    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {

    }
}

在上面的applyOptions方法的参数中有GlideBuilder对象,我们来看一下他的方法:

  • .setMemoryCache(MemoryCache memoryCache)
  • .setBitmapPool(BitmapPool bitmapPool)
  • .setDiskCache(DiskCache.Factory diskCacheFactory)
  • .setDiskCacheService(ExecutorService service)
  • .setResizeService(ExecutorService service)
  • .setDecodeFormat(DecodeFormat decodeFormat)

由于Glide默认使用将bitmap使用RGB565解析,下面我们就通过GlideModule来将图片设置为ARGB8888

@GlideModule
public class FutureStudioAppGlideModule extends AppGlideModule {  
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
    }

    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {

    }
}

接受自签名Https证书

Glide内部使用标准的HTTPUrlConnection去下载图片。Glide也提供两个集成库。这三个方法优点是在安全设置上都是相当严格的。唯一的不足之处是当你从一个使用HTTPS,还是self-signed的服务器下载图片时,Glide并不会下载或者显示图片,因为self-signed认证会被认为存在安全问题。

这样,你会需要去实现能够接受self-signed认证的网络栈。幸运地,我们已经实现并用过一个“不安全的”OkHttpClient。由于它提供给了一个需要集成的常规OkHttpClient,我们只需要拷贝并粘贴这个类:

public class UnsafeOkHttpClient {  
    public static OkHttpClient getUnsafeOkHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[] {
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

            // Create an ssl socket factory with our all-trusting manager
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
            builder.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

            OkHttpClient okHttpClient = builder.build();
            return okHttpClient;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

该类关闭了所有SSL认证检查。
接下来我们需要将上面的方法集成到GlideModule中。Glide使用一个ModelLoader去链接到数据模型创建一个具体的数据类型。我们的例子中,我们需要创建一个ModelLoader,它连接到一个URL,通过GlideUrl类响应并转化为输入流。Glide需要能够创建我们的新ModelLoader的实例,所以我们在.register()方法中传入一个工厂:

@GlideModule
public class UnsafeOkHttpGlideModule extends LibraryGlideModule {  
    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {
        OkHttpClient client = UnsafeOkHttpClient.getUnsafeOkHttpClient();
        registry.replace(GlideUrl.class, InputStream.class,
                new OkHttpUrlLoader.Factory(client));
    }
}

自定义缓存

Glide使用MemorySizeCalculator类来设置内存缓存的大小和bitmap poolbitmap pool会把图片保存到应用的栈内存中。合适的bitmap pool尺寸是非常必要的,因为要避免 太大会导致内存频繁的被回收。
我们可以很简单的获取到GlideMemorySizeCalculator类和默认的值:

MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context).build();  
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();  
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();  

那么如何自定义缓存大小呢?

@GlideModule
public class CustomCachingGlideModule extends AppGlideModule {  
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context).build();
        int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
        int defaultBitmapPoolSize = calculator.getBitmapPoolSize();

        int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);
        int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);

        builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize));
        builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize));
    }

    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {
        // nothing to do here
    }
}
  • 自定义硬盘缓存

Glide提供了两种硬盘缓存方式InternalCacheDiskCacheFactoryExternalCacheDiskCacheFactory

@GlideModule
public class CustomCachingGlideModule extends AppGlideModule {  
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        // set disk cache size & external vs. internal
        int cacheSize100MegaBytes = 104857600;

        builder.setDiskCache(
                new InternalCacheDiskCacheFactory(context, cacheSize100MegaBytes));

        //builder.setDiskCache(
        //        new ExternalCacheDiskCacheFactory(context, cacheSize100MegaBytes));
    }

    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {
        // nothing to do here
    }
}

上面的配置只是更改硬盘缓存的大小,并不会改变缓存目录,如果需要将缓存文件位置修改为指定位置,需要使用DiskLruCacheFactory类。

// or any other path
String downloadDirectoryPath = Environment.getDownloadCacheDirectory().getPath(); 

builder.setDiskCache(  
        new DiskLruCacheFactory( downloadDirectoryPath, cacheSize100MegaBytes )
);

// In case you want to specify a cache sub folder (i.e. "glidecache"):
//builder.setDiskCache(
//    new DiskLruCacheFactory( downloadDirectoryPath, "glidecache", cacheSize100MegaBytes ) 
//);

缓存总结

通过上面的设置可以发现,Glide一共使用了三种缓存方式:

  • Memory cache needs to implement: MemoryCache
  • Bitmap pool needs to implement BitmapPool
  • Disk cache needs to implement: DiskCache

参考