公司有款产品使用了阿里云的服务。
在之前的版本中,手机明文持有Key,拿到Key后可以进行上传操作。上传结束将返回的url(http开头)上传给服务器,服务器进行保存。
这本来是个正常的逻辑,但是在后面我们遇到了搞鬼的,他拿到了Key并且开始上传黄色图片。
这时我们需要进行方案二,用户不持有key,登陆之后才能从服务器拿到key,并且key是有有效期限的,需要定时获取。服务器负责与阿里云进行key的交互。上传之后我们拿到的不再是以http开头的url(http://xxxx.jpg),而是一个以oss开头的后面跟着一段字符串的url(oss://xxxssdddfffggg)。服务器的图片地址也已这个形式保存
很安全的样子对不对。而且关键是,阿里云已经帮我们做好了上传下载的功能。哇,好方便。但是,这跟原有的图片访问不兼容啊。Glide只支持url,resource等访问,oss开头是阿里云才支持的格式,如何解决呢。
首先我们想到的是自己实现了个图片三级缓存,并且在调用阶段,根据字符串开头判断是用普通的网络请求还是用阿里云的sdk。功能实现了,而且效果不错。但是几天写出来的框架,会有Glide那么大的工程性能好吗。持着这样的怀疑,我决定模仿Glide的框架去优化我的框架。
写着写着发现了ModelLoader这个类。发现不一般,打开文档查看发现它支持我们扩展访问类型。不错,我们来实现一下。
####9月27日更新####
以下均为kotlin实现
1.首先ModelLoader这个类就是Glide用来针对不同地址头使用不同加载方式产生的。Glide需要根据地址头来调用对应的加载方式。这个加载方式就是DataFetcher,这里先实现DataFetcher。
import xxx.aliyun.AliyunOssClient
import com.bumptech.glide.Priority
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.data.DataFetcher
import java.io.InputStream
class AliyunOSSDataFetcher(private val model: String) : DataFetcher<InputStream> {
val ossClient = AliyunOssClient()
override fun getDataClass(): Class<InputStream> {
return InputStream::class.java
}
override fun cleanup() {
}
override fun getDataSource(): DataSource {
return DataSource.REMOTE
}
override fun cancel() {
ossClient.cancel()
}
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
ossClient.download(model, object : AliyunOssClient.LoadCallback {
override fun onLoadComplete(inputStream: InputStream) {
callback.onDataReady(inputStream)
}
override fun onLoadFailed(exception: Exception) {
callback.onLoadFailed(exception)
}
})
}
}
这里的ossClient在Glide调用loadData时,需要下载图片,并把结果通过callback返回给Glide。ossClient的具体实现就不具体写了,可以根据阿里云的文档进行实现,主要是返回inputStream给callback.onDataReady()
2.然后是ModelLoader
import com.bumptech.glide.load.Options
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.ModelLoader
import com.bumptech.glide.load.model.ModelLoader.LoadData
import java.io.InputStream
class AliyunOSSModelLoader : ModelLoader<String, InputStream> {
override fun buildLoadData(model: String, width: Int, height: Int, options: Options): LoadData<InputStream>? {
return LoadData<InputStream>(GlideUrl(model), AliyunOSSDataFetcher(model))
}
override fun handles(model: String): Boolean {
return model.startsWith("oss:")
}
}
这里就很明显了,是给Glide判断用的,如果是oss:开头,那么久用AliyunOssDataFetcher加载
3.然后是实现一个ModelLoaderFactory
import com.bumptech.glide.load.model.ModelLoader
import com.bumptech.glide.load.model.ModelLoaderFactory
import com.bumptech.glide.load.model.MultiModelLoaderFactory
import java.io.InputStream
class AliyunOSSModelLoaderFactory :ModelLoaderFactory<String,InputStream> {
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<String, InputStream> {
return AliyunOSSModelLoader()
}
override fun teardown() {
}
}
4.最后一步,给Glide加载注释@GlideModule,让其生成配置
import android.content.Context
import xxx.utillib.image.glide.AliyunOSSModelLoaderFactory
import xxx.utillib.util.FileUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.GlideBuilder
import com.bumptech.glide.Registry
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.load.engine.cache.DiskLruCacheFactory
import com.bumptech.glide.load.engine.cache.LruResourceCache
import com.bumptech.glide.module.AppGlideModule
import java.io.InputStream
@GlideModule
class CustomGlideModule : AppGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
registry.prepend(String::class.java, InputStream::class.java, AliyunOSSModelLoaderFactory())
}
override fun applyOptions(context: Context, builder: GlideBuilder) {
super.applyOptions(context, builder)
var defaultMemoryCacheSize = 8 * 1024 * 1024L
val runtimeMemoryCacheSize = Runtime.getRuntime().maxMemory() / 8
val defaultDiskCacheSize = 250 * 1024 * 1024L
if (runtimeMemoryCacheSize > defaultDiskCacheSize) {
defaultMemoryCacheSize = runtimeMemoryCacheSize
}
builder.setMemoryCache(LruResourceCache(defaultMemoryCacheSize))
.setDiskCache(DiskLruCacheFactory(FileUtil.get().getCachePath(context, FileUtil.Companion.CacheCategory.PICTURE), defaultDiskCacheSize))
}
}
至此大家应该可以实现了