Use @StringKey instead of @ViewModelKey.
Using string keys avoids unnecessary class loading.
Additionally provide a consuming optimization rule to keep
the class names of the @ViewModelInject annotated ViewModel
classes since the class canonical name is used for the
multibinding map of assisted factories.
Test: ./gradlew hilt:integration-tests:hilt-testapp-viewmodel:installRelease
Change-Id: I6286f2a2b25ae07f68c85230e3e7ec974964c6d3
diff --git a/hilt/integration-tests/viewmodelapp/build.gradle b/hilt/integration-tests/viewmodelapp/build.gradle
index 3319ac1..7177380 100644
--- a/hilt/integration-tests/viewmodelapp/build.gradle
+++ b/hilt/integration-tests/viewmodelapp/build.gradle
@@ -24,6 +24,15 @@
id("dagger.hilt.android.plugin")
}
+android {
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ }
+ }
+}
+
dependencies {
implementation(project(":activity:activity"))
implementation(project(":fragment:fragment-ktx"))
diff --git a/lifecycle/lifecycle-viewmodel-hilt-compiler/src/main/kotlin/androidx/lifecycle/hilt/ClassNames.kt b/lifecycle/lifecycle-viewmodel-hilt-compiler/src/main/kotlin/androidx/lifecycle/hilt/ClassNames.kt
index 9310102..71e537a 100644
--- a/lifecycle/lifecycle-viewmodel-hilt-compiler/src/main/kotlin/androidx/lifecycle/hilt/ClassNames.kt
+++ b/lifecycle/lifecycle-viewmodel-hilt-compiler/src/main/kotlin/androidx/lifecycle/hilt/ClassNames.kt
@@ -31,6 +31,6 @@
val VIEW_MODEL_ASSISTED_FACTORY =
ClassName.get("androidx.lifecycle.hilt", "ViewModelAssistedFactory")
val VIEW_MODEL = ClassName.get("androidx.lifecycle", "ViewModel")
- val VIEW_MODEL_KEY = ClassName.get("androidx.lifecycle.hilt", "ViewModelKey")
val SAVED_STATE_HANDLE = ClassName.get("androidx.lifecycle", "SavedStateHandle")
+ val STRING_KEY = ClassName.get("dagger.multibindings", "StringKey")
}
diff --git a/lifecycle/lifecycle-viewmodel-hilt-compiler/src/main/kotlin/androidx/lifecycle/hilt/HiltViewModelGenerator.kt b/lifecycle/lifecycle-viewmodel-hilt-compiler/src/main/kotlin/androidx/lifecycle/hilt/HiltViewModelGenerator.kt
index 6961686..0364e54 100644
--- a/lifecycle/lifecycle-viewmodel-hilt-compiler/src/main/kotlin/androidx/lifecycle/hilt/HiltViewModelGenerator.kt
+++ b/lifecycle/lifecycle-viewmodel-hilt-compiler/src/main/kotlin/androidx/lifecycle/hilt/HiltViewModelGenerator.kt
@@ -17,6 +17,7 @@
package androidx.lifecycle.hilt
import androidx.lifecycle.hilt.ext.L
+import androidx.lifecycle.hilt.ext.S
import androidx.lifecycle.hilt.ext.T
import androidx.lifecycle.hilt.ext.W
import androidx.lifecycle.hilt.ext.addGeneratedAnnotation
@@ -42,7 +43,7 @@
* public interface $_HiltModule {
* @Binds
* @IntoMap
- * @ViewModelKey($.class)
+ * @StringKey("pkg.$")
* ViewModelAssistedFactory<? extends ViewModel> bind($_AssistedFactory factory)
* }
* ```
@@ -101,8 +102,8 @@
.addAnnotation(ClassNames.BINDS)
.addAnnotation(ClassNames.INTO_MAP)
.addAnnotation(
- AnnotationSpec.builder(ClassNames.VIEW_MODEL_KEY)
- .addMember("value", "$T.class", viewModelElements.className)
+ AnnotationSpec.builder(ClassNames.STRING_KEY)
+ .addMember("value", S, viewModelElements.className.canonicalName())
.build())
.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
.returns(
diff --git a/lifecycle/lifecycle-viewmodel-hilt-compiler/src/test/kotlin/androidx/lifecycle/hilt/HiltViewModelGeneratorTest.kt b/lifecycle/lifecycle-viewmodel-hilt-compiler/src/test/kotlin/androidx/lifecycle/hilt/HiltViewModelGeneratorTest.kt
index 851d483..0df260d 100644
--- a/lifecycle/lifecycle-viewmodel-hilt-compiler/src/test/kotlin/androidx/lifecycle/hilt/HiltViewModelGeneratorTest.kt
+++ b/lifecycle/lifecycle-viewmodel-hilt-compiler/src/test/kotlin/androidx/lifecycle/hilt/HiltViewModelGeneratorTest.kt
@@ -407,12 +407,12 @@
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.hilt.ViewModelAssistedFactory;
- import androidx.lifecycle.hilt.ViewModelKey;
import dagger.Binds;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.ActivityRetainedComponent;
import dagger.multibindings.IntoMap;
+ import dagger.multibindings.StringKey;
import $GENERATED_TYPE;
$GENERATED_ANNOTATION
@@ -421,7 +421,7 @@
public interface MyViewModel_HiltModule {
@Binds
@IntoMap
- @ViewModelKey(MyViewModel.class)
+ @StringKey("androidx.lifecycle.hilt.test.MyViewModel")
ViewModelAssistedFactory<? extends ViewModel> bind(MyViewModel_AssistedFactory factory)
}
""".toJFO("androidx.lifecycle.hilt.test.MyViewModel_HiltModule")
@@ -480,12 +480,12 @@
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.hilt.ViewModelAssistedFactory;
- import androidx.lifecycle.hilt.ViewModelKey;
import dagger.Binds;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.ActivityRetainedComponent;
import dagger.multibindings.IntoMap;
+ import dagger.multibindings.StringKey;
import $GENERATED_TYPE;
$GENERATED_ANNOTATION
@@ -494,7 +494,7 @@
public interface Outer_InnerViewModel_HiltModule {
@Binds
@IntoMap
- @ViewModelKey(Outer.InnerViewModel.class)
+ @StringKey("androidx.lifecycle.hilt.test.Outer.InnerViewModel")
ViewModelAssistedFactory<? extends ViewModel> bind(
Outer_InnerViewModel_AssistedFactory factory)
}
diff --git a/lifecycle/lifecycle-viewmodel-hilt/build.gradle b/lifecycle/lifecycle-viewmodel-hilt/build.gradle
index 82aae8e..a8d80dc 100644
--- a/lifecycle/lifecycle-viewmodel-hilt/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-hilt/build.gradle
@@ -26,6 +26,12 @@
id("com.android.library")
}
+android {
+ buildTypes.all {
+ consumerProguardFiles 'proguard-rules.pro'
+ }
+}
+
dependencies {
api("androidx.annotation:annotation:1.1.0")
api(project(":lifecycle:lifecycle-viewmodel-hilt-common"))
diff --git a/lifecycle/lifecycle-viewmodel-hilt/proguard-rules.pro b/lifecycle/lifecycle-viewmodel-hilt/proguard-rules.pro
new file mode 100644
index 0000000..01a9362
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-hilt/proguard-rules.pro
@@ -0,0 +1,5 @@
+# Keep class names of Hilt injected ViewModels since their name are used as a multibinding map key.
+-keepclasseswithmembernames class * extends androidx.lifecycle.ViewModel {
+ @androidx.lifecycle.hilt.ViewModelInject
+ <init>(...);
+}
\ No newline at end of file
diff --git a/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelFactory.java b/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelFactory.java
index 6a35805..9445cea 100644
--- a/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelFactory.java
+++ b/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelFactory.java
@@ -34,14 +34,13 @@
*/
public final class ViewModelFactory extends AbstractSavedStateViewModelFactory {
- private final Map<
- Class<? extends ViewModel>,
+ private final Map<String,
Provider<ViewModelAssistedFactory<? extends ViewModel>>> mViewModelFactories;
ViewModelFactory(
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs,
- @NonNull Map<Class<? extends ViewModel>,
+ @NonNull Map<String,
Provider<ViewModelAssistedFactory<? extends ViewModel>>> viewModelFactories) {
super(owner, defaultArgs);
this.mViewModelFactories = viewModelFactories;
@@ -53,7 +52,15 @@
protected <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass,
@NonNull SavedStateHandle handle) {
// TODO(danysantiago): What to do with 'key' ???
- // TODO(danysantiago): Better exception for missing class
- return (T) mViewModelFactories.get(modelClass).get().create(handle);
+ Provider<ViewModelAssistedFactory<? extends ViewModel>> factoryProvider =
+ mViewModelFactories.get(modelClass.getCanonicalName());
+ if (factoryProvider == null) {
+ // TODO(danysantiago): Consider still creating those ViewModel that have a default
+ // no-arg constructor.
+ throw new IllegalStateException("Unable to create the ViewModel class "
+ + modelClass.getCanonicalName() + ". Did you forgot to annotate its "
+ + "constructor with @ViewModelInject?");
+ }
+ return (T) factoryProvider.get().create(handle);
}
}
diff --git a/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelFactoryModules.java b/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelFactoryModules.java
index 74da3d7a..fc2b020 100644
--- a/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelFactoryModules.java
+++ b/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelFactoryModules.java
@@ -54,16 +54,14 @@
@NonNull
@Multibinds
- abstract Map<Class<? extends ViewModel>,
- ViewModelAssistedFactory<? extends ViewModel>> viewModelFactoriesMap();
+ abstract Map<String, ViewModelAssistedFactory<? extends ViewModel>> viewModelFactoriesMap();
@Provides
@NonNull
@ActivityViewModelFactory
static ViewModelFactory provideFactory(
@NonNull Activity activity,
- @NonNull Map<Class<? extends ViewModel>,
- Provider<ViewModelAssistedFactory<? extends ViewModel>>>
+ @NonNull Map<String, Provider<ViewModelAssistedFactory<? extends ViewModel>>>
viewModelFactories) {
// Hilt guarantees concrete activity is a subclass of ComponentActivity.
SavedStateRegistryOwner owner = (ComponentActivity) activity;
@@ -84,8 +82,7 @@
@FragmentViewModelFactory
static ViewModelFactory provideFactory(
@NonNull Fragment fragment,
- @NonNull Map<Class<? extends ViewModel>,
- Provider<ViewModelAssistedFactory<? extends ViewModel>>>
+ @NonNull Map<String, Provider<ViewModelAssistedFactory<? extends ViewModel>>>
viewModelFactories) {
Bundle defaultArgs = fragment.getArguments();
return new ViewModelFactory(fragment, defaultArgs, viewModelFactories);
diff --git a/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelKey.java b/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelKey.java
deleted file mode 100644
index 8f1ad82..0000000
--- a/lifecycle/lifecycle-viewmodel-hilt/src/main/java/androidx/lifecycle/hilt/ViewModelKey.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * http://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 androidx.lifecycle.hilt;
-
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.ViewModel;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import dagger.MapKey;
-
-/**
- * Dagger multibinding key for ViewModels
- *
- * @hide
- */
-@MapKey
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-public @interface ViewModelKey {
- /**
- * The ViewModel class used as key.
- * @return the class.
- */
- // TODO(danysantiago): Change to use strings and add optimizer rule to avoid class loading.
- Class<? extends ViewModel> value();
-}