Android Coil ImageLoader MemoryCache设置Key与复用内存缓存,Kotlin
import android.content.ContentUris
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
companion object {
const val TAG = "fly"
const val SPAN_COUNT = 8
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.rv_layout)
val rv = findViewById<RecyclerView>(R.id.rv)
val layoutManager = GridLayoutManager(this, SPAN_COUNT)
layoutManager.orientation = LinearLayoutManager.VERTICAL
val adapter = ImageAdapter(this, 0)
rv.adapter = adapter
rv.layoutManager = layoutManager
//rv.setHasFixedSize(true)
//rv.setItemViewCacheSize(SPAN_COUNT * 20)
//rv.recycledViewPool.setMaxRecycledViews(0, SPAN_COUNT * 20)
val ctx = this
lifecycleScope.launch(Dispatchers.IO) {
val lists = readAllImage(ctx)
Log.d(TAG, "readAllImage size=${lists.size}")
lifecycleScope.launch(Dispatchers.Main) {
adapter.dataChanged(lists)
}
}
}
class MyData(var path: String, var uri: Uri)
private fun readAllImage(ctx: Context): ArrayList<MyData> {
val photos = ArrayList<MyData>()
//读取所有图
val cursor = ctx.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null
)
while (cursor!!.moveToNext()) {
//路径
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
val id = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)
val imageUri: Uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getLong(id))
//名称
//val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
//大小
//val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))
photos.add(MyData(path, imageUri))
}
cursor.close()
return photos
}
}
import android.content.Context
import android.graphics.Bitmap
import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.AppCompatImageView
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import coil.ImageLoader
import coil.memory.MemoryCache
import coil.request.CachePolicy
import coil.request.ImageRequest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ImageAdapter : RecyclerView.Adapter<ImageHolder> {
private var mCtx: Context? = null
private var mImageLoader: ImageLoader? = null
private var mViewSize = 0
constructor(ctx: Context, type: Int) : super() {
mCtx = ctx
mImageLoader = ImageLoader.Builder(mCtx!!)
.memoryCachePolicy(CachePolicy.ENABLED)
.availableMemoryPercentage(0.9999)
.bitmapPoolingEnabled(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.diskCachePolicy(CachePolicy.ENABLED)
.build()
Log.d(MainActivity.TAG, "memoryCache.maxSize=${mImageLoader?.memoryCache?.maxSize}")
mViewSize = mCtx!!.resources.displayMetrics.widthPixels / MainActivity.SPAN_COUNT
}
private var mItems = ArrayList<MainActivity.MyData>()
fun dataChanged(items: ArrayList<MainActivity.MyData>) {
this.mItems = items
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageHolder {
val view = MyIV(mCtx!!, mViewSize)
return ImageHolder(view)
}
override fun getItemCount(): Int {
return mItems.size
}
override fun onBindViewHolder(holder: ImageHolder, position: Int) {
bind(mItems[position], holder.image)
}
private fun bind(data: MainActivity.MyData, image: MyIV) {
val key = MemoryCache.Key.invoke(data.path)
val bmp = mImageLoader?.memoryCache?.get(key)
if (bmp != null && bmp.byteCount > 0) {
Log.d(
MainActivity.TAG,
"memory cache bmp=${bmp.byteCount} ${mImageLoader?.memoryCache?.size}/${mImageLoader?.memoryCache?.maxSize}"
)
image.setImageBitmap(bmp)
} else {
val t = System.currentTimeMillis()
val request = ImageRequest.Builder(mCtx!!)
.data(data.uri)
.size(mViewSize)
.target(image)
.memoryCacheKey(key)
.build()
(mCtx as? MainActivity)?.lifecycleScope?.launch(Dispatchers.IO) {
mImageLoader?.execute(request)
}
Log.d(MainActivity.TAG, "no memory cache time=${System.currentTimeMillis() - t}")
}
}
}
class ImageHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var image = itemView as MyIV
}
class MyIV : AppCompatImageView {
private var mSize = 0
constructor(ctx: Context, size: Int) : super(ctx) {
mSize = size
scaleType = ScaleType.CENTER_CROP
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(mSize, mSize)
}
}
1、已知问题,发现Coil在宫格多时候,如果直接:
mImageLoader?.enqueue(request)
也有一定小的耗时。所以干脆用协程包装起来。
2、虽然通过设置内存系数
availableMemoryPercentage
扩大了内存,但跑起来发现设置后内存还是比较小(约300mb),这是不够的,需要通过其他配置方式扩大内存空间。
3、app跑起来后,没有在当前app的硬盘缓存空间发现图片解码后的磁盘文件缓存痕迹。这需要再配置。