【Android Compose】Compose Coil 如何实现网络图片缓存

Coil的使用(加载网络图片)

首先开启网络权限

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

Coil图片缓存

缓存分类:

  • 磁盘缓存
  • 内存缓存

在百度随便找一张图片即可,通过 Coil 的 AsyncImage 加载网络图片,虽然以前的 Image 也可以通过 rememberImagePainter 加载网络图片但是已经被废弃掉了,这里就不考虑。

注意:AsyncImage 的 model 虽然是 Any 类型,但其实并不是可以接收所有的类型,只不过它支持很多种图片格式,所以才使用 Any

setContent {
	ImageCacheForCoilTheme {
        val imageUrl="https://xxxx.jpg"
        
        AsyncImage(
		      model = imageUrl,
		      contentDescription = null,
		      modifier = Modifier
				.fillMaxWidth()
				.aspectRatio(1280f / 847f)//图片比例
		)
    }
}

自定义ImageLoder

其实 coil 是通过 imageLoader 对 url 进行图片加载的,并且在幕后也进行了缓存工作。在 MainActivity 中可以直接调用 ImageLoader ,这是 Coil 默认的图片加载器。下面我们将自定义自己的图片加载器以满足我们开发中的各种需求。

创建 MyApplication 继承 Application 并实现 ImageLoaderFactory 的接口,在 newImageLoader 方法中重写我们自己的图片加载器覆盖掉默认的。从代码看出提供的类型还是很多的,可以根据需求自定义。

class MyApplication : Application(), ImageLoaderFactory {

	override fun newImageLoader(): ImageLoader {
		//在这里可以自定义我们的ImageLoader
		return ImageLoader(this).newBuilder()
		      .memoryCachePolicy(CachePolicy.ENABLED)//开启内存缓存策略
		      .memoryCache {//构建内存缓存
		            MemoryCache.Builder(this)
		                  .maxSizePercent(0.1)//最大只能使用剩余内存空间的10%
		                  .strongReferencesEnabled(true)//开启强引用
		                  .build()
		      }
		      .diskCachePolicy(CachePolicy.ENABLED)//开启磁盘缓存策略
		      .diskCache { //构建磁盘缓存
		            DiskCache.Builder()
		                  .maxSizePercent(0.03)//最大只能使用剩余内存空间的3%
		                  .directory(cacheDir)//存放在缓存目录
		                  .build()
		      }
		      //.callFactory {
		      //      //允许你拦截Coil发出的请求,假设你请求一张需要身份验证的图片
		      //      //因此你只需要附加token到http请求的标头即可,默认情况下Coil是不会拦截的
		      //      Call.Factory{
		      //            it.newBuilder()
		      //                  .addHeader("Authorization", "")
		      //                  .build()
		      //      }
		      //}
		      .logger(DebugLogger())//开启调试记录
		      .build()
	}

运行后查看Log,会自动记录我们的图片加载状态、从哪里加载。

Coil 默认是内存缓存和磁盘缓存的混合使用,如果涉及到较大的图片那么 Coil 会将它缓存到磁盘,如果只是比较小的图片,它会使用内存缓存。从下图中就看出 Coil 智能的帮助我们该缓存到哪个位置。

清除缓存

  • 清除所有
//清除掉所有内存、磁盘缓存
imageLoader.diskCache?.clear()
imageLoader.memoryCache?.clear()
setContent {
	ImageCacheForCoilTheme {
		val imageUrl ="https://xxx.jpg"
		Column(
			modifier = Modifier.fillMaxSize()
		) {
		    AsyncImage(
				model = imageUrl,
				contentDescription = null,
				modifier = Modifier
				      .fillMaxWidth()
				      .aspectRatio(1280f / 847f)
		      )
			Button(
			    onClick = {
                  //清除掉所有内存、磁盘缓存
			      imageLoader.diskCache?.clear()
			      imageLoader.memoryCache?.clear()
				}
			) {
			    Text(text = "清除缓存")
			}
		}
    }
}

  • 清除key
//清除谷歌照片缓存
imageLoader.diskCache?.remove(googleUrl)
imageLoader.memoryCache?.remove(MemoryCache.Key(googleUrl))
setContent {
	ImageCacheForCoilTheme {
		val baiduUrl="https://www.baidu.com/....png"
		val googleUrl="https://www.google.cn/....png"
        
        Column(
			modifier = Modifier.fillMaxSize()
		) {
			AsyncImage(
			      model = baiduUrl,
			      contentDescription = null,
			      modifier = Modifier
			            .fillMaxWidth()
			            .aspectRatio(1280f / 847f)
			)
			AsyncImage(
			      model = googleUrl,
			      contentDescription = null,
			      modifier = Modifier
			            .fillMaxWidth()
			            .aspectRatio(1280f / 847f)
			)
			Button(
                onClick = {
                    //清除谷歌照片缓存
					imageLoader.diskCache?.remove(googleUrl)
					imageLoader.memoryCache?.remove(MemoryCache.Key(googleUrl))
		      	}
            ){
				Text(text = "清除缓存")
			}
		}
    }
}

注意:

清除所有缓存使用:clear()
清除特定缓存使用:remove()
从调用 remove() 方法得出,移除特定缓存只需要传入一个 key 值(单一),而 ImageLoader 里面的机制就是通过这个 key 来判断该图片是否已经缓存了,如果没有则发起请求,反之从缓存中加载,移除缓存也是通过这个 key 来搜索。

磁盘缓存

  1. 添加缓存策略
    val request = ImageRequest.Builder(LocalContext.current)
        .data(url)
        .allowHardware(false)
        .memoryCachePolicy(CachePolicy.ENABLED)
        .diskCachePolicy(CachePolicy.ENABLED)//开启磁盘缓存策略
        .build()

2.添加文件头缓存策略

val imageLoader = ImageLoader.Builder(context).respectCacheHeaders(false).build()
  • 完整示例:
@Composable
fun MyAsyncImage(
    url: Any,
    modifier: Modifier = Modifier,
    contentScale: ContentScale = ContentScale.Fit,
    placeholder: Painter = CColors.TRANS.intColor2colorPainter(),
) {
    val context = LocalContext.current
    val imageLoader = ImageLoader.Builder(context).respectCacheHeaders(false).build()
    val request = ImageRequest.Builder(LocalContext.current)
        .data(url)
        .allowHardware(false)
        .memoryCachePolicy(CachePolicy.ENABLED)
        .diskCachePolicy(CachePolicy.ENABLED)//开启磁盘缓存策略
        .build()
    var reLoading by remember { mutableStateOf(false) }
    Box(
        contentAlignment = Alignment.Center,
        modifier = modifier
    ) {
        if (reLoading) {
            MyProgress(
                progress = -1f,
                iconSize = CDimens.SIZE_IMG_24.dp
            )
        }
        AsyncImage(
            imageLoader = imageLoader,
            model = request,
            contentDescription = "",
            contentScale = contentScale,
            onLoading = {
                reLoading = true
            },
            onSuccess = {
                reLoading = false
            },
            onError = {
                reLoading = false
            },
            placeholder = placeholder,
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight()
        )
    }
}

@Composable
fun MyProgress(
    progress: Float = 0.5f,
    iconSize: Dp = CDimens.SIZE_IMG_16.dp,
) {
    if (progress < 0f) {
        CircularProgressIndicator(
            strokeWidth = 2.dp,
            color = CColors.WHITE.longColor2color(),
            strokeCap = StrokeCap.Round,
            modifier = Modifier
                .size(iconSize)
        )
    } else {
        CircularProgressIndicator(
            strokeWidth = 2.dp,
            color = CColors.WHITE.longColor2color(),
            progress = progress,
            strokeCap = StrokeCap.Round,
            modifier = Modifier
                .size(iconSize)
        )
    }
}

部分内容来自:https://blog.csdn.net/weixin_49767743/article/details/134011871

猜你喜欢

转载自blog.csdn.net/weixin_42473228/article/details/143176253