项目作者: alialbaali

项目描述 :
Kotlin asynchronous media loading and caching library for Compose.
高级语言: Kotlin
项目地址: git://github.com/alialbaali/Kamel.git
创建时间: 2021-01-06T08:14:48Z
项目社区:https://github.com/alialbaali/Kamel

开源协议:Apache License 2.0

下载


Kamel

Version
Snapshot
License
Kotlin
Compose Multiplatform

Kamel is an asynchronous media loading library for Compose Multiplatform. It provides a simple, customizable and
efficient way to load, cache, decode and display images in your application. By default, it uses
Ktor client for loading resources.

Table of contents

Setup

Kamel is published on Maven Central:

  1. repositories {
  2. mavenCentral()
  3. // ...
  4. }

Default Setup

Add the dependency to the common source-set or to any platform source-set:

  1. kotlin {
  2. sourceSets {
  3. commonMain {
  4. dependencies {
  5. implementation("media.kamel:kamel-image-default:1.0.3")
  6. // no need to specify ktor engines, one is included for each target
  7. // ...
  8. }
  9. }
  10. }
  11. }

Granular Setup

For a more granular setup, you can choose which modules to include in your project:

  1. kotlin {
  2. sourceSets {
  3. commonMain {
  4. dependencies {
  5. // core module (required)
  6. implementation("media.kamel:kamel-image:1.0.3")
  7. // Note: When using `kamel-image` a ktor engine is not included.
  8. // To fetch remote images you also must ensure you add your own
  9. // ktor engine for each target.
  10. // optional modules (choose what you need and add them to your kamel config)
  11. implementation("media.kamel:kamel-decoder-image-bitmap:1.0.3")
  12. implementation("media.kamel:kamel-decoder-image-bitmap-resizing:1.0.3") // android only right now
  13. implementation("media.kamel:kamel-decoder-image-vector:1.0.3")
  14. implementation("media.kamel:kamel-decoder-svg-batik:1.0.3")
  15. implementation("media.kamel:kamel-decoder-svg-std:1.0.3")
  16. implementation("media.kamel:kamel-decoder-animated-image:1.0.3")
  17. implementation("media.kamel:kamel-fetcher-resources-jvm:1.0.3")
  18. implementation("media.kamel:kamel-fetcher-resources-android:1.0.3")
  19. // ...
  20. }
  21. }
  22. jvmMain {
  23. dependencies {
  24. // optional modules (choose what you need and add them to your kamel config)
  25. implementation("media.kamel:kamel-fetcher-resources-jvm:1.0.3")
  26. // ...
  27. }
  28. }
  29. androidMain {
  30. dependencies {
  31. // optional modules (choose what you need and add them to your kamel config)
  32. implementation("media.kamel:kamel-fetcher-resources-android:1.0.3")
  33. // ...
  34. }
  35. }
  36. }
  37. }
Granular Setup: Ktor HttpClient Engine

When using kamel-image ktor engines are not included per target.
In order to fetch remote images you also must ensure you add your own ktor engine for each target.
You can find the available engines here.

Usage

Loading an image resource

To load an image asynchronously, you can use asyncPainterResource composable, it can load
images from different data sources:

  1. // String
  2. asyncPainterResource(data = "https://www.example.com/image.jpg")
  3. asyncPainterResource(data = "file:///path/to/image.png")
  4. // Ktor Url
  5. asyncPainterResource(data = Url("https://www.example.com/image.jpg"))
  6. // URI
  7. asyncPainterResource(data = URI("https://www.example.com/image.png"))
  8. // File (JVM, Native)
  9. asyncPainterResource(data = File("/path/to/image.png"))
  10. // File (JS)
  11. asyncPainterResource(data = File(org.w3c.files.File(arrayOf(blob), "/path/to/image.png")))
  12. // URL
  13. asyncPainterResource(data = URL("https://www.example.com/image.jpg"))
  14. // and more...

asyncPainterResource can be used to load SVG, XML, JPEG, and PNG by default depending on the
platform implementation.

asyncPainterResource returns a Resource<Painter> object which can be used to display the image
using KamelImage composable.

Platform specific implementations

Since there isn’t any shared resource system between Android and Desktop, some implementations (e.g.
fetchers and mappers) are only available for a specific platform:

Desktop only implementations

To load an image file from desktop application resources, you have to add resourcesFetcher to
the KamelConfig:

  1. val desktopConfig = KamelConfig {
  2. takeFrom(KamelConfig.Default)
  3. // Available only on Desktop.
  4. resourcesFetcher()
  5. // Available only on Desktop.
  6. // An alternative svg decoder
  7. batikSvgDecoder()
  8. }

Assuming there’s an image.png file in the /resources directory in the project:

  1. CompositionLocalProvider(LocalKamelConfig provides desktopConfig) {
  2. asyncPainterResource("image.png")
  3. }

Android only implementations

To load an image file from android application resources, you have to add resourcesFetcher
and resourcesIdMapper to the KamelConfig:

  1. val context: Context = LocalContext.current
  2. val androidConfig = KamelConfig {
  3. takeFrom(KamelConfig.Default)
  4. // Available only on Android.
  5. resourcesFetcher(context)
  6. // Available only on Android.
  7. resourcesIdMapper(context)
  8. }

Assuming there’s an image.png file in the /res/raw directory in the project:

  1. CompositionLocalProvider(LocalKamelConfig provides androidConfig) {
  2. asyncPainterResource(R.raw.image)
  3. }

Configuring an image resource

asyncPainterResource supports configuration using a trailing lambda:

  1. val painterResource: Resource<Painter> = asyncPainterResource("https://www.example.com/image.jpg") {
  2. // CoroutineContext to be used while loading the image.
  3. coroutineContext = Job() + Dispatcher.IO
  4. // Customizes HTTP request
  5. requestBuilder { // this: HttpRequestBuilder
  6. header("Key", "Value")
  7. parameter("Key", "Value")
  8. cacheControl(CacheControl.MAX_AGE)
  9. }
  10. }

Displaying an image resource

KamelImage is a composable function that takes a Resource<Painter> object, display it
and provide extra functionality:

  1. KamelImage(
  2. resource = painterResource,
  3. contentDescription = "Profile",
  4. )

KamelImage can also be used to get the exception using onFailure,
and progress using onLoading parameters, to display a snackbar or a progress indicator,
depending on the case:

  1. val coroutineScope = rememberCoroutineScope()
  2. val snackbarHostState = remember { SnackbarHostState() }
  3. Box {
  4. KamelImage(
  5. resource = painterResource,
  6. contentDescription = "Profile",
  7. onLoading = { progress -> CircularProgressIndicator(progress) },
  8. onFailure = { exception ->
  9. coroutineScope.launch {
  10. snackbarHostState.showSnackbar(
  11. message = exception.message.toString(),
  12. actionLabel = "Hide",
  13. duration = SnackbarDuration.Short
  14. )
  15. }
  16. }
  17. )
  18. SnackbarHost(hostState = snackbarHostState, modifier = Modifier.padding(16.dp))
  19. }

You can also provide your own custom implementation using a simple when expression:

  1. when (val resource = asyncPainterResource("https://www.example.com/image.jpg")) {
  2. is Resource.Loading -> {
  3. Text("Loading...")
  4. }
  5. is Resource.Success -> {
  6. val painter: Painter = resource.value
  7. Image(painter, contentDescription = "Profile")
  8. }
  9. is Resource.Failure -> {
  10. log(resource.exception)
  11. val fallbackPainter = painterResource("/path/to/fallbackImage.jpg")
  12. Image(fallbackPainter, contentDescription = "Profile")
  13. }
  14. }

Crossfade animation

You can enable, disable or customize crossfade (fade-in) animation through the animationSpec
parameter. Setting animationSpec to null will disable the animation:

  1. KamelImage(
  2. resource = imageResource,
  3. contentDescription = "Profile",
  4. // null by default
  5. animationSpec = tween(),
  6. )

Configuring Kamel

The default implementation is KamelConfig.Default. If you wish to configure it, you can do it
the following way:

  1. val customKamelConfig = KamelConfig {
  2. // Copies the default implementation if needed
  3. takeFrom(KamelConfig.Default)
  4. // Sets the number of images to cache
  5. imageBitmapCacheSize = DefaultCacheSize
  6. // adds an ImageBitmapDecoder
  7. imageBitmapDecoder()
  8. // adds an ImageVectorDecoder (XML)
  9. imageVectorDecoder()
  10. // adds an SvgDecoder (SVG)
  11. svgDecoder()
  12. // adds a FileFetcher
  13. fileFetcher()
  14. // Configures Ktor HttpClient
  15. httpUrlFetcher {
  16. // httpCache is defined in kamel-core and configures the ktor client
  17. // to install a HttpCache feature with the implementation provided by Kamel.
  18. // The size of the cache can be defined in Bytes.
  19. httpCache(10 * 1024 * 1024 /* 10 MiB */)
  20. defaultRequest {
  21. url("https://www.example.com/")
  22. cacheControl(CacheControl.MaxAge(maxAgeSeconds = 10000))
  23. }
  24. install(HttpRequestRetry) {
  25. maxRetries = 3
  26. retryIf { httpRequest, httpResponse ->
  27. !httpResponse.status.isSuccess()
  28. }
  29. }
  30. // Requires adding "io.ktor:ktor-client-logging:$ktor_version"
  31. Logging {
  32. level = LogLevel.INFO
  33. logger = Logger.SIMPLE
  34. }
  35. }
  36. // more functionality available.
  37. }

Memory cache size (number of entries to cache)

Kamel provides a generic Cache<K,V> interface, the default implementation uses LRU memory cache
mechanism backed by LinkedHashMap. You can provide a number of entries to cache for each type like
so:

  1. KamelConfig {
  2. // 100 by default
  3. imageBitmapCacheSize = 500
  4. // 100 by default
  5. imageVectorCacheSize = 300
  6. // 100 by default
  7. svgCacheSize = 200
  8. }

Disk cache size (in bytes)

Kamel can create a persistent disk cache for images by implementing ktor’s CacheStorage feature.
The default config KamelConfig.Default installs this feature with a 10 MiB disk cache size.
The underlying disk cache is based on coil’s multiplatform DiskLruCache implementation.

  1. KamelConfig {
  2. httpFetcher {
  3. // The size of the cache can be defined in bytes. Or DefaultHttpCacheSize (10 MiB) can be used.
  4. httpCache(DefaultHttpCacheSize)
  5. }
  6. }

Applying Kamel configuration

You can use LocalKamelConfig to apply your custom configuration:

  1. CompositionLocalProvider(LocalKamelConfig provides customKamelConfig) {
  2. asyncPainterResource("image.jpg")
  3. }

Contributions

Contributions are always welcome!. If you’d like to contribute, please feel free to create a PR or
open an issue.

License

  1. Copyright 2021 Ali Albaali
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. https://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.