Kotlin编程之Glide V4(使用OkHttp3作为传输层)。

Android Glide4 异步图片框架

  • 简介篇: Glide框架

  • 迁移篇:Glide V4 框架新特性(Migrating from v3 to v4)

  • 基础篇:Android开发中使用Glide V4 中Generated API特性

  • 常用篇:Android Glide设置默认图片、异常图片为圆形图片

  • 进级篇:Kotlin编程开发之Glide V4使用OkHttp3作为传输层

前言

Glide默认使用HttpUrlConnection的网络堆栈,也可以使用Google的Volley库和Squareas的OkHttp库来替代。

Integration Libraries(集成库)


1. volley-integration库

  • 介绍

    Volley不是非常高效的加载图像,因为Volley是将远程服务器返回的数据复制到字节数组中。虽然Volley尝试重新使用这些字节数组,但是对于中等或者超大图像,他的回收率相对较差。故,当Volley与Glide一起结合使用时,Volley会占用大量内存来加载图像。与Glide的默认网络库相比较,Volley具备在网络较差可以重试的优势。

  • 注意点

    在使用volley-integration库时候,要禁用Volley的磁盘缓存或者Glide的磁盘缓存(二选一),若是不这样,Volley与Glide会在磁盘中缓存相同的数据。

  • Gradle 中添加集成库:

    compile "com.github.bumptech.glide:volley-integration:4.0.0-RC1"
  • 集成库的源码: https://github.com/bumptech/glide/tree/master/integration/volley

2. okhttp3-integration库:

  • 介绍

    OkHttp是一个比Cronet或者Volley更低API使用的网络库,同时还支持SPDY。OkHttp与Glide结合使用会有更好的性能,加载图占用的内存比Volley少,产生更少的垃圾。OkHttp是一个很合理的选择,它支持任何一个Android系统版本中使用,同时OkHttp具备其他强大的功能,你会在项目中使用到它。

  • Gradle 中添加集成库:

    compile "com.github.bumptech.glide:okhttp3-integration:4.0.0-RC1"
  • 集成库的源码: https://github.com/bumptech/glide/tree/master/integration/okhttp3

除了此之外,Glide还具备recyclerview-integration库,这里不做介绍。

根据项目需求,自定义Glide集成OkHttp3库


需求

当Glide自带的集成库,可以满足项目中大部分需求。当集成库满足不了项目需求的时候,就该自己手写自定义的集成库。

思路分析

  • 使用Glide v4的Generated API,需要自定义一个 AppGlideModule。

  • 集成第三方网络库OkHttp,需要自定义一个LibraryGlideModule 。

前期配置

1.添加依赖库

添加项目中使用到的插件和依赖库.例如 : Kotlin-Android扩展插件,Kotlin-kapt插件,Glidev4和OkHttp3的依赖。

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'//扩展插件
apply plugin: 'kotlin-kapt'//kapt3插件

//.....省略部分代码

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    //Glide v4
    compile 'com.github.bumptech.glide:glide:4.0.0-RC1'
    //在Kotlin中Glide的注解,kapt是 Kotlin内置的注解处理器
    kapt 'com.github.bumptech.glide:compiler:4.0.0-RC1'
    //OkHttp3
    compile 'com.squareup.okhttp3:okhttp:3.8.1'
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
}
repositories {
    mavenCentral()
}

更多详情,请阅读Kotlin-kapt插件使用和 Kotlin编程之Kotlin Android Extensions(扩展插件)。

2.添加混淆规则

添加Glide和OkHttp的混淆规则。

#Glide库的混淆
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
    **[] $VALUES;
    public *;
}

#OkHttp库的混淆
-dontwarn okio.**
-dontwarn javax.annotation.Nullable
-dontwarn javax.annotation.ParametersAreNonnullByDefault

3.添加权限

添加联网权限。

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

使用Kotlin编程来编写项目代码

若是在Java编程上使用Glide v4,请阅读 Android开发中使用Glide V4 中Generated API特性。

1. 自定义AppGlideModule

为运用程序定义一个带有@GlideModule注解的AppGlideModule,运用程序会使用和AppGlideMoudle同一个包下的GlideApp类。通过GlideApp.with()方式使用Glide的Generated API。

@GlideModule
internal class CustomAppGlideModule : AppGlideModule() {
    /**
     * 设置内存缓存大小10M
     */
    val  cacheSize=10*1024*1024
    override fun applyOptions(context: android.content.Context?, builder: com.bumptech.glide.GlideBuilder) {
        builder.setMemoryCache(LruResourceCache(cacheSize))
    }
    /**
     * 注册一个String类型的BaseGlideUrlLoader
     */
    override fun registerComponents(context: android.content.Context?, registry: com.bumptech.glide.Registry) {
        registry.append(String::class.java, java.io.InputStream::class.java,
                CustomBaseGlideUrlLoader.Companion.factory)
    }
    /**
     * 关闭解析AndroidManifest
     */
    override fun isManifestParsingEnabled(): Boolean {
        return false
    }
}

2. 自定义自定义BaseGlideUrlLoader

根据带有图片尺寸的URl,来获取合适比例的图片资源。通过指定String类型的Model, BaseGliUrlLOader中getgetURL()来覆盖原本的带有http或者htpps的URL. 这里处理方式来源于,Google IO App.

internal class CustomBaseGlideUrlLoader(concreteLoader: ModelLoader, modelCache: ModelCache): BaseGlideUrlLoader(concreteLoader,modelCache){
    /**
     * Url的匹配规则
     */
    val patern= Pattern.compile("__w-((?:-?\\d+)+)__")
    /**
     * 控制需要加载图片的尺寸大小
     */
    override fun getUrl(model: String, width: Int, height: Int, options: Options): String {
        var  m=patern.matcher(model)
        var bestBucket=0
        if (m.find()){
            var  found=m.group(1).split("-")
            for (item in found){
                bestBucket=item.toInt()
                if (bestBucket>=width) break
            }
        }
        return model
    }
    override fun handles(model: String?)=true 

    companion object{
        val urlCache= ModelCache(150)
        val factory=object: ModelLoaderFactory {
            override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader {
                return CustomBaseGlideUrlLoader(multiFactory.build(GlideUrl::class.java, InputStream::class.java), urlCache)
            }
            override fun teardown() {
            }
        }
    }

3. 自定义LibraryGlideModule

自定义一个OkHttp3集成的GlideModule,注册OkHttp相关类型。运用程序使用了集成其他网络库,则会包含一个AppGlideModule和Glide`注解,而本类将会自动包含。

@GlideModule
internal class CustomOkHttpGlideModule : LibraryGlideModule(){
    override fun registerComponents(context: Context?, registry: Registry) {

                 registry.replace(GlideUrl::class.java, 
                    InputStream::class.java,OkHttpUrlLoader.factory)
    }
}

4. 自定义ModelLoader

一个简单的加载器,用于使用OkHttp通过http/https获取的流。

 class OkHttpUrlLoader(var client:Call.Factory) :ModelLoader{ 

    override fun buildLoadData(model: GlideUrl, width: Int, height: Int, options: Options?): ModelLoader.LoadData? {
              return ModelLoader.LoadData(model,OkHttpStreamFetcher(client,model))
    }
    override fun handles(model: GlideUrl?)=true

     companion object{
         /**
          * 一个OkHttpUrlLoader的工厂
          */
         var factory=object :ModelLoaderFactory{
             @Volatile private var internalClient: Call.Factory? = null
             private var client: Call.Factory? = null
             init {
                 client=getInternalClient()
             }
             private fun getInternalClient(): Call.Factory? {
                 if (internalClient == null) {
                     synchronized(this) {
                         if (internalClient == null) {
                             internalClient = OkHttpProvider.createOkHttpClient()
                         }
                     }
                 }
                 return internalClient
             }
             override fun build(multiFactory: MultiModelLoaderFactory?) 
                                : ModelLoader {
                   return  OkHttpUrlLoader(client!!)
             }
             override fun teardown() {
             }
         }
     }
}

5. 自定义DataFetcher

重写DataFetcher,获取OkHttp库的数据流。

class OkHttpStreamFetcher(var client: Call.Factory, var url: GlideUrl) : DataFetcher<InputStream> {
    var tag = OkHttpStreamFetcher::class.java.simpleName
    @Synthetic var stream: InputStream? = null
    @Synthetic var responseBody: ResponseBody? = null
    @Volatile private var call: Call? = null

    override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
        var requestBuilder = Request.Builder().url(url.toStringUrl())
        //添加header
        for (headerEntry in url.headers.entries) {
            requestBuilder.addHeader( headerEntry.key, headerEntry.value)
        }
        var request = requestBuilder.build()
        call = client.newCall(request)
         call?.enqueue(object : Callback {
            override fun onFailure(call: Call?, e: IOException) {
                callback.onLoadFailed(e)
            }
            override fun onResponse(call: Call, response: Response) {
                responseBody= response.body()
                if (response.isSuccessful) {//请求成功
                    var contentLength = responseBody!!.contentLength()
                    stream = ContentLengthInputStream.obtain( 
                                   responseBody?.byteStream(), contentLength)
                    callback.onDataReady(stream)
                } else {//请求失败
                    callback.onLoadFailed(HttpException(response.message(), response.code()))
                }
            }
        })
    }


    override fun getDataSource(): DataSource {
       return  DataSource.REMOTE
    }

    /**
     * 清空
     */
    override fun cleanup() {
       try {
            stream?.close()
       }catch (e: IOException){
           e.printStackTrace()
       }
        responseBody?.close()
    }

    override fun getDataClass(): Class<InputStream> {
       return  InputStream::class.java
    }

    /**
     * 取消
     */
    override fun cancel() {
        var  localCall=call
        if (localCall!=null){
            localCall.cancel()
        }
    }
}

6. OkHttp的相关配置(重点)

这里只是简单的配置了OkHttp日志 , 还可以配置连接时间,拦截器,Https的配置。更多具体配置,请阅读OkHttp官网。

internal class OkHttpProvider{
    companion object{
        /**
         * 自定义配置OkHttpClient
         */
        fun createOkHttpClient(): OkHttpClient {
            var  builder= OkHttpClient.Builder()
            var  loggingInterceptor= HttpLoggingInterceptor()
            loggingInterceptor.level= HttpLoggingInterceptor.Level.BODY
            builder.addInterceptor(loggingInterceptor)

            return builder.build()
        }
    }
}

7.确保GlideApp类正常引用

当配置完以上步骤后,发觉不能使用GlideApp类。

解决方式:在AndroidStudio中Build–>Make Project–>将会出现build/generated/source中,便可以使用GlideApp。

8. 使用GlideApp类加载图片

这里使用Kotlin Android扩展插件,省略了findviewbyid()。

import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        GlideApp.with(this)
                .load("https://github.com/bumptech/glide/raw/master/static/glide_logo.png")
                .placeholder(R.mipmap.ic_launcher)
                .error(R.mipmap.ic_launcher)
                .centerCrop()
                .into(main_iv)
    }
}

AnroidStudio上运行项目

运行结果,打印出日志数据

1. Request的body和header

07-31 01:20:22.419 5601-5601/com.xingen.glideokhttp D/NetworkSecurityConfig: No Network Security Config specified, using platform default
07-31 01:20:22.470 5601-5631/com.xingen.glideokhttp D/OkHttp: --> GET https://github.com/bumptech/glide/raw/master/static/glide_logo.png http/1.1
07-31 01:20:22.470 5601-5631/com.xingen.glideokhttp D/OkHttp: User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.0; Android SDK built for x86 Build/NYC)
07-31 01:20:22.470 5601-5631/com.xingen.glideokhttp D/OkHttp: Accept-Encoding: identity
07-31 01:20:22.470 5601-5631/com.xingen.glideokhttp D/OkHttp: --> END GET

2. Response的数据

07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: <-- 200 OK https://raw.githubusercontent.com/bumptech/glide/master/static/glide_logo.png (1583ms)
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Strict-Transport-Security: max-age=31536000
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-Content-Type-Options: nosniff
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-Frame-Options: deny
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-XSS-Protection: 1; mode=block
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: ETag: "f9f4d799446f67d505d5e8aa4a4f3adc7e0d558b"
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Content-Type: image/png
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Cache-Control: max-age=300
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-Geo-Block-List: 
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-GitHub-Request-Id: 7ECA:275E9:454068:4AEB18:597E8546
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Content-Length: 13002
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Accept-Ranges: bytes
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Date: Mon, 31 Jul 2017 01:20:11 GMT
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Via: 1.1 varnish
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Connection: keep-alive
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-Served-By: cache-nrt6130-NRT
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-Cache: HIT
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-Cache-Hits: 1
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-Timer: S1501464011.294124,VS0,VE1
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Vary: Authorization,Accept-Encoding
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Access-Control-Allow-Origin: *
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: X-Fastly-Request-ID: 36b5d30a2d33ec2e614c0af64205f7b3ae4d3183
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Expires: Mon, 31 Jul 2017 01:25:11 GMT
07-31 01:20:24.053 5601-5631/com.xingen.glideokhttp D/OkHttp: Source-Age: 133
07-31 01:20:24.063 5601-5631/com.xingen.glideokhttp D/OkHttp: <-- END HTTP (binary 13002-byte body omitted)

运行效果如下

Kotlin编程之Glide V4(使用OkHttp3作为传输层)。_第1张图片

项目代码:https://github.com/13767004362/GlideOkhttpDemo

资源参考

  • Glide v4 confg: http://bumptech.github.io/glide/doc/configuration.html

  • Glide v4 Config: http://bumptech.github.io/glide/doc/configuration.html

  • Glide Migrating from v3 to v4: http://bumptech.github.io/glide/doc/migrating.html

你可能感兴趣的:(Android,热门的框架与第三方SDK,Glide,V4,图片加载库)