OSDN Git Service

Add API to allow apps more control over instantiation
authorJason Monk <jmonk@google.com>
Tue, 6 Jun 2017 22:01:31 +0000 (18:01 -0400)
committerJason Monk <jmonk@google.com>
Thu, 22 Jun 2017 17:18:27 +0000 (13:18 -0400)
Let them control construction of standard manifest elements (like
activities) through Application. This will allow for things like
dependency injection and class loader magic.

Test: manual
Bug: 62534409
Change-Id: I5fc2a7ec52bb0e232966704d16f94a192d033267
Merged-In: Iaa2215b225c3b97f4e9ac809dfe499f98c7ad5f1

core/java/android/app/ActivityThread.java
core/java/android/app/Application.java

index df55080..8a8f8dd 100644 (file)
@@ -2708,8 +2708,14 @@ public final class ActivityThread {
         Activity activity = null;
         try {
             java.lang.ClassLoader cl = appContext.getClassLoader();
-            activity = mInstrumentation.newActivity(
-                    cl, component.getClassName(), r.intent);
+            if (appContext.getApplicationContext() instanceof Application) {
+                activity = ((Application) appContext.getApplicationContext())
+                        .instantiateActivity(cl, component.getClassName(), r.intent);
+            }
+            if (activity == null) {
+                activity = mInstrumentation.newActivity(
+                        cl, component.getClassName(), r.intent);
+            }
             StrictMode.incrementExpectedActivityCount(activity.getClass());
             r.intent.setExtrasClassLoader(cl);
             r.intent.prepareToEnterProcess();
@@ -3234,7 +3240,8 @@ public final class ActivityThread {
             data.intent.setExtrasClassLoader(cl);
             data.intent.prepareToEnterProcess();
             data.setExtrasClassLoader(cl);
-            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
+            receiver = instantiate(cl, component, data.intent, app,
+                    Application::instantiateReceiver);
         } catch (Exception e) {
             if (DEBUG_BROADCAST) Slog.i(TAG,
                     "Finishing failed broadcast to " + data.intent.getComponent());
@@ -3322,12 +3329,13 @@ public final class ActivityThread {
             } else {
                 try {
                     if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
+                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
 
                     java.lang.ClassLoader cl = packageInfo.getClassLoader();
-                    agent = (BackupAgent) cl.loadClass(classname).newInstance();
+                    agent = instantiate(cl, classname, context,
+                            Application::instantiateBackupAgent);
 
                     // set up the agent's context
-                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
                     context.setOuterContext(agent);
                     agent.attach(context);
 
@@ -3387,9 +3395,12 @@ public final class ActivityThread {
         LoadedApk packageInfo = getPackageInfoNoCheck(
                 data.info.applicationInfo, data.compatInfo);
         Service service = null;
+        Application app = null;
         try {
+            app = packageInfo.makeApplication(false, mInstrumentation);
             java.lang.ClassLoader cl = packageInfo.getClassLoader();
-            service = (Service) cl.loadClass(data.info.name).newInstance();
+            service = instantiate(cl, data.info.name, data.intent, app,
+                    Application::instantiateService);
         } catch (Exception e) {
             if (!mInstrumentation.onException(service, e)) {
                 throw new RuntimeException(
@@ -3404,7 +3415,6 @@ public final class ActivityThread {
             ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
             context.setOuterContext(service);
 
-            Application app = packageInfo.makeApplication(false, mInstrumentation);
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManager.getService());
             service.onCreate();
@@ -5721,8 +5731,8 @@ public final class ActivityThread {
 
             try {
                 final ClassLoader cl = instrContext.getClassLoader();
-                mInstrumentation = (Instrumentation)
-                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
+                mInstrumentation = instantiate(cl, data.instrumentationName.getClassName(),
+                        instrContext, Application::instantiateInstrumentation);
             } catch (Exception e) {
                 throw new RuntimeException(
                     "Unable to instantiate instrumentation "
@@ -6267,8 +6277,8 @@ public final class ActivityThread {
 
             try {
                 final java.lang.ClassLoader cl = c.getClassLoader();
-                localProvider = (ContentProvider)cl.
-                    loadClass(info.name).newInstance();
+                localProvider = instantiate(cl, info.name, context,
+                        Application::instantiateProvider);
                 provider = localProvider.getIContentProvider();
                 if (provider == null) {
                     Slog.e(TAG, "Failed to instantiate class " +
@@ -6467,6 +6477,38 @@ public final class ActivityThread {
         }
     }
 
+    private <T> T instantiate(ClassLoader cl, String className, Context c,
+            Instantiator<T> instantiator)
+            throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+        if (c.getApplicationContext() instanceof Application) {
+            T a = instantiator.instantiate((Application) c.getApplicationContext(),
+                    cl, className);
+            if (a != null) return a;
+        }
+        return (T) cl.loadClass(className).newInstance();
+    }
+
+    private <T> T instantiate(ClassLoader cl, String className, Intent intent, Context c,
+            IntentInstantiator<T> instantiator)
+            throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+        if (c.getApplicationContext() instanceof Application) {
+            T a = instantiator.instantiate((Application) c.getApplicationContext(),
+                    cl, className, intent);
+            if (a != null) return a;
+        }
+        return (T) cl.loadClass(className).newInstance();
+    }
+
+    private interface Instantiator<T> {
+        T instantiate(Application app, ClassLoader cl, String className)
+                throws ClassNotFoundException, IllegalAccessException, InstantiationException;
+    }
+
+    private interface IntentInstantiator<T> {
+        T instantiate(Application app, ClassLoader cl, String className, Intent intent)
+                throws ClassNotFoundException, IllegalAccessException, InstantiationException;
+    }
+
     private static class EventLoggingReporter implements EventLogger.Reporter {
         @Override
         public void report (int code, Object... list) {
index 156df36..7fb5e2e 100644 (file)
 
 package android.app;
 
-import java.util.ArrayList;
-
 import android.annotation.CallSuper;
+import android.app.backup.BackupAgent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks;
 import android.content.ComponentCallbacks2;
+import android.content.ContentProvider;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
 
+import java.util.ArrayList;
+
 /**
  * Base class for maintaining global application state. You can provide your own
  * implementation by creating a subclass and specifying the fully-qualified name
@@ -289,4 +292,79 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
             }
         }
     }
+
+    /**
+     * Allows application to override the creation of activities. This can be used to
+     * perform things such as dependency injection or class loader changes to these
+     * classes. Return null to use the default creation flow.
+     * @param cl The default classloader to use for instantiation.
+     * @param className The class to be instantiated.
+     * @param intent Intent creating the class.
+     * @hide
+     */
+    public Activity instantiateActivity(ClassLoader cl, String className, Intent intent) {
+        return null;
+    }
+
+    /**
+     * Allows application to override the creation of receivers. This can be used to
+     * perform things such as dependency injection or class loader changes to these
+     * classes. Return null to use the default creation flow.
+     * @param cl The default classloader to use for instantiation.
+     * @param className The class to be instantiated.
+     * @param intent Intent creating the class.
+     * @hide
+     */
+    public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent) {
+        return null;
+    }
+
+    /**
+     * Allows application to override the creation of services. This can be used to
+     * perform things such as dependency injection or class loader changes to these
+     * classes. Return null to use the default creation flow.
+     * @param cl The default classloader to use for instantiation.
+     * @param className The class to be instantiated.
+     * @param intent Intent creating the class.
+     * @hide
+     */
+    public Service instantiateService(ClassLoader cl, String className, Intent intent) {
+        return null;
+    }
+
+    /**
+     * Allows application to override the creation of providers. This can be used to
+     * perform things such as dependency injection or class loader changes to these
+     * classes. Return null to use the default creation flow.
+     * @param cl The default classloader to use for instantiation.
+     * @param className The class to be instantiated.
+     * @hide
+     */
+    public ContentProvider instantiateProvider(ClassLoader cl, String className) {
+        return null;
+    }
+
+    /**
+     * Allows application to override the creation of backup agents. This can be used to
+     * perform things such as dependency injection or class loader changes to these
+     * classes. Return null to use the default creation flow.
+     * @param cl The default classloader to use for instantiation.
+     * @param className The class to be instantiated.
+     * @hide
+     */
+    public BackupAgent instantiateBackupAgent(ClassLoader cl, String className) {
+        return null;
+    }
+
+    /**
+     * Allows application to override the creation of instrumentation. This can be used to
+     * perform things such as dependency injection or class loader changes to these
+     * classes. Return null to use the default creation flow.
+     * @param cl The default classloader to use for instantiation.
+     * @param className The class to be instantiated.
+     * @hide
+     */
+    public Instrumentation instantiateInstrumentation(ClassLoader cl, String className) {
+        return null;
+    }
 }
\ No newline at end of file