From 5f0612102d0f1c54cc57039a626efe0126d870c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Mon, 29 Jan 2024 23:00:32 +0100 Subject: [PATCH 01/10] Improve lazy loading for Coil + OkHttp This way, we can load Coil's backend on a background thread and not block the MainThread with it. Previously, the Coil image loader was initialized with the first composed image, which caused ~10ms duration and most likely skipped frames. Change-Id: Iaa583b6adc1df7d7a51dbae1473e539f2c0b0b62 --- .../util/ImageLoaderAsyncFactory.kt | 59 +++++++++++++++++++ .../apps/nowinandroid/NiaApplication.kt | 13 ++-- .../core/network/di/NetworkModule.kt | 52 ++++++++-------- 3 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt new file mode 100644 index 000000000..540082e50 --- /dev/null +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.util + +import androidx.tracing.trace +import coil.ImageLoader +import coil.ImageLoaderFactory +import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import javax.inject.Inject + +/** + * This class asynchronously loads the Coil's image loader on a [ApplicationScope], which uses Default dispatcher. + * Reason for this is to prevent initializing Coil (and thus OkHttp internally) with the first image loading + * to prevent skipping frames and performance issues. + * + * Usage: + * - Init creates an async initialization of the image loader. + * - delegate to [newImageLoader] so that Coil can automatically reach for its loader. + */ +class ImageLoaderAsyncFactory @Inject constructor( + @ApplicationScope + appScope: CoroutineScope, + private val imageLoader: dagger.Lazy, +) : ImageLoaderFactory { + private lateinit var asyncNewImageLoader: Deferred + + init { + appScope.launch { + // Initializing asynchronously, but start immediately + asyncNewImageLoader = async { imageLoader.get() } + } + } + + /** + * This runBlocking here is on purpose to prevent any unfinished Coil initialization. + * Most likely this will be already initialized by the time we want to show an image on the screen. + */ + override fun newImageLoader() = + trace("NiaImageLoader.runBlocking") { runBlocking { asyncNewImageLoader.await() } } +} diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt index 9f0bb2ef7..40fffc4d9 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt @@ -17,21 +17,20 @@ package com.google.samples.apps.nowinandroid import android.app.Application -import coil.ImageLoader -import coil.ImageLoaderFactory +import coil.Coil import com.google.samples.apps.nowinandroid.sync.initializers.Sync +import com.google.samples.apps.nowinandroid.util.ImageLoaderAsyncFactory import com.google.samples.apps.nowinandroid.util.ProfileVerifierLogger import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject -import javax.inject.Provider /** * [Application] class for NiA */ @HiltAndroidApp -class NiaApplication : Application(), ImageLoaderFactory { +class NiaApplication : Application() { @Inject - lateinit var imageLoader: Provider + lateinit var imageLoaderAsyncFactory: ImageLoaderAsyncFactory @Inject lateinit var profileVerifierLogger: ProfileVerifierLogger @@ -41,7 +40,7 @@ class NiaApplication : Application(), ImageLoaderFactory { // Initialize Sync; the system responsible for keeping data in the app up to date. Sync.initialize(context = this) profileVerifierLogger() + // We set immediately Coil's image loader factory to prevent initialization with the first image. + Coil.setImageLoader(imageLoaderAsyncFactory) } - - override fun newImageLoader(): ImageLoader = imageLoader.get() } diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt index 21d93c0e4..a68683c7c 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt @@ -17,6 +17,7 @@ package com.google.samples.apps.nowinandroid.core.network.di import android.content.Context +import androidx.tracing.trace import coil.ImageLoader import coil.decode.SvgDecoder import coil.util.DebugLogger @@ -51,16 +52,18 @@ internal object NetworkModule { @Provides @Singleton - fun okHttpCallFactory(): Call.Factory = OkHttpClient.Builder() - .addInterceptor( - HttpLoggingInterceptor() - .apply { - if (BuildConfig.DEBUG) { - setLevel(HttpLoggingInterceptor.Level.BODY) - } - }, - ) - .build() + fun okHttpCallFactory(): Call.Factory = trace("NiaOkHttpClient") { + OkHttpClient.Builder() + .addInterceptor( + HttpLoggingInterceptor() + .apply { + if (BuildConfig.DEBUG) { + setLevel(HttpLoggingInterceptor.Level.BODY) + } + }, + ) + .build() + } /** * Since we're displaying SVGs in the app, Coil needs an ImageLoader which supports this @@ -72,20 +75,21 @@ internal object NetworkModule { @Provides @Singleton fun imageLoader( - okHttpCallFactory: Call.Factory, + // We specifically request dagger.Lazy here, so that it's not instantiated from Dagger. + okHttpCallFactory: dagger.Lazy, @ApplicationContext application: Context, - ): ImageLoader = ImageLoader.Builder(application) - .callFactory(okHttpCallFactory) - .components { - add(SvgDecoder.Factory()) - } - // Assume most content images are versioned urls - // but some problematic images are fetching each time - .respectCacheHeaders(false) - .apply { - if (BuildConfig.DEBUG) { - logger(DebugLogger()) + ): ImageLoader = trace("NiaImageLoader") { + ImageLoader.Builder(application) + .callFactory { okHttpCallFactory.get() } + .components { add(SvgDecoder.Factory()) } + // Assume most content images are versioned urls + // but some problematic images are fetching each time + .respectCacheHeaders(false) + .apply { + if (BuildConfig.DEBUG) { + logger(DebugLogger()) + } } - } - .build() + .build() + } } From 8e3903e90c43f506e522cb0a0d3bbfc294e5204d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Wed, 31 Jan 2024 12:57:25 +0100 Subject: [PATCH 02/10] Fix comment Co-authored-by: Yuri Schimke --- .../samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index 540082e50..5e613117e 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -45,7 +45,7 @@ class ImageLoaderAsyncFactory @Inject constructor( init { appScope.launch { - // Initializing asynchronously, but start immediately + // Initialize immediately, but need a Deferred for callers asyncNewImageLoader = async { imageLoader.get() } } } From 7c33946ce13c4bb37df453023de7478818ca7748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 12:53:37 +0100 Subject: [PATCH 03/10] Launch loader directly without additional coroutine scope Change-Id: Icbbdbcbcac1a6275857ebe998509f1e09109db7a --- .../samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index 5e613117e..aa79b8261 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -44,9 +44,9 @@ class ImageLoaderAsyncFactory @Inject constructor( private lateinit var asyncNewImageLoader: Deferred init { - appScope.launch { + asyncNewImageLoader = appScope.async { // Initialize immediately, but need a Deferred for callers - asyncNewImageLoader = async { imageLoader.get() } + imageLoader.get() } } From e853748cf76b3e77495a7ef642e038e19c0dc68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 13:50:01 +0100 Subject: [PATCH 04/10] Don't use runBlocking if async init completed Change-Id: I641187ce277f434c6fca49a11b3cfccd50ecf5da --- .../apps/nowinandroid/util/ImageLoaderAsyncFactory.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index aa79b8261..0670f6dc8 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -55,5 +55,8 @@ class ImageLoaderAsyncFactory @Inject constructor( * Most likely this will be already initialized by the time we want to show an image on the screen. */ override fun newImageLoader() = - trace("NiaImageLoader.runBlocking") { runBlocking { asyncNewImageLoader.await() } } + trace("NiaImageLoader.runBlocking") { + if (asyncNewImageLoader.isCompleted) asyncNewImageLoader.getCompleted() + else runBlocking { asyncNewImageLoader.await() } + } } From d1211f15e783fff251da76f86fa3e9efdeb68c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 13:50:33 +0100 Subject: [PATCH 05/10] Inline initialization Change-Id: I0a77eb6457cac27c1a4d604c8efdcbbdce95bc48 --- .../nowinandroid/util/ImageLoaderAsyncFactory.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index 0670f6dc8..b746ec974 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -19,11 +19,11 @@ package com.google.samples.apps.nowinandroid.util import androidx.tracing.trace import coil.ImageLoader import coil.ImageLoaderFactory +import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.async -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import javax.inject.Inject @@ -41,14 +41,12 @@ class ImageLoaderAsyncFactory @Inject constructor( appScope: CoroutineScope, private val imageLoader: dagger.Lazy, ) : ImageLoaderFactory { - private lateinit var asyncNewImageLoader: Deferred - init { - asyncNewImageLoader = appScope.async { - // Initialize immediately, but need a Deferred for callers - imageLoader.get() - } - } + /** + * Initialize immediately, but need a Deferred for callers + * [ApplicationScope] already uses [Default] dispatcher, so we don't have to switch it here. + */ + private val asyncNewImageLoader: Deferred = appScope.async { imageLoader.get() } /** * This runBlocking here is on purpose to prevent any unfinished Coil initialization. From f3e89bad4ade836afefc208d1c75b493ab3486e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 16:06:01 +0100 Subject: [PATCH 06/10] Add braces Change-Id: I859babab7278137a4a2e49e5a085c65632888dd0 --- .../apps/nowinandroid/util/ImageLoaderAsyncFactory.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt index b746ec974..d8f928de5 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt @@ -54,7 +54,10 @@ class ImageLoaderAsyncFactory @Inject constructor( */ override fun newImageLoader() = trace("NiaImageLoader.runBlocking") { - if (asyncNewImageLoader.isCompleted) asyncNewImageLoader.getCompleted() - else runBlocking { asyncNewImageLoader.await() } + if (asyncNewImageLoader.isCompleted) { + asyncNewImageLoader.getCompleted() + } else { + runBlocking { asyncNewImageLoader.await() } + } } } From d110d92b87359f52acb5b4adb7ce100843d3705c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 16:06:23 +0100 Subject: [PATCH 07/10] Optimize OkHttp initialization for Retrofit Change-Id: I347f1080ab5adf774a0cdd3c659cbf25c4820f9a --- .../network/retrofit/RetrofitNiaNetwork.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt index 321e856fe..42a0e9d64 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.core.network.retrofit +import androidx.tracing.trace import com.google.samples.apps.nowinandroid.core.network.BuildConfig import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList @@ -73,17 +74,19 @@ private data class NetworkResponse( @Singleton internal class RetrofitNiaNetwork @Inject constructor( networkJson: Json, - okhttpCallFactory: Call.Factory, + okhttpCallFactory: dagger.Lazy, ) : NiaNetworkDataSource { - private val networkApi = Retrofit.Builder() - .baseUrl(NIA_BASE_URL) - .callFactory(okhttpCallFactory) - .addConverterFactory( - networkJson.asConverterFactory("application/json".toMediaType()), - ) - .build() - .create(RetrofitNiaNetworkApi::class.java) + private val networkApi = trace("RetrofitNiaNetwork") { + Retrofit.Builder() + .baseUrl(NIA_BASE_URL) + .callFactory { okhttpCallFactory.get().newCall(it) } + .addConverterFactory( + networkJson.asConverterFactory("application/json".toMediaType()), + ) + .build() + .create(RetrofitNiaNetworkApi::class.java) + } override suspend fun getTopics(ids: List?): List = networkApi.getTopics(ids = ids).data From 4cdc2ad34570e693aa69aac36cda275e9d32c5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 1 Feb 2024 16:12:32 +0100 Subject: [PATCH 08/10] Add comment for Retrofit async OkHttp Change-Id: Ic7a6887b76caf26f00b58b0753271d426b67e75b --- .../nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt index 42a0e9d64..e9fe99d9e 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt @@ -80,6 +80,8 @@ internal class RetrofitNiaNetwork @Inject constructor( private val networkApi = trace("RetrofitNiaNetwork") { Retrofit.Builder() .baseUrl(NIA_BASE_URL) + // We use callFactory lambda here with dagger.Lazy + // to prevent initializing OkHttp on the main thread. .callFactory { okhttpCallFactory.get().newCall(it) } .addConverterFactory( networkJson.asConverterFactory("application/json".toMediaType()), From 58483b6166f2ffdc1bb1629229bf05e993c45f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Thu, 8 Feb 2024 15:05:12 +0100 Subject: [PATCH 09/10] Revert fast initialization of Coil As discussed in https://github.com/coil-kt/coil/issues/2097 the problem is caused by regitering system services, which will be fixed in 2.6.0 Change-Id: I9085309780508137f10b25ff82deed3c62e5d159 --- .../util/ImageLoaderAsyncFactory.kt | 63 ------------------- .../apps/nowinandroid/NiaApplication.kt | 12 ++-- 2 files changed, 6 insertions(+), 69 deletions(-) delete mode 100644 app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt deleted file mode 100644 index d8f928de5..000000000 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/util/ImageLoaderAsyncFactory.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.util - -import androidx.tracing.trace -import coil.ImageLoader -import coil.ImageLoaderFactory -import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default -import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.async -import kotlinx.coroutines.runBlocking -import javax.inject.Inject - -/** - * This class asynchronously loads the Coil's image loader on a [ApplicationScope], which uses Default dispatcher. - * Reason for this is to prevent initializing Coil (and thus OkHttp internally) with the first image loading - * to prevent skipping frames and performance issues. - * - * Usage: - * - Init creates an async initialization of the image loader. - * - delegate to [newImageLoader] so that Coil can automatically reach for its loader. - */ -class ImageLoaderAsyncFactory @Inject constructor( - @ApplicationScope - appScope: CoroutineScope, - private val imageLoader: dagger.Lazy, -) : ImageLoaderFactory { - - /** - * Initialize immediately, but need a Deferred for callers - * [ApplicationScope] already uses [Default] dispatcher, so we don't have to switch it here. - */ - private val asyncNewImageLoader: Deferred = appScope.async { imageLoader.get() } - - /** - * This runBlocking here is on purpose to prevent any unfinished Coil initialization. - * Most likely this will be already initialized by the time we want to show an image on the screen. - */ - override fun newImageLoader() = - trace("NiaImageLoader.runBlocking") { - if (asyncNewImageLoader.isCompleted) { - asyncNewImageLoader.getCompleted() - } else { - runBlocking { asyncNewImageLoader.await() } - } - } -} diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt index 40fffc4d9..8e3ad814a 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt @@ -17,9 +17,9 @@ package com.google.samples.apps.nowinandroid import android.app.Application -import coil.Coil +import coil.ImageLoader +import coil.ImageLoaderFactory import com.google.samples.apps.nowinandroid.sync.initializers.Sync -import com.google.samples.apps.nowinandroid.util.ImageLoaderAsyncFactory import com.google.samples.apps.nowinandroid.util.ProfileVerifierLogger import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject @@ -28,9 +28,9 @@ import javax.inject.Inject * [Application] class for NiA */ @HiltAndroidApp -class NiaApplication : Application() { +class NiaApplication : Application(), ImageLoaderFactory { @Inject - lateinit var imageLoaderAsyncFactory: ImageLoaderAsyncFactory + lateinit var imageLoader: dagger.Lazy @Inject lateinit var profileVerifierLogger: ProfileVerifierLogger @@ -40,7 +40,7 @@ class NiaApplication : Application() { // Initialize Sync; the system responsible for keeping data in the app up to date. Sync.initialize(context = this) profileVerifierLogger() - // We set immediately Coil's image loader factory to prevent initialization with the first image. - Coil.setImageLoader(imageLoaderAsyncFactory) } + + override fun newImageLoader(): ImageLoader = imageLoader.get() } From d9aaec0699c45fd6d59aa1ea13b5068aafd01cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Wed, 31 Jan 2024 11:32:49 +0100 Subject: [PATCH 10/10] Use androidx.tracing everywhere Change-Id: I847827efc08f5cbc00f0c809ad992c267f826e50 --- .../src/main/kotlin/AndroidFeatureConventionPlugin.kt | 2 ++ .../src/main/kotlin/AndroidLibraryConventionPlugin.kt | 3 +++ .../samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt | 2 +- gradle/libs.versions.toml | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt index 7a334beb3..8a76a1ac9 100644 --- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -44,6 +44,8 @@ class AndroidFeatureConventionPlugin : Plugin { add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) + + add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index 8eb329f58..d442d94ef 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -21,6 +21,7 @@ import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests +import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @@ -51,6 +52,8 @@ class AndroidLibraryConventionPlugin : Plugin { } dependencies { add("testImplementation", kotlin("test")) + + add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) } } } diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt index 30134715b..e8b19594f 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt @@ -81,7 +81,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.compose.ui.unit.sp -import androidx.compose.ui.util.trace +import androidx.tracing.trace import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.accompanist.permissions.ExperimentalPermissionsApi diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 795510bce..69a0c42fb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ androidxTestCore = "1.5.0" androidxTestExt = "1.1.5" androidxTestRules = "1.5.0" androidxTestRunner = "1.5.2" -androidxTracing = "1.1.0" +androidxTracing = "1.3.0-alpha02" androidxUiAutomator = "2.2.0" androidxWindowManager = "1.2.0" androidxWork = "2.9.0"