Add `initializeComponent` API
Test: Unit tests pass. Ran instrumentation tests.
Change-Id: I2208803fcf1ff84f0c2c93025b6c343d37a5ba22
diff --git a/startup/startup-runtime/api/1.0.0-alpha01.txt b/startup/startup-runtime/api/1.0.0-alpha01.txt
index da9dd9d..ea4f606 100644
--- a/startup/startup-runtime/api/1.0.0-alpha01.txt
+++ b/startup/startup-runtime/api/1.0.0-alpha01.txt
@@ -3,8 +3,7 @@
public final class AppInitializer {
method public static androidx.startup.AppInitializer getInstance(android.content.Context);
- method public void initializeAllComponents();
- method public void initializeComponents(java.util.List<java.lang.Class<?>!>);
+ method public <T> T initializeComponent(Class<? extends androidx.startup.ComponentInitializer<T!>>);
}
public interface ComponentInitializer<T> {
diff --git a/startup/startup-runtime/api/current.txt b/startup/startup-runtime/api/current.txt
index da9dd9d..ea4f606 100644
--- a/startup/startup-runtime/api/current.txt
+++ b/startup/startup-runtime/api/current.txt
@@ -3,8 +3,7 @@
public final class AppInitializer {
method public static androidx.startup.AppInitializer getInstance(android.content.Context);
- method public void initializeAllComponents();
- method public void initializeComponents(java.util.List<java.lang.Class<?>!>);
+ method public <T> T initializeComponent(Class<? extends androidx.startup.ComponentInitializer<T!>>);
}
public interface ComponentInitializer<T> {
diff --git a/startup/startup-runtime/api/public_plus_experimental_1.0.0-alpha01.txt b/startup/startup-runtime/api/public_plus_experimental_1.0.0-alpha01.txt
index da9dd9d..ea4f606 100644
--- a/startup/startup-runtime/api/public_plus_experimental_1.0.0-alpha01.txt
+++ b/startup/startup-runtime/api/public_plus_experimental_1.0.0-alpha01.txt
@@ -3,8 +3,7 @@
public final class AppInitializer {
method public static androidx.startup.AppInitializer getInstance(android.content.Context);
- method public void initializeAllComponents();
- method public void initializeComponents(java.util.List<java.lang.Class<?>!>);
+ method public <T> T initializeComponent(Class<? extends androidx.startup.ComponentInitializer<T!>>);
}
public interface ComponentInitializer<T> {
diff --git a/startup/startup-runtime/api/public_plus_experimental_current.txt b/startup/startup-runtime/api/public_plus_experimental_current.txt
index da9dd9d..ea4f606 100644
--- a/startup/startup-runtime/api/public_plus_experimental_current.txt
+++ b/startup/startup-runtime/api/public_plus_experimental_current.txt
@@ -3,8 +3,7 @@
public final class AppInitializer {
method public static androidx.startup.AppInitializer getInstance(android.content.Context);
- method public void initializeAllComponents();
- method public void initializeComponents(java.util.List<java.lang.Class<?>!>);
+ method public <T> T initializeComponent(Class<? extends androidx.startup.ComponentInitializer<T!>>);
}
public interface ComponentInitializer<T> {
diff --git a/startup/startup-runtime/api/restricted_1.0.0-alpha01.txt b/startup/startup-runtime/api/restricted_1.0.0-alpha01.txt
index da9dd9d..ea4f606 100644
--- a/startup/startup-runtime/api/restricted_1.0.0-alpha01.txt
+++ b/startup/startup-runtime/api/restricted_1.0.0-alpha01.txt
@@ -3,8 +3,7 @@
public final class AppInitializer {
method public static androidx.startup.AppInitializer getInstance(android.content.Context);
- method public void initializeAllComponents();
- method public void initializeComponents(java.util.List<java.lang.Class<?>!>);
+ method public <T> T initializeComponent(Class<? extends androidx.startup.ComponentInitializer<T!>>);
}
public interface ComponentInitializer<T> {
diff --git a/startup/startup-runtime/api/restricted_current.txt b/startup/startup-runtime/api/restricted_current.txt
index da9dd9d..ea4f606 100644
--- a/startup/startup-runtime/api/restricted_current.txt
+++ b/startup/startup-runtime/api/restricted_current.txt
@@ -3,8 +3,7 @@
public final class AppInitializer {
method public static androidx.startup.AppInitializer getInstance(android.content.Context);
- method public void initializeAllComponents();
- method public void initializeComponents(java.util.List<java.lang.Class<?>!>);
+ method public <T> T initializeComponent(Class<? extends androidx.startup.ComponentInitializer<T!>>);
}
public interface ComponentInitializer<T> {
diff --git a/startup/startup-runtime/src/androidTest/java/androidx/startup/AppInitializerTest.kt b/startup/startup-runtime/src/androidTest/java/androidx/startup/AppInitializerTest.kt
index 6b1172e..899c90e 100644
--- a/startup/startup-runtime/src/androidTest/java/androidx/startup/AppInitializerTest.kt
+++ b/startup/startup-runtime/src/androidTest/java/androidx/startup/AppInitializerTest.kt
@@ -43,29 +43,15 @@
}
@Test
- fun basicUsageTest() {
- appInitializer.initializeComponents(listOf<Class<*>>(InitializerNoDependencies::class.java))
+ fun basicInitializationTest() {
+ appInitializer.initializeComponent(InitializerNoDependencies::class.java)
+ assertThat(appInitializer.mInitialized.size, `is`(1))
assertTrue(appInitializer.mInitialized.containsKey(InitializerNoDependencies::class.java))
}
@Test
- fun basicInitializationTest() {
- val initializing = mutableSetOf<Class<*>>()
- val components = listOf<Class<*>>(InitializerNoDependencies::class.java)
- appInitializer.doInitialize(components, initializing)
- assertThat(initializing.size, `is`(0))
- assertThat(appInitializer.mInitialized.size, `is`(1))
- for (component in components) {
- assertTrue(appInitializer.mInitialized.containsKey(component))
- }
- }
-
- @Test
fun initializationWithDependencies() {
- val initializing = mutableSetOf<Class<*>>()
- val components = listOf<Class<*>>(InitializerWithDependency::class.java)
- appInitializer.doInitialize(components, initializing)
- assertThat(initializing.size, `is`(0))
+ appInitializer.initializeComponent(InitializerWithDependency::class.java)
assertThat(appInitializer.mInitialized.size, `is`(2))
assertTrue(appInitializer.mInitialized.containsKey(InitializerNoDependencies::class.java))
assertTrue(appInitializer.mInitialized.containsKey(InitializerWithDependency::class.java))
@@ -73,10 +59,8 @@
@Test
fun initializationWithCyclicDependencies() {
- val initializing = mutableSetOf<Class<*>>()
- val components = listOf<Class<*>>(CyclicDependencyInitializer::class.java)
try {
- appInitializer.doInitialize(components, initializing)
+ appInitializer.initializeComponent(CyclicDependencyInitializer::class.java)
fail()
} catch (exception: StartupException) {
assertThat(exception.localizedMessage, containsString("Cycle detected."))
diff --git a/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.java b/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.java
index 4be6352..c9e7ad1 100644
--- a/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.java
+++ b/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.java
@@ -24,10 +24,7 @@
import android.os.Bundle;
import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -53,9 +50,6 @@
private static final Object sLock = new Object();
@NonNull
- final List<Class<?>> mDiscovered;
-
- @NonNull
final Map<Class<?>, Object> mInitialized;
@NonNull
@@ -69,7 +63,6 @@
AppInitializer(@NonNull Context context) {
mContext = context.getApplicationContext();
mInitialized = new HashMap<>();
- mDiscovered = discoverComponents();
}
/**
@@ -88,69 +81,51 @@
}
/**
- * Discovers an initializes all available {@link ComponentInitializer} classes based on the
- * merged manifest `<meta-data>` entries in the `AndroidManifest.xml`.
- */
- public void initializeAllComponents() {
- initializeComponents(mDiscovered);
- }
-
- /**
- * Initializes a {@link List} of {@link ComponentInitializer} class types.
+ * Initializes a {@link ComponentInitializer} class type.
*
- * @param components The {@link List} of {@link Class}es that represent all discovered
- * {@link ComponentInitializer}s
+ * @param component The {@link Class} of {@link ComponentInitializer} to initialize.
+ * @param <T> The instance type being initialized
+ * @return The initialized instance
*/
- public void initializeComponents(@NonNull List<Class<?>> components) {
- synchronized (sLock) {
- doInitialize(components, new HashSet<>());
- }
+ @NonNull
+ @SuppressWarnings("unused")
+ public <T> T initializeComponent(@NonNull Class<? extends ComponentInitializer<T>> component) {
+ return doInitialize(component, new HashSet<>());
}
- /**
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- public void doInitialize(
- @NonNull List<Class<?>> components,
+ @NonNull
+ @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
+ <T> T doInitialize(
+ @NonNull Class<? extends ComponentInitializer<?>> component,
@NonNull Set<Class<?>> initializing) {
-
- for (Class<?> component : components) {
+ synchronized (sLock) {
if (initializing.contains(component)) {
String message = String.format(
"Cannot initialize %s. Cycle detected.", component.getName()
);
throw new IllegalStateException(message);
}
+ Object result;
if (!mInitialized.containsKey(component)) {
initializing.add(component);
try {
Object instance = component.getDeclaredConstructor().newInstance();
- if (!(instance instanceof ComponentInitializer<?>)) {
- String message = String.format(
- "%s is not a subtype of ComponentInitializer", component.getName()
- );
- throw new IllegalStateException(message);
- }
ComponentInitializer<?> initializer = (ComponentInitializer<?>) instance;
List<Class<? extends ComponentInitializer<?>>> dependencies =
initializer.dependencies();
- List<Class<?>> filtered = null;
+
if (!dependencies.isEmpty()) {
- filtered = new ArrayList<>(dependencies.size());
for (Class<? extends ComponentInitializer<?>> clazz : dependencies) {
if (!mInitialized.containsKey(clazz)) {
- filtered.add(clazz);
+ doInitialize(clazz, initializing);
}
}
}
- if (filtered != null && !filtered.isEmpty()) {
- doInitialize(filtered, initializing);
- }
+
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Initializing %s", component.getName()));
}
- Object result = initializer.create(mContext);
+ result = initializer.create(mContext);
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Initialized %s", component.getName()));
}
@@ -159,16 +134,15 @@
} catch (Throwable throwable) {
throw new StartupException(throwable);
}
+ } else {
+ result = mInitialized.get(component);
}
+ return (T) result;
}
}
- /**
- * @hide
- */
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- public List<Class<?>> discoverComponents() {
+ @SuppressWarnings("unchecked")
+ void discoverAndInitialize() {
try {
ApplicationInfo applicationInfo =
mContext.getPackageManager()
@@ -177,21 +151,22 @@
Bundle metadata = applicationInfo.metaData;
String startup = mContext.getString(R.string.androidx_startup);
if (metadata != null) {
- List<Class<?>> components = new ArrayList<>(metadata.size());
+ Set<Class<?>> initializing = new HashSet<>();
Set<String> keys = metadata.keySet();
for (String key : keys) {
String value = metadata.getString(key, null);
if (startup.equals(value)) {
Class<?> clazz = Class.forName(key);
- if (StartupLogger.DEBUG) {
- StartupLogger.i(String.format("Discovered %s", key));
+ if (ComponentInitializer.class.isAssignableFrom(clazz)) {
+ Class<? extends ComponentInitializer<?>> component =
+ (Class<? extends ComponentInitializer<?>>) clazz;
+ if (StartupLogger.DEBUG) {
+ StartupLogger.i(String.format("Discovered %s", key));
+ }
+ doInitialize(component, initializing);
}
- components.add(clazz);
}
}
- return components;
- } else {
- return Collections.emptyList();
}
} catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
throw new StartupException(exception);
diff --git a/startup/startup-runtime/src/main/java/androidx/startup/InitializationProvider.java b/startup/startup-runtime/src/main/java/androidx/startup/InitializationProvider.java
index 24873746..d94aafc 100644
--- a/startup/startup-runtime/src/main/java/androidx/startup/InitializationProvider.java
+++ b/startup/startup-runtime/src/main/java/androidx/startup/InitializationProvider.java
@@ -39,7 +39,7 @@
public boolean onCreate() {
Context context = getContext();
if (context != null) {
- AppInitializer.getInstance(context).initializeAllComponents();
+ AppInitializer.getInstance(context).discoverAndInitialize();
} else {
throw new StartupException("Context cannot be null");
}