From 81fb1ae984059b874e57e7e5e5d9a5b1c3649908 Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Sun, 28 Nov 2021 20:36:30 +0300 Subject: [PATCH 1/3] Revert "Add Stetho to debug application (#18)" This reverts commit cc2fb772 Stetho seems to be unmaintained anymore It also doesn't work with latest Chrome --- app/build.gradle | 5 ----- app/src/main/java/gq/kirmanak/mealient/App.kt | 6 +----- .../java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt | 6 +----- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3dda312..cc8f742 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -155,11 +155,6 @@ dependencies { // https://mvnrepository.com/artifact/com.google.truth/truth testImplementation "com.google.truth:truth:1.1.3" - // https://github.com/facebook/stetho/releases - def stetho_version = "1.6.0" - implementation "com.facebook.stetho:stetho:$stetho_version" - implementation "com.facebook.stetho:stetho-okhttp3:$stetho_version" - // https://github.com/androidbroadcast/ViewBindingPropertyDelegate/releases implementation "com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.3" } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/App.kt b/app/src/main/java/gq/kirmanak/mealient/App.kt index a5f1488..7a28e6f 100644 --- a/app/src/main/java/gq/kirmanak/mealient/App.kt +++ b/app/src/main/java/gq/kirmanak/mealient/App.kt @@ -1,7 +1,6 @@ package gq.kirmanak.mealient import android.app.Application -import com.facebook.stetho.Stetho import dagger.hilt.android.HiltAndroidApp import timber.log.Timber @@ -9,10 +8,7 @@ import timber.log.Timber class App : Application() { override fun onCreate() { super.onCreate() - if (BuildConfig.DEBUG) { - Timber.plant(Timber.DebugTree()) - Stetho.initializeWithDefaults(this) - } + if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) Timber.v("onCreate() called") } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt b/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt index 97c5ffc..5e05065 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt @@ -1,6 +1,5 @@ package gq.kirmanak.mealient.data.impl -import com.facebook.stetho.okhttp3.StethoInterceptor import gq.kirmanak.mealient.BuildConfig import gq.kirmanak.mealient.data.auth.impl.AuthOkHttpInterceptor import okhttp3.Interceptor @@ -15,11 +14,8 @@ class OkHttpBuilder @Inject constructor( fun buildOkHttp(): OkHttpClient { Timber.v("buildOkHttp() called") val builder = OkHttpClient.Builder() + if (BuildConfig.DEBUG) builder.addNetworkInterceptor(buildLoggingInterceptor()) builder.addNetworkInterceptor(authOkHttpInterceptor) - if (BuildConfig.DEBUG) { - builder.addNetworkInterceptor(buildLoggingInterceptor()) - builder.addNetworkInterceptor(StethoInterceptor()) - } return builder.build() } From 25f14226df40d8289756b9e8caaeea237847db4e Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Sun, 28 Nov 2021 21:55:29 +0300 Subject: [PATCH 2/3] Replace Stetho with Flipper, add LeakCanary --- app/build.gradle | 17 +++- app/proguard-rules-debug.pro | 25 ++++++ .../debug/java/gq/kirmanak/mealient/App.kt | 33 ++++++++ .../gq/kirmanak/mealient/di/DebugModule.kt | 78 +++++++++++++++++++ app/src/main/java/gq/kirmanak/mealient/App.kt | 14 ---- .../mealient/data/impl/OkHttpBuilder.kt | 26 +++---- .../gq/kirmanak/mealient/di/AuthModule.kt | 21 +++-- .../release/java/gq/kirmanak/mealient/App.kt | 7 ++ 8 files changed, 182 insertions(+), 39 deletions(-) create mode 100644 app/proguard-rules-debug.pro create mode 100644 app/src/debug/java/gq/kirmanak/mealient/App.kt create mode 100644 app/src/debug/java/gq/kirmanak/mealient/di/DebugModule.kt delete mode 100644 app/src/main/java/gq/kirmanak/mealient/App.kt create mode 100644 app/src/release/java/gq/kirmanak/mealient/App.kt diff --git a/app/build.gradle b/app/build.gradle index cc8f742..4d5d32c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,7 +40,7 @@ android { debug { minifyEnabled true shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', 'proguard-rules-debug.pro' } release { minifyEnabled true @@ -109,7 +109,7 @@ dependencies { // https://github.com/square/okhttp/tags implementation platform("com.squareup.okhttp3:okhttp-bom:4.9.3") implementation "com.squareup.okhttp3:okhttp" - implementation "com.squareup.okhttp3:logging-interceptor" + debugImplementation "com.squareup.okhttp3:logging-interceptor" testImplementation "com.squareup.okhttp3:mockwebserver" // https://github.com/Kotlin/kotlinx.serialization/releases @@ -157,4 +157,17 @@ dependencies { // https://github.com/androidbroadcast/ViewBindingPropertyDelegate/releases implementation "com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.3" + + // https://github.com/facebook/flipper/releases + def flipper_version = "0.127.0" + debugImplementation "com.facebook.flipper:flipper:$flipper_version" + debugImplementation "com.facebook.flipper:flipper-leakcanary2-plugin:$flipper_version" + debugImplementation "com.facebook.flipper:flipper-network-plugin:$flipper_version" + + // https://github.com/facebook/SoLoader/releases + debugImplementation "com.facebook.soloader:soloader:0.10.3" + + // https://github.com/square/leakcanary/releases + debugImplementation "com.squareup.leakcanary:leakcanary-android:2.7" + } \ No newline at end of file diff --git a/app/proguard-rules-debug.pro b/app/proguard-rules-debug.pro new file mode 100644 index 0000000..793bcb0 --- /dev/null +++ b/app/proguard-rules-debug.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile +### Flipper https://github.com/facebook/flipper/issues/314#issuecomment-940828336 ### +-keep class com.facebook.jni.** { *; } +-keep class com.facebook.flipper.** { *; } +### Flipper https://github.com/facebook/flipper/issues/314#issuecomment-940828336 ### diff --git a/app/src/debug/java/gq/kirmanak/mealient/App.kt b/app/src/debug/java/gq/kirmanak/mealient/App.kt new file mode 100644 index 0000000..072de6f --- /dev/null +++ b/app/src/debug/java/gq/kirmanak/mealient/App.kt @@ -0,0 +1,33 @@ +package gq.kirmanak.mealient + +import android.app.Application +import com.facebook.flipper.android.AndroidFlipperClient +import com.facebook.flipper.android.utils.FlipperUtils +import com.facebook.flipper.core.FlipperPlugin +import com.facebook.soloader.SoLoader +import dagger.hilt.android.HiltAndroidApp +import timber.log.Timber +import javax.inject.Inject + +@HiltAndroidApp +class App : Application() { + // Use @JvmSuppressWildcards because otherwise dagger can't inject it (https://stackoverflow.com/a/43149382) + @Inject + lateinit var flipperPlugins: Set<@JvmSuppressWildcards FlipperPlugin> + + override fun onCreate() { + super.onCreate() + Timber.plant(Timber.DebugTree()) + Timber.v("onCreate() called") + setupFlipper() + } + + private fun setupFlipper() { + if (FlipperUtils.shouldEnableFlipper(this)) { + SoLoader.init(this, false) + val flipperClient = AndroidFlipperClient.getInstance(this) + for (flipperPlugin in flipperPlugins) flipperClient.addPlugin(flipperPlugin) + flipperClient.start() + } + } +} diff --git a/app/src/debug/java/gq/kirmanak/mealient/di/DebugModule.kt b/app/src/debug/java/gq/kirmanak/mealient/di/DebugModule.kt new file mode 100644 index 0000000..12b6d63 --- /dev/null +++ b/app/src/debug/java/gq/kirmanak/mealient/di/DebugModule.kt @@ -0,0 +1,78 @@ +package gq.kirmanak.mealient.di + +import android.content.Context +import com.facebook.flipper.core.FlipperPlugin +import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin +import com.facebook.flipper.plugins.inspector.DescriptorMapping +import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin +import com.facebook.flipper.plugins.leakcanary2.FlipperLeakListener +import com.facebook.flipper.plugins.leakcanary2.LeakCanary2FlipperPlugin +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin +import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.IntoSet +import leakcanary.LeakCanary +import okhttp3.Interceptor +import okhttp3.logging.HttpLoggingInterceptor +import timber.log.Timber +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DebugModule { + @Provides + @Singleton + @IntoSet + fun provideLoggingInterceptor(): Interceptor { + val interceptor = HttpLoggingInterceptor { message -> Timber.tag("OkHttp").v(message) } + interceptor.level = HttpLoggingInterceptor.Level.BODY + return interceptor + } + + @Provides + @Singleton + @IntoSet + fun provideFlipperInterceptor(networkFlipperPlugin: NetworkFlipperPlugin): Interceptor { + return FlipperOkhttpInterceptor(networkFlipperPlugin) + } + + @Provides + @Singleton + fun networkFlipperPlugin() = NetworkFlipperPlugin() + + @Provides + @Singleton + @IntoSet + fun bindNetworkFlipperPlugin(plugin: NetworkFlipperPlugin): FlipperPlugin = plugin + + @Provides + @Singleton + @IntoSet + fun sharedPreferencesPlugin(@ApplicationContext context: Context): FlipperPlugin = + SharedPreferencesFlipperPlugin(context) + + @Provides + @Singleton + @IntoSet + fun leakCanaryPlugin(): FlipperPlugin { + LeakCanary.config = LeakCanary.config.copy(onHeapAnalyzedListener = FlipperLeakListener()) + return LeakCanary2FlipperPlugin() + } + + @Provides + @Singleton + @IntoSet + fun databasesPlugin(@ApplicationContext context: Context): FlipperPlugin = + DatabasesFlipperPlugin(context) + + @Provides + @Singleton + @IntoSet + fun inspectorPlugin(@ApplicationContext context: Context): FlipperPlugin = + InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()) +} diff --git a/app/src/main/java/gq/kirmanak/mealient/App.kt b/app/src/main/java/gq/kirmanak/mealient/App.kt deleted file mode 100644 index 7a28e6f..0000000 --- a/app/src/main/java/gq/kirmanak/mealient/App.kt +++ /dev/null @@ -1,14 +0,0 @@ -package gq.kirmanak.mealient - -import android.app.Application -import dagger.hilt.android.HiltAndroidApp -import timber.log.Timber - -@HiltAndroidApp -class App : Application() { - override fun onCreate() { - super.onCreate() - if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) - Timber.v("onCreate() called") - } -} \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt b/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt index 5e05065..df5d1bf 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt @@ -1,27 +1,21 @@ package gq.kirmanak.mealient.data.impl -import gq.kirmanak.mealient.BuildConfig -import gq.kirmanak.mealient.data.auth.impl.AuthOkHttpInterceptor import okhttp3.Interceptor import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor import timber.log.Timber import javax.inject.Inject -class OkHttpBuilder @Inject constructor( - private val authOkHttpInterceptor: AuthOkHttpInterceptor +class OkHttpBuilder +@Inject +constructor( + // Use @JvmSuppressWildcards because otherwise dagger can't inject it (https://stackoverflow.com/a/43149382) + private val interceptors: Set<@JvmSuppressWildcards Interceptor> ) { + fun buildOkHttp(): OkHttpClient { Timber.v("buildOkHttp() called") - val builder = OkHttpClient.Builder() - if (BuildConfig.DEBUG) builder.addNetworkInterceptor(buildLoggingInterceptor()) - builder.addNetworkInterceptor(authOkHttpInterceptor) - return builder.build() + return OkHttpClient.Builder() + .apply { for (interceptor in interceptors) addNetworkInterceptor(interceptor) } + .build() } - - private fun buildLoggingInterceptor(): Interceptor { - val interceptor = HttpLoggingInterceptor { message -> Timber.tag("OkHttp").v(message) } - interceptor.level = HttpLoggingInterceptor.Level.BODY - return interceptor - } -} \ No newline at end of file +} diff --git a/app/src/main/java/gq/kirmanak/mealient/di/AuthModule.kt b/app/src/main/java/gq/kirmanak/mealient/di/AuthModule.kt index b90d3f4..d401112 100644 --- a/app/src/main/java/gq/kirmanak/mealient/di/AuthModule.kt +++ b/app/src/main/java/gq/kirmanak/mealient/di/AuthModule.kt @@ -4,26 +4,33 @@ import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import dagger.multibindings.IntoSet import gq.kirmanak.mealient.data.auth.AuthDataSource import gq.kirmanak.mealient.data.auth.AuthRepo import gq.kirmanak.mealient.data.auth.AuthStorage import gq.kirmanak.mealient.data.auth.impl.AuthDataSourceImpl +import gq.kirmanak.mealient.data.auth.impl.AuthOkHttpInterceptor import gq.kirmanak.mealient.data.auth.impl.AuthRepoImpl import gq.kirmanak.mealient.data.auth.impl.AuthStorageImpl import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.serialization.ExperimentalSerializationApi +import okhttp3.Interceptor @ExperimentalCoroutinesApi @ExperimentalSerializationApi @Module @InstallIn(SingletonComponent::class) interface AuthModule { - @Binds - fun bindAuthDataSource(authDataSourceImpl: AuthDataSourceImpl): AuthDataSource + @Binds + fun bindAuthDataSource(authDataSourceImpl: AuthDataSourceImpl): AuthDataSource - @Binds - fun bindAuthStorage(authStorageImpl: AuthStorageImpl): AuthStorage + @Binds + fun bindAuthStorage(authStorageImpl: AuthStorageImpl): AuthStorage - @Binds - fun bindAuthRepo(authRepo: AuthRepoImpl): AuthRepo -} \ No newline at end of file + @Binds + fun bindAuthRepo(authRepo: AuthRepoImpl): AuthRepo + + @Binds + @IntoSet + fun bindAuthInterceptor(authOkHttpInterceptor: AuthOkHttpInterceptor): Interceptor +} diff --git a/app/src/release/java/gq/kirmanak/mealient/App.kt b/app/src/release/java/gq/kirmanak/mealient/App.kt new file mode 100644 index 0000000..d929ce5 --- /dev/null +++ b/app/src/release/java/gq/kirmanak/mealient/App.kt @@ -0,0 +1,7 @@ +package gq.kirmanak.mealient + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class App : Application() From 50f6066416a395c17355067e3af86f41c736a79e Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Mon, 27 Dec 2021 13:25:29 +0300 Subject: [PATCH 3/3] Fix memory leaks found by LeakCanary --- .../gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt | 7 +++++++ .../mealient/ui/recipes/info/RecipeInfoFragment.kt | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt index 6f58570..d7fa772 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt @@ -77,4 +77,11 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) { } } } + + override fun onDestroyView() { + super.onDestroyView() + Timber.v("onDestroyView() called") + // Prevent RV leaking through mObservers list in adapter + binding.recipes.adapter = null + } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/info/RecipeInfoFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/info/RecipeInfoFragment.kt index 36e6f88..4c33d71 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/info/RecipeInfoFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/info/RecipeInfoFragment.kt @@ -51,4 +51,14 @@ class RecipeInfoFragment : BottomSheetDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = BottomSheetDialog(requireContext(), R.style.NoShapeBottomSheetDialog) + + override fun onDestroyView() { + super.onDestroyView() + Timber.v("onDestroyView() called") + // Prevent RV leaking through mObservers list in adapter + with(binding) { + ingredientsList.adapter = null + instructionsList.adapter = null + } + } } \ No newline at end of file