Android Learning Road (14) Explicación detallada del contexto

1. Introducción

En el desarrollo o entrevista de Android, los cuatro componentes principales son inseparables. Al crear o iniciar estos componentes, no puede crear objetos de instancia directamente a través de la nueva palabra clave seguida del nombre de la clase, sino que debe tener su propio entorno de contexto. que se discutirá en este artículo.

1.1 Descripción general del contexto

Contexto, literalmente significa: contexto, entorno, contexto. En el sistema Android, puede entenderse como el entorno de trabajo del objeto actual en la aplicación. Define internamente muchas interfaces para acceder a información global en el entorno de la aplicación. A través de ella, puede acceder a clases relacionadas con los recursos de la aplicación, como Recursos, AssetManager, Paquete e información relacionada con permisos. También se puede utilizar para llamar a operaciones a nivel de aplicación, como iniciar actividades y servicios, enviar transmisiones, etc.

Echemos un vistazo a los comentarios oficiales sobre la clase Contexto:

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {...}

Traducción: Context proporciona una interfaz para información global sobre el entorno de la aplicación. Es una clase abstracta y su implementación la proporciona el sistema Android. Permite el acceso a recursos y tipos que caracterizan la aplicación y es un contexto que gobierna algunos recursos (variables de entorno de la aplicación, etc.).

1.2 Arquitectura de contexto

  • Contexto: es una clase abstracta que define una serie de interfaces para interactuar con el sistema.
  • ContextWrapper: Heredado de la clase abstracta Context, es una clase contenedora (modo decorador) de la clase Context. Mantiene internamente una variable miembro de tipo Context mBase que apunta a un objeto ContextImpl. Las llamadas a métodos en ContextWrapper son para llamar a los métodos en ContextImpl. a través de mBase Aquí se utiliza el modo Proxy.
  • ContextImpl: hereda de la clase abstracta Context, implementa los métodos abstractos en la clase Context y es la clase de implementación específica de la clase Context. Proporciona un entorno de contexto para la actividad y otros componentes de la aplicación, y él implementa los métodos de contexto utilizados en la aplicación.
  • ContextThemeWrapper: heredado de la clase ContextWrapper, agrega lógica relacionada con el tema sobre la base de ContextWrapper, es decir, puede especificar la clase contenedora de contexto de Theme, que se utiliza para proporcionar el conjunto de atributos de Theme para la Vista cuando se construye.

Las principales clases principales involucradas en la clase de implementación ContextImpl son: ActivityThread, LoadedApk, PackageManager y ResourcesManager. Estas clases son todas singleton y comparten el mismo objeto en un proceso de aplicación.
Contextlmpl es una clase liviana, mientras que LoadedApk es una clase pesada. La mayoría de las funciones pesadas para operaciones de paquetes en Contextlmpl en realidad recurren a los métodos correspondientes del objeto LoadedApk.

La actividad hereda de ContextThemeWrapper, y la Aplicación y el Servicio heredan de ContextWrapper. Heredan directa o indirectamente de la clase ContextWrapper, por lo que también tienen una variable miembro mBase del tipo Context que apunta a un objeto ContextImpl. ContextImpl es la clase de implementación específica de Clase de contexto, por lo que todos tienen todas las funciones proporcionadas por Context.

Modo proxy: Pertenece al modo estructural, que se refiere a proporcionar un proxy para que otros objetos controlen el acceso a este objeto. El modo proxy se divide en proxy estático y proxy dinámico.
Patrón decorador: También llamado patrón de empaquetado, también es un patrón estructural, se refiere a un patrón que agrega dinámicamente algunas responsabilidades al objeto (es decir, agrega sus funciones adicionales) sin cambiar la estructura del objeto existente.

1.3 Alcance del contexto

Nota: Si necesita cargar la interfaz de visualización del diseño, use Actividad como el campo Contexto tanto como sea posible. Aunque puede usar Aplicación y Servicio como el campo Contexto para cargar el diseño e iniciar la Actividad, no se recomienda para evitar errores. o la interfaz de usuario utiliza inexplicablemente el tema predeterminado del sistema.

1.4 Resumen

A través del análisis de esta sección, tenemos una comprensión simple del concepto, la arquitectura y el alcance de Context, ¡así que exploremos en profundidad!

2. Explicación detallada del contexto

Cuando hablamos de la arquitectura de Context anteriormente, aprendimos que sus clases de implementación finales son: Aplicación, Servicio y Actividad. Todas ellas contienen la implementación real de la clase abstracta de Context ContextImpl. A continuación, discutiremos y analizaremos estas tres clases de implementación respectivamente. .

2.1 Contexto de la aplicación

La aplicación es un componente del sistema en el marco del sistema Android. Cuando se inicia una aplicación de Android, el sistema creará un solo objeto de clase de aplicación para almacenar cierta información del sistema, es decir, la aplicación es un singleton.

Por lo general, no es necesario especificar una aplicación durante el proceso de desarrollo. El sistema la crea automáticamente para los desarrolladores. Si desea crear una aplicación personalizada para la aplicación, solo necesita crear una clase que herede la aplicación y registrarla en la etiqueta de la aplicación. en el archivo AndroidManifest.xml (simplemente agregue el atributo de nombre a la etiqueta de la aplicación y agregue el nombre de la aplicación personalizada).

Por lo general, el propósito de personalizar la aplicación es realizar algún trabajo de inicialización global cuando se inicia la aplicación. Cuando se inicia la aplicación, la aplicación se crea y se inicia sincrónicamente. El sistema creará un PID, que es el ID del proceso, y todas las actividades serán En este proceso, durante la ejecución, se pueden obtener los valores de estas variables globales inicializadas, y dado que el objeto Aplicación siempre existirá durante toda la ejecución de la aplicación, algunos desarrolladores escribirán algunos métodos de herramientas en la Aplicación para obtenerlos y usarlos globalmente. , pero recuerde no hacerlo. De esta manera, la Aplicación se utiliza como una clase de herramienta. Nota: Esto viola gravemente los principios de Google para el diseño de Aplicaciones y también viola el principio de responsabilidad única en los patrones de diseño.

2.1.1 Instancia de aplicación personalizada

open class TestApplication : Application() {
    // 全局 context
    companion object{
        lateinit var context: Context
    }
    override fun onCreate() {
        super.onCreate()
        context = this
        initSDKs() // 全局初始化
    }

    private fun initSDKs() {...}
}

Herede la aplicación y anule el método onCreate (), que se llama cuando se crea la aplicación. Generalmente se usa para la inicialización global, como la inicialización de SDK de terceros, la configuración del entorno, etc. Al mismo tiempo, el contexto global El objeto del tipo Aplicación se puede obtener a través del contexto TestApplication #.

2.1.2 Obtener instancia de aplicación

class TestActivity : Activity() {
    private val TAG: String = TestActivity::class.java.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)

        val applicationContext = [email protected]()
        val application = [email protected]()

        Log.e(TAG, "application: $application")
        Log.e(TAG, "applicationContext: $applicationContext")
    }
}

Generalmente existen dos métodos para obtener la Solicitud:

  • Actividad # getApplication() y Servicio # getApplication()
  • Contexto # getApplicationContext()

La aplicación se puede obtener a través de getApplication() y getApplicationContext(), entonces, ¿cuál es la diferencia entre ellos?

Puede ver en el resultado del registro que obtienen el mismo objeto, pero algunos estudiantes quieren preguntar, ¿por qué es necesario proporcionar dos métodos con la misma función? Porque el método getApplication() es más intuitivo, pero solo se puede llamar en escenarios de Actividad y Servicio. El método getApplicationContext() tiene un alcance de aplicación más amplio y se puede llamar a través del objeto Context en cualquier escenario.

2.1.3 Proceso de creación del contexto de la aplicación

El contexto de la aplicación se crea cuando se crea la aplicación. Para realizar un seguimiento de su creación, debe explorarlo desde el proceso de inicio de la aplicación, es decir, desde hacer clic en el icono de la aplicación de escritorio hasta un determinado paso del proceso cuando se inicia la aplicación. Se muestra la interfaz de la aplicación. Para conocer el paso específico de creación, puede consultar el análisis detallado de este artículo: una explicación detallada del proceso de inicio de la actividad de Android R (11.0) .

Describamos brevemente el proceso:

  • La clase ActivityThread sirve como clase de inicialización de la aplicación: llama a ActivityThread en su método de entrada, método main(), # método adjunto(), y luego llama al método adjuntoApplication() de AMS en el proceso system_server entre procesos a través de la comunicación Binder y pasa ApplicationThread. como parámetro...
  • A través del ApplicationThread pasado, la comunicación entre procesos llama al método ApplicationThread # bindApplication () en el proceso de la aplicación para vincular la Aplicación.
  • ApplicationThread # bindApplication (), cree el objeto AppBindData y luego envíe el mensaje del controlador de tipo BIND_APPLICATION a través de la clase interna H, y luego llame al método ActivityThread # handleBindApplication () para crear y vincular la aplicación.

2.1.4 Diagrama de tiempos

Pregunta de la entrevista: ¿ActivityThread es un hilo?
La clase ActivityThread es una clase de inicialización de aplicaciones. Su método main() es el método de entrada de la aplicación. También es lo que llamamos el "hilo principal". Sin embargo, ActivityThread en sí no es un hilo. La razón por la que se llama El "hilo principal" se debe a que se ejecuta en el hilo principal. Entonces ActivityThread es parte del hilo principal, pero no representa el hilo principal. Su función es la siguiente:

  • ActivityThread es responsable de crear el objeto Aplicación y administrar las llamadas al método de su ciclo de vida.
  • ActivityThread gestiona las llamadas al método del ciclo de vida de los cuatro componentes principales.

2.1.5 Análisis del código fuente

A partir de la descripción del proceso y el diagrama de secuencia, podemos ver que el contexto de la aplicación se crea en el método ActivityThread # handleBindApplication () Rastree y vea el código fuente para un análisis detallado.

2.1.5.1 ActividadThread # handleBindApplication()

ActivityThread.class (api 30)
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
    ......
 	@UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
		......
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
        updateLocaleListFromAppContext(appContext,
                mResourcesManager.getConfiguration().getLocales());
        ......
        if (ii != null) {
            ......
            final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false, true, false);
            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
                    appContext.getOpPackageName());
            try {
            	// 获取 ClassLoader 加载类文件
                final ClassLoader cl = instrContext.getClassLoader();
                // 获取 Instrumentation 类并构建实例对象
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            }
			......
            final ComponentName component = new ComponentName(ii.packageName, ii.name);
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
			......
        } 
        ......
        Application app;
		......
        try {
        	// 创建 Application
            app = data.info.makeApplication(data.restrictedBackupMode, null);
			......
            mInitialApplication = app;
			......
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            ......
            try {
           		// 内部调用 Application # onCreate() 的方法
                // 故 Application # onCreate() 比 ActivityThread 的 main() 方法慢执行
                // 但是会比所有该应用 Activity 的生命周期先调用,因为此时的 Activity 还没启动
                mInstrumentation.callApplicationOnCreate(app);
            }
            ......
        }
    }
    ......
}

ActivityThread # El parámetro AppBindData del método handleBindApplication () es la información de inicio pasada por AMS a la aplicación, que incluye LoadedApk, ApplicationInfo, etc., y luego crea objetos de instancia ContextImpl y Aplicación a través del objeto de instancia LoadedApk.

2.1.5.2 LoadedApk # makeApplication()

LoadedApk.java  (api 30)
public final class LoadedApk {
	......
   @UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
       		// 如果 mApplication 已经存在则直接返回
            return mApplication;
        }
		......
        Application app = null;
        // 获取 AndroidMenifest 中 application 标签指定的 Application 类
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
        	// 获取 ClassLoader
            final java.lang.ClassLoader cl = getClassLoader();
            ......
            // 创建 ContextImpl 实例
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            ......
            // 利用类加载器 ClassLoader 创建 AndroidMenifest 指定的 Application 类
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
			// 将 Application 实例赋值给 ContextImpl,以便 ContextImpl 可以访问 Application
            appContext.setOuterContext(app);
        }
        ......
        mActivityThread.mAllApplications.add(app);
        // app 赋值给 mApplication,当我们调用 Context.getApplicationContext() 就是获取这个对象
        mApplication = app;
        if (instrumentation != null) {
            try {
            	// 由于 instrumentation 此时为空所以不会回调 Application 的 onCreate 方法
                instrumentation.callApplicationOnCreate(app);
            }
            ......
        }
		......
        return app;
    }
    ......
}

El proceso de ejecución es el siguiente:

  • Primero determine si mApplication en el objeto LoadedApk existe, si ya existe, devuélvalo directamente. Si no existe, primero obtenga el nombre de la clase de aplicación especificado por el atributo de nombre en la etiqueta de la aplicación en AndroidMenifest. Luego obtenga ClassLoader y cree una instancia de ContextImpl.
  • Cree una instancia de clase de Aplicación a través del cargador de clases ClassLoader y asigne la instancia de Aplicación a la variable miembro mOuterContext de ContextImpl para que ContextImpl pueda acceder a la instancia de Aplicación a través de mOuterContext. Al mismo tiempo, asigne la instancia de la Aplicación a mApplication y llame al método Context # getApplicationContext() para obtener el objeto de la instancia.

2.1.5.3 Creación de ContextImpl

ContextImpl.java (api 30)
class ContextImpl extends Context {
	......
    @UnsupportedAppUsage
    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        return createAppContext(mainThread, packageInfo, null);
    }

    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
            String opPackageName) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
                0, null, opPackageName);
        context.setResources(packageInfo.getResources());
        context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
        return context;
    }
    ......
}

Cree un objeto de instancia ContextImpl y asigne a ContextImpl los objetos de "permiso" relacionados con el acceso a los recursos del sistema: ActivityThread, LoadedApk, etc.

2.1.5.4 Creación de aplicaciones

Volvamos atrás y veamos la creación de la instancia de la clase Aplicación de LoadedApk # makeApplication(). El código es el siguiente:

Instrumentation.java (api 30)
public class Instrumentation {
	......
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
		// 获取 LoadedApk 的 AppComponentFactory,然后通过 ClassLoader 
		// 加载 Application 类,并创建类的实例对象
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
		// 将 ContextImpl 实例绑定到 Application 实例对象的 mBase 成员变量
        app.attach(context);
        return app;
    }

    private AppComponentFactory getFactory(String pkg) {
        ......
        LoadedApk apk = mThread.peekPackageInfo(pkg, true);
        // This is in the case of starting up "android".
        if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
        return apk.getAppFactory();
    }
    ......
}

AppComponentFactory.java (api 30)
public class AppComponentFactory {
	......
    public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		// 通过 ClassLoader 加载 Application 类,并创建类的实例对象
        return (Application) cl.loadClass(className).newInstance();
    }
    ......
}

Obtenga AppComponentFactory de LoadedApk, luego cargue la clase Aplicación a través de ClassLoader y cree un objeto de instancia de la clase. A continuación, asigne la instancia de ContextImpl a la variable miembro mBase del objeto de instancia de Aplicación creado.

2.1.5.5 Enlace de aplicación ContextImpl

Application.java (api 30)
public class Application extends ContextWrapper implements ComponentCallbacks2 {
	......
    @UnsupportedAppUsage
    /* package */ final void attach(Context context) {
    	// 这里 context 为 ContextImpl 实例对象
    	// 调用 ContextWrapper # attachBaseContext() 方法
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
    ......
}

ContextWrapper.java (api 30)
public class ContextWrapper extends Context {
	......
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        // 将 ContextImpl 实例对象赋值给成员变量 mBase
        mBase = base;
    }
    ......
}

En el método adjunto() de la Aplicación, llame al método adjuntoBaseContext() de ContextWrapper y asigne el objeto de instancia ContextImpl a su variable miembro mBase.

2.2 Contexto del servicio

El servicio es uno de los cuatro componentes principales del marco del sistema Android y es un componente de la aplicación que puede realizar operaciones de larga duración en segundo plano sin una interfaz de usuario. Puede ser iniciado por otros componentes de la aplicación (como Actividad). Una vez que se inicia el servicio, siempre se ejecutará en segundo plano. Incluso si el componente (Actividad) que inicia el servicio se destruye, no se verá afectado. A continuación, analizar el origen de su instancia de Contexto.

2.2.1 Proceso de creación del contexto del servicio

El Contexto del Servicio se crea cuando el Servicio es iniciado por otros componentes de la aplicación (como el inicio en Actividad). El proceso de inicio del Servicio no se analizará en detalle aquí, ni es el tema central de este artículo. Para obtener más información, consulte el análisis detallado de este artículo: una explicación detallada del proceso de inicio del servicio Android R (11.0) .

Describamos brevemente el proceso:

  • El método Context # startService () generalmente se llama para iniciar el Servicio. Como se puede ver en el análisis anterior, aquí se llamará al método ContextImpl # startService () de su clase de implementación, y luego al método startService () de AMS en el proceso system_server se llama entre procesos a través de la comunicación Binder. Después de una serie de llamadas en el proceso del sistema, el proceso pasa al método ScheduleCreateService () de ApplicationThread, en el que la información de servicio creada en el proceso AMS se encapsula en un objeto CreateServiceData.
  • ApplicationThread # En el método ScheduleCreateService (), el objeto de instancia CreateServiceData envía un mensaje Handler del tipo CREATE_SERVICE a través de la clase interna H, y luego llama al método ActivityThread # handleCreateService () para crear el Servicio y crear el Contexto del Servicio.

2.2.2 Diagrama de tiempos

2.2.3 Análisis del código fuente

A partir de la descripción del proceso y el diagrama de secuencia, podemos ver que el contexto del servicio se crea en el método ActivityThread # handleCreateService () Rastree y vea el código fuente para un análisis detallado.

2.2.3.1 ActividadThread # handleCreateService()

ActivityThread.class (api 30)
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
    ......
    @UnsupportedAppUsage
    private void handleCreateService(CreateServiceData data) {
		......
		// 获取 LoadedApk 实例对象,用于获取类加载器 ClassLoader 等
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
  			......
  			// 创建 ContextImpl 实例,即 Service 的上下文环境 Context[参见 2.1.5.3 节]
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            // 创建 Application 实例[参见 2.1.5.2 节]
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            // 获取 ClassLoader 实例对象
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            // 加载 Service 类并创建实例对象
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
			......
            context.getResources().addLoaders(
                    app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
			// 将 Service 实例赋值给 ContextImpl,以便 ContextImpl 可以访问 Service
            context.setOuterContext(service);
            // 将 ContextImpl 实例绑定到 Service 实例对象的 mBase 成员变量
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
			// 回调 Service # onCreate() 方法
            service.onCreate();
            // 将 service 对象加入到 mService 集合中,key 值为 data.token
            mServices.put(data.token, service);
            try {
            	// 跨进程调用 AMS # serviceDoneExecuting() 方法通知 AMS,Service 已经启动完毕
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } 
        ......
    }
    ......
}

El proceso de ejecución es el siguiente:

  • Obtenga el objeto de instancia LoadedApk, que se utiliza para obtener el cargador de clases ClassLoader, etc., luego cree un objeto de instancia ContextImpl, que es el entorno contextual del Servicio, y asigne a ContextImpl los objetos de "permiso" relacionados con el acceso a los recursos del sistema. - ActividadThread, LoadedApk, etc.
  • Obtenga el objeto de instancia ClassLoader y úselo para cargar la clase de Servicio y crear un objeto de instancia. Asigne la instancia de Servicio creada a la variable miembro mOuterContext de ContextImpl para que ContextImpl pueda acceder a la instancia de Servicio a través de mOuterContext.
  • Llame al método Service # adjunto() para vincular la instancia de ContextImpl a la variable miembro mBase del objeto de instancia de Servicio, luego vuelva a llamar al método Servicio # onCreate() y finalmente notifique a AMS que el Servicio se ha iniciado llamando a AMS # método serviceDoneExecuting() en todos los procesos.

2.2.3.2 Enlace de servicio ContextImpl

Service.java (api 30)
public abstract class Service extends ContextWrapper implements ComponentCallbacks2,
        ContentCaptureManager.ContentCaptureClient {
	......
    @UnsupportedAppUsage
    public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
		// 继续调用 Service # attachBaseContext() 方法
        attachBaseContext(context);
        ......
        setContentCaptureOptions(application.getContentCaptureOptions());
    }
    
    @Override
    protected void attachBaseContext(Context newBase) {
    	// 调用父类 ContextWrapper # attachBaseContext() 方法
        super.attachBaseContext(newBase);
        if (newBase != null) {
            newBase.setContentCaptureOptions(getContentCaptureOptions());
        }
    }
    ......
}

ContextWrapper.java (api 30)
public class ContextWrapper extends Context {
	......
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        // 将 ContextImpl 实例对象赋值给成员变量 mBase
        mBase = base;
    }
    ......
}

Continúe llamando al método Service # adjuntoBaseContext() en el método adjunto() del Servicio, y luego continúe llamando al método de la clase principal ContextWrapper # adjuntoBaseContext() para asignar el objeto de instancia ContextImpl a la variable miembro mBase.

2.3 Contexto de la actividad

La actividad es el componente más utilizado entre los cuatro componentes principales del marco del sistema Android y es una interfaz que se utiliza para mostrar contenido a los usuarios e interactuar directamente con los usuarios. El Contexto de Actividad se usa a menudo en el desarrollo diario, como iniciar una nueva Actividad, iniciar un Servicio y registrar un receptor de transmisión a través de Contexto.Echemos un vistazo al origen de la instancia de Contexto de Actividad.

2.3.1 Proceso de creación del contexto de actividad

El contexto de actividad se crea cuando se inicia el componente de actividad. Aquí no analizamos el proceso de inicio de actividad en detalle, ni es el enfoque de este artículo. Los estudiantes interesados ​​pueden consultar el análisis detallado de este artículo - En profundidad Explicación del proceso de inicio de actividad de Android R (11.0) .

Describamos brevemente el proceso:

  • Por lo general, se llama al método Actividad # startActivity () para iniciar la Actividad, y luego se llama al método startActivity () de ATMS en el proceso system_server entre procesos a través de la comunicación Binder. Después de una serie de llamadas en el proceso del sistema, el proceso pasa a el método ScheduleTransaction() de ApplicationThread.
  • El método ApplicationThread # ScheduleTransaction () programa la transacción LaunchActivityItem que inicia la actividad de acuerdo con el estado del ciclo de vida. En el método LaunchActivityItem # Execute (), se llama al método ActivityThread # handleLaunchActivity () para crear e iniciar la actividad, y al mismo tiempo tiempo crear el contexto de la actividad.

Android P (9.0) inicia la lógica relacionada con el ciclo de vida y el inicio de la actividad, que se desacopla en múltiples transacciones de transacciones (como: LaunchActivityItem, ResumeActivityItem, etc.) y la ejecución de las transacciones se programa a través de ClientLifecycleManager.

2.3.2 Diagrama de tiempos

2.3.3 Análisis del código fuente

En el resumen del proceso y el diagrama de secuencia, podemos ver que el contexto de la actividad se crea en el método ActivityThread # handleLaunchActivity () Rastree y vea el código fuente para un análisis detallado.

2.3.3.1 ActividadThread # handleLaunchActivity()

ActivityThread.java (api 30)
public final class ActivityThread extends ClientTransactionHandler {
	......
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        ......
        final Activity a = performLaunchActivity(r, customIntent);
        ......
        return a;
    }
    ......
}

Continúe llamando a ActivityThread # performLaunchActivity() para ejecutar el inicio de la Actividad y continuar rastreando el proceso de inicio.

2.3.3.2 ActividadThread # performLaunchActivity()

ActivityThread.java (api 30)
public final class ActivityThread extends ClientTransactionHandler {
	......
     /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
        	// 获取 LoadedApk 实例对象,用于获取类加载器 ClassLoader 等
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
    	......
		// 创建 ContextImpl 实例,即 Activity 的上下文环境 Context
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
        	// 通过类加载器 ClassLoader 加载并新建 Activity 的实例
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ......
        } catch (Exception e) {
            ......
        }
        try {
        	// 创建 Application,注意 r.packageInfo 是前面获取的 LoadedApk 实例对象
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
			......
            if (activity != null) {
                ......
                appContext.getResources().addLoaders(
                        app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
                // 将 Activity 实例赋值给 ContextImpl,以便 ContextImpl 可以访问 Activity
                appContext.setOuterContext(activity);
                // 执行 Activity 的 attach、初始化 Window 等并把 ContextImpl 实例设置给 Activity
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);
                ......
                activity.mCalled = false;
                // 执行 Activity 的 onCreate
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ......
            }
            // 设置生命周期的状态为 ON_CREATE
            r.setState(ON_CREATE);
            synchronized (mResourcesManager) {
            	// 将包含 Activity 信息集的 r 对象,也就是 ActivityClientRecord
            	// 加入到 mActivities 中,r.token 为 key 值
                mActivities.put(r.token, r);
            }
        }
		......
        return activity;
    }
    ......
}

El proceso de ejecución es el siguiente:

  • Obtenga el objeto de instancia LoadedApk, que se utiliza para obtener el cargador de clases ClassLoader, etc., y continúe llamando al método ActivityThread # createBaseContextForActivity(), que llama al método ContextImpl # createActivityContext() para crear el objeto de instancia ContextImpl, que es el contexto entorno Contexto de la Actividad.
  • Llame al método Instrumentación # newActivity () para cargar y crear un nuevo objeto de instancia de Actividad, que llama al método AppComponentFactory # instantiateActivity (), y luego cargue y cree un nuevo objeto de instancia de Actividad a través del cargador de clases ClassLoader obtenido en ActivityThread # performLaunchActivity ( ) método.
  • Cree un objeto Aplicación a través del método LoadApk # makeApplication(). El proceso es similar a cargar y crear una nueva Actividad, utilizando el cargador de clases ClassLoader.
  • Ejecute el método Actividad # adjuntar (), a través del cual la instancia ContextImpl se establece en la Actividad. Además, el método también completa la creación de la instancia de Ventana y establece su asociación con la Ventana, de modo que cuando la Ventana reciba una entrada externa evento, sus Eventos se pueden pasar a Actividad.
  • Ejecute el método Instrumentación # callActivityOnCreate(), que llama al método Actividad # performCreate(), y el método Actividad # performCreate() llama al método Actividad # onCreate().

2.3.3.3 ActividadThread # createBaseContextForActivity()

ActivityThread.java (api 30)
public final class ActivityThread extends ClientTransactionHandler {
	......
	private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        ......
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
		......
        return appContext;
    }
    ......
}

ContextImpl.java (api 30)
class ContextImpl extends Context {
	......
    @UnsupportedAppUsage
    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
		......
		// 通过 LoadedApk 获取类加载器 ClassLoader
        ClassLoader classLoader = packageInfo.getClassLoader();
        ......
        // 创建 Activity 的 ContextImpl 实例对象
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
                activityInfo.splitName, activityToken, null, 0, classLoader, null);
        ......
        return context;
    }
    ......
}

Simplemente mire el código fuente del proceso principal y llame a ContextImpl # createActivityContext() para crear el contexto de la actividad.

En cuanto al proceso de carga y creación de Actividad y Aplicación en el proceso principal, aquellos que estén interesados ​​pueden seguir el código fuente para verlo. El objetivo principal es crear nuevos objetos de instancia después de ser cargados por el cargador de clases ClassLoader. El siguiente es Principalmente para ver el proceso de contexto de vinculación de actividad.

2.3.3.4 Actividad # adjuntar()

ContextImpl.java (api 30)
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2, ...... {
	......
	@UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,...) {
        // 将 ContextImpl 实例绑定到 Activity 实例对象的 mBase 成员变量
        attachBaseContext(context);
		......
		// 新建 PhoneWindow 实例
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ......
        mUiThread = Thread.currentThread();
        ......
        mWindowManager = mWindow.getWindowManager();
		......
    }
	......
    @Override
    protected void attachBaseContext(Context newBase) {
    	// 继续调用父类的 attachBaseContext() 方法
        super.attachBaseContext(newBase);
        if (newBase != null) {
            newBase.setAutofillClient(this);
            newBase.setContentCaptureOptions(getContentCaptureOptions());
        }
    }
    ......
}

ContextThemeWrapper.java (api 30)
public class ContextThemeWrapper extends ContextWrapper {
	......
    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }
    ......
}

ContextWrapper.java (api 30)
public class ContextWrapper extends Context {
	......
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    ......
}

Continúe llamando al método Actividad # adjuntarBaseContext () en el método adjuntar () de la Actividad, y luego continúe llamando al método de la clase principal ContextThemeWrapper # adjuntarBaseContext (). Dado que ContextThemeWrapper hereda de ContextWrapper, continúe llamando al método ContextWrapper # adjuntarBaseContext () para asigne el objeto de instancia ContextImpl a la variable miembro mBase.

2.4 Resumen

A través de un análisis en profundidad del código fuente, se puede ver que la aplicación, el servicio y la actividad heredan directa o indirectamente de la clase ContextWrapper, por lo que todos tienen una variable miembro de tipo Context mBase que apunta a un objeto ContextImpl. ContextImpl es la implementación específica clase de la clase Context, por lo que también Todos tienen la función de interfaz proporcionada por Context para obtener información global sobre el entorno de la aplicación.

3. Conocimientos complementarios del contexto.

Los capítulos anteriores analizaron la creación de componentes como Aplicación, Servicio y Actividad y el proceso de vinculación de objetos de instancia de ContextImpl. Algunos estudiantes pueden preguntar: ¿los objetos de instancia de ContextImpl no están vinculados durante el proceso de creación de BroadcastReceiver y ContentProvider entre los cuatro componentes principales?

De hecho, también están vinculados, pero no heredan de Context. La instancia de Context debe proporcionarse a través de los tres mencionados anteriormente. Echemos un vistazo general al código fuente.

3.1 BroadcastReceiver obtiene una instancia de contexto

Durante el desarrollo, los receptores de transmisión generalmente se registran llamando al método Contexto # RegisterReceiver (). El contexto aquí puede ser proporcionado por cualquiera de los tres mencionados anteriormente de acuerdo con el escenario al registrar el receptor de transmisión. Aquí, el registro en el escenario de actividad es Por ejemplo, llame al método Context # RegisterReceiver () para registrarse. Como se puede ver en el análisis anterior, se llamará al método ContextImpl # RegisterReceiver () de la clase de implementación Context. El código es el siguiente:

ContextImpl.java (api 30)
class ContextImpl extends Context {
	......
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        return registerReceiver(receiver, filter, null, null);
    }
    
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), 0);
    }
    ......
    @UnsupportedAppUsage
    final Context getOuterContext() {
        return mOuterContext;
    }
    ......
}

Al registrar un receptor de transmisión, continúe llamando al método ContextImpl # RegisterReceiverInternal() y pase el contexto actual, es decir, Context. Aquí, la instancia de Context se obtiene a través de ContextImpl # getOuterContext(). ¿Le resulta familiar este método? En secciones 2.1.5.2, 2.2.3.1 y 2.3.3.2, el valor se asigna a través del método ContextImpl # setOuterContext(), que también verifica el análisis anterior. La instancia de Context se obtiene en función del escenario al registrar el receptor de transmisión. Decida cuál de se quieren obtener los tres descritos anteriormente.

3.2 ContentProvider obtiene una instancia de contexto

ContentProvider es el menos utilizado de los cuatro componentes. Generalmente se usa para compartir datos entre procesos. Lo crea el sistema cuando se inicia la aplicación, pero no pertenece a la arquitectura Context en sí, por lo que se usa al crear una instancia de ContentProvider. La instancia de Context debe obtenerse en otro lugar. En este caso, primero echemos un vistazo a dónde se crea la instancia de ContentProvider durante el proceso de inicio de la aplicación.

3.2.1 Diagrama de tiempos

El proceso de inicio de la aplicación no se explicará en detalle aquí. Puede consultar el análisis detallado de este artículo: una explicación detallada del proceso de inicio de la actividad de Android R (11.0). Después de que la aplicación crea y vincula la aplicación, crea y vincula una instancia de contexto a través del método ActivityThread # installContentProviders (), exploremos el código fuente para verificarlo.

3.2.2 Análisis del código fuente

3.2.2.1 ActividadThread # handleBindApplication()


ActivityThread.class (api 30)
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
    ......
 	@UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
        ......
        Application app;
		......
        try {
        	// 创建 Application
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            ......
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                	// 创建 ContentProvider 实例,注意入参是 Application 实例
                    installContentProviders(app, data.providers);
                }
            }
            ......
            try {
           		// 内部调用 Application # onCreate() 的方法
                mInstrumentation.callApplicationOnCreate(app);
            }
            ......
        }
    }
    
    @UnsupportedAppUsage
    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            ......
            // 继续调用 installProvider() 方法创建 ContentProvider 实例
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }
        try {
        	// 将 ContentProvider 列表发布到 AMS 中目的是进行缓存
        	// 其它应用进程想要获取它的 ContentProvider 的时候可以直接在缓存中遍历获取
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

	@UnsupportedAppUsage
    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        if (holder == null || holder.provider == null) {
            ......
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            if (context.getPackageName().equals(ai.packageName)) {
            	// 包名相同即同一应用内,则使用入参 Application 作为 Context
                c = context;
            }
			......// 根据不同使用场景,获取对应场景下的 Context 实例
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                ......
                // 内部通过 ClassLoader 加载并新建 ContentProvider 实例对象
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                provider = localProvider.getIContentProvider();
                ......
                // XXX Need to create the correct context for this provider.
                // 为 ContentProvider 设置合适的上下文环境 - Context
                localProvider.attachInfo(c, info);
            }
            ......
        } 
        ......
    }
    ......
}

En el método ActivityThread # handleBindApplication(), llame al método ActivityThread # installContentProviders() y pase el objeto de instancia de Aplicación creado. Continúe llamando al método ActivityThread # installProvider() para crear el objeto de instancia ContentProvider. El proceso de creación es similar al uno analizado anteriormente A través de la clase El cargador ClassLoader carga y crea un nuevo objeto de instancia de ContentProvider y finalmente llama al método ContentProvider # adjuntarInfo () para establecer el entorno de contexto apropiado: contexto para ContentProvider.

3.2.2.2 Proveedor de contenido # adjuntarInfo()

ContentProvider.class (api 30)
public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 {
    ......
    public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;
        mCallingPackage = new ThreadLocal<>();
        // 这里只允许设置一次,因此 ContentProvider 创建交付使用后,客户端不能再更改它
        if (mContext == null) {
            mContext = context;
            ......
            // 回调 ContentProvider # onCreate() 方法
            ContentProvider.this.onCreate();
        }
    }
    ......
}

El método ContentProvider # adjuntoInfo() asigna el objeto de instancia de Contexto a la variable miembro mContext de ContentProvider, de modo que ContentProvider puede usar la función de interfaz proporcionada por Contexto para obtener información global sobre el entorno de la aplicación, y este Contexto también es el ActivityThread inicial # handleBindApplication( ) método El objeto de instancia de aplicación pasado (nota: la variable miembro mContext solo puede configurarse una vez).

Nota: En el método ContentProvider # installProvider(), el objeto de instancia Context en el escenario correspondiente se obtendrá según diferentes escenarios de uso, lo que estamos analizando aquí es dentro de la misma aplicación, por lo que a la variable miembro mContext de ContentProvider se le asigna el valor del objeto de instancia de aplicación pasado. Si el nombre del paquete de otras aplicaciones se especifica entre procesos o mediante el Intent # setPackage(), debe obtener el objeto de instancia de Contexto en el escenario correspondiente.

3.3 Resumen

Esta sección complementa cómo BroadcastReceiver y ContentProvider entre los cuatro componentes principales obtienen objetos de instancia de Context. Aunque son componentes del sistema, no son miembros de la arquitectura de Context. Sin embargo, como componentes del sistema, también necesitan usar Context. Proporciona una función de interfaz. Para obtener información global del entorno de la aplicación, por lo tanto, con una actitud de aprendizaje profundo, leí atentamente el proceso del código fuente y clasifiqué el proceso de creación y obtención de objetos de instancia de Contexto.

4. Resumen

Combinado con la explicación y el análisis del código fuente de este artículo, echemos un vistazo a las preguntas formuladas en la entrevista para profundizar nuestra comprensión.

Pregunta 1: ¿Cuántos contextos hay en una aplicación en el sistema Android?

De la explicación detallada de este artículo, podemos ver que en la arquitectura Contexto, cuando Aplicación, Actividad y Servicio crean objetos de instancia, crearán un objeto de instancia ContextImpl y lo asignarán a la variable miembro mBase de su clase principal ContextWrapper. El objeto de subclase tiene todas las propiedades y métodos de la clase principal en el objeto, por lo que el objeto de instancia ContextImpl se puede obtener a través de la variable miembro mBase en los objetos de instancia de Aplicación, Actividad y Servicio. Es decir, cada Actividad y Servicio tiene un Contexto, y dado que la Aplicación en cada aplicación es única, la cantidad de Contextos en una aplicación en el sistema Android = la cantidad de Actividades + la cantidad de Servicios + 1.

Pregunta 2: ¿El contexto provocará pérdidas de memoria?

Generalmente, las pérdidas de memoria causadas por el Contexto casi siempre ocurren cuando el Contexto se destruye, pero la destrucción del GC falla porque se hace referencia. Se puede entender que el objeto Contexto de la Aplicación existe con el proceso de la aplicación, por lo que aquí hay un resumen de algunos problemas. al utilizar la sugerencia Context.:

  • Cuando se pueda manejar el Contexto de la Aplicación, y para objetos con un ciclo de vida largo, se utilizará primero el Contexto de la Aplicación.
  • No permita que los objetos con un ciclo de vida más largo que la Actividad contengan una referencia a la Actividad.
  • Intente no utilizar clases internas no estáticas en Actividad, porque las clases internas no estáticas contendrán implícitamente referencias a instancias de clases externas. Se recomienda utilizar clases internas estáticas y mantener referencias de instancias externas como referencias débiles.

Pregunta 3: ¿Cuáles son las diferencias entre getContext(), getBaseContxet(), getApplication() y getApplicationContext()?

Como se analiza en el artículo, los dos métodos getApplication() y getApplicationContext() obtienen el mismo objeto de instancia, pero el alcance del escenario de uso es diferente. El método getApplication() es más intuitivo, pero solo se puede llamar en escenarios de Actividad y Servicio. El método getApplicationContext() tiene un alcance de aplicación más amplio y se puede llamar a través del objeto Context en cualquier escenario. Entonces ¿por qué es el mismo objeto? Eche un vistazo rápido al código fuente:

Activity.class (api 30)
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,... {
	......
	@UnsupportedAppUsage
    private Application mApplication;
    
    public final Application getApplication() {
        return mApplication;
    }
    @UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread,...
            Application application, ...) {
        ......
        mApplication = application;
		......
    }
}

Service.class (api 30)
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
	......
	@UnsupportedAppUsage
    private Application mApplication;
    
    public final Application getApplication() {
        return mApplication;
    }
    @UnsupportedAppUsage
    public final void attach(
            Context context,...
            Application application, Object activityManager) {
        ......
        mApplication = application;
        ......
    }
}

En primer lugar, vemos que el método getApplication() devuelve el objeto de instancia de Aplicación pasado cuando la Actividad y el Servicio llaman al método adjuntar(). Aún recuerde el análisis anterior en el artículo, para la invocación del método Actividad # adjuntar(), consulte 2.3.3.2 ActivityThread # performLaunchActivity(), y para la invocación del método Servicio # adjuntar(), consulte 2.2.3.1 ActividadThread # handleCreateService().Entre estos dos, los objetos de instancia de aplicación pasados ​​al método adjunto() en cada método se crean y obtienen a través del método LoadedApk # makeApplication().

Echemos un vistazo al valor de retorno del método getApplicationContext(). Aunque se llama a ContextWrapper, en última instancia se delega a la clase de implementación ContextImpl. El código fuente es el siguiente:

ContextImpl.java (api 30)
class ContextImpl extends Context {
	......
	@UnsupportedAppUsage
    final @NonNull ActivityThread mMainThread;
    @UnsupportedAppUsage
    final @NonNull LoadedApk mPackageInfo;
    
    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    ......
}

Aquí, dependiendo de si mPackageInfo está vacío, se llaman respectivamente el método mPackageInfo # getApplication() y el método mMainThread # getApplication(). Echemos un vistazo a estos dos métodos. Primero, observe el valor de retorno del método getApplication() en LoadedApk. El código es el siguiente:


LoadedApk.java (api 30)
public final class LoadedApk {
	......
	@UnsupportedAppUsage
    private Application mApplication;

    Application getApplication() {
        return mApplication;
    }

	@UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }
		......
        Application app = null;
		......
        try {
        	......
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        }
        ......
        mApplication = app;
		......
        return app;
    }
    ......
}

Primero, determine si mApplication en LoadedApk está vacío (se garantiza que es un singleton del objeto). Si no, regrese directamente. Si está vacío, cree un nuevo objeto de instancia de Aplicación y asígnelo a mApplication. Luego eche un vistazo al valor de retorno del método getApplication () en ActivityThread, el código es el siguiente:

ActivityThread.java (api 30)
public final class ActivityThread extends ClientTransactionHandler {
	......
    @UnsupportedAppUsage
    Application mInitialApplication;
    
    @UnsupportedAppUsage
    public Application getApplication() {
        return mInitialApplication;
    }
    ......
    @UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
		......
        Application app;
        try {
        	// data.info 是 LoadedApk 类
            app = data.info.makeApplication(data.restrictedBackupMode, null);
			......
            mInitialApplication = app;
			......
        }
	......
}

La mInitialApplication devuelta por el método ActivityThread # getApplication() también es devuelta por el método LoadedApk # makeApplication(), por lo que se puede concluir que el método getApplicationContext() devuelve el mismo objeto de instancia de Aplicación en los dos casos anteriores.

Dado que el objeto de instancia de Aplicación devuelto por el método getApplication() también se crea y se obtiene mediante el método LoadedApk # makeApplication(), los dos métodos getApplication() y getApplicationContext() devuelven el mismo objeto de instancia de Aplicación.

¿Cuál es la diferencia entre los métodos getContext(), getBaseContxet() y getApplicationContext()?

Primero, el método getBaseContxet() obtiene el objeto de instancia de la clase de implementación asignada al contexto de mBase analizado anteriormente. LoadedApk devuelto por el método getApplicationContext() #Objeto de instancia de aplicación creado por el método makeApplication(). Y Aplicación, Actividad y Servicio tienen dos métodos: getBaseContxet() y getApplicationContext(). El método getContext() se utiliza en Fragment o View para obtener su objeto host.

Cinco, referencia

  1. Análisis completo del mecanismo Contexto.

Supongo que te gusta

Origin blog.csdn.net/qq_32907491/article/details/132789346
Recomendado
Clasificación