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.添權重限:
添加聯網權限。
使用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=**
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<GlideUrl, InputStream>, modelCache: ModelCache<String, GlideUrl>): BaseGlideUrlLoader<String>(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=
if (m.find()){
var found=m.group().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<String, GlideUrl>()
val factory=object: ModelLoaderFactory<String, InputStream> {
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<String, InputStream> {
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<GlideUrl,InputStream>{
override fun buildLoadData(model: GlideUrl, width: Int, height: Int, options: Options?): ModelLoader.LoadData<InputStream>? {
return ModelLoader.LoadData(model,OkHttpStreamFetcher(client,model))
}
override fun handles(model: GlideUrl?)=true
companion object{
/**
* 一個OkHttpUrlLoader的工廠
*/
var factory=object :ModelLoaderFactory<GlideUrl,InputStream>{
@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<GlideUrl, InputStream> {
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:
- :: -/com.xingen.glideokhttp D/NetworkSecurityConfig: No Network Security Config specified, using platform default
- :: -/com.xingen.glideokhttp D/OkHttp: --> GET https://github.com/bumptech/glide/raw/master/static/glide_logo.png http/
- :: -/com.xingen.glideokhttp D/OkHttp: User-Agent: Dalvik/ (Linux; U; Android 7.0; Android SDK built for x86 Build/NYC)
- :: -/com.xingen.glideokhttp D/OkHttp: Accept-Encoding: identity
- :: -/com.xingen.glideokhttp D/OkHttp: --> END GET
2. Response的資料:
- :: -/com.xingen.glideokhttp D/OkHttp: <-- OK https://raw.githubusercontent.com/bumptech/glide/master/static/glide_logo.png (1583ms)
- :: -/com.xingen.glideokhttp D/OkHttp: Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'
- :: -/com.xingen.glideokhttp D/OkHttp: Strict-Transport-Security: max-age=
- :: -/com.xingen.glideokhttp D/OkHttp: X-Content-Type-Options: nosniff
- :: -/com.xingen.glideokhttp D/OkHttp: X-Frame-Options: deny
- :: -/com.xingen.glideokhttp D/OkHttp: X-XSS-Protection: ; mode=block
- :: -/com.xingen.glideokhttp D/OkHttp: ETag: "f9f4d799446f67d505d5e8aa4a4f3adc7e0d558b"
- :: -/com.xingen.glideokhttp D/OkHttp: Content-Type: image/png
- :: -/com.xingen.glideokhttp D/OkHttp: Cache-Control: max-age=
- :: -/com.xingen.glideokhttp D/OkHttp: X-Geo-Block-List:
- :: -/com.xingen.glideokhttp D/OkHttp: X-GitHub-Request-Id: ECA:::AEB18:
- :: -/com.xingen.glideokhttp D/OkHttp: Content-Length:
- :: -/com.xingen.glideokhttp D/OkHttp: Accept-Ranges: bytes
- :: -/com.xingen.glideokhttp D/OkHttp: Date: Mon, Jul :: GMT
- :: -/com.xingen.glideokhttp D/OkHttp: Via: varnish
- :: -/com.xingen.glideokhttp D/OkHttp: Connection: keep-alive
- :: -/com.xingen.glideokhttp D/OkHttp: X-Served-By: cache-nrt6130-NRT
- :: -/com.xingen.glideokhttp D/OkHttp: X-Cache: HIT
- :: -/com.xingen.glideokhttp D/OkHttp: X-Cache-Hits:
- :: -/com.xingen.glideokhttp D/OkHttp: X-Timer: S1501464011,VS0,VE1
- :: -/com.xingen.glideokhttp D/OkHttp: Vary: Authorization,Accept-Encoding
- :: -/com.xingen.glideokhttp D/OkHttp: Access-Control-Allow-Origin: *
- :: -/com.xingen.glideokhttp D/OkHttp: X-Fastly-Request-ID: b5d30a2d33ec2e614c0af64205f7b3ae4d3183
- :: -/com.xingen.glideokhttp D/OkHttp: Expires: Mon, Jul :: GMT
- :: -/com.xingen.glideokhttp D/OkHttp: Source-Age:
- :: -/com.xingen.glideokhttp D/OkHttp: <-- END HTTP (binary -byte body omitted)
運作效果如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1DNXplb1cVY0Y1RhZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TN1MDMyczMwETMzcDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
項目代碼: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