Análisis del código fuente de Android 12: capa de aplicación 2 (organización general y proceso de inicio de SystemUI)

Análisis del código fuente de Android 12: capa de aplicación 2 (organización general y proceso de inicio de SystemUI)

En el artículo anterior, presentamos cómo SystemUI usa IDE para editar y depurar. Esta es la base más básica para analizar SystemUI, espero que los lectores puedan dominarla tanto como sea posible.

Este artículo presentará la estructura organizativa general de SystemUI y su proceso de inicio. Después de leer este artículo, sabrás:

  1. ¿Por qué SystemUI elige usar Dagger2?
  2. Cómo crear un nuevo módulo en SystemUI
  3. Proceso de inicio de SystemUI

Antes de leer, piense conmigo en las siguientes preguntas:

  1. ¿Qué funciones completa SystemUI?
  2. ¿Existe la necesidad de comunicación entre funciones?
  3. Si es necesario que haya comunicación entre funciones, ¿cómo organizar las relaciones de referencia entre ellas?
  4. ¿Las funciones individuales necesitan comunicarse con los servicios del sistema?
  5. Si cada función necesita comunicarse con los servicios del sistema, ¿cómo organizar eficientemente las referencias entre ellas?
  6. ¿Existen dependencias entre funciones?
  7. ¿Existe algún requisito de secuencia para el inicio entre funciones?

Para los problemas 1, 2 y 3 anteriores, podemos ver en la interfaz que SystemUI incluye: pantalla de bloqueo, barra de estado, barra de navegación, pantalla Toast, ajuste de volumen y otras funciones. Estas funciones pueden hacer referencia entre sí, por ejemplo, el módulo de la barra de estado necesita conocer la situación del módulo de la pantalla de bloqueo para decidir si ocultar algunos íconos de la barra de estado.

Estas funciones interactuarán más o menos con el sistema, por ejemplo, el módulo de pantalla de bloqueo necesita conocer el estado del botón de encendido, para decidir si mostrar u ocultar la pantalla de bloqueo; otro ejemplo, si cada módulo de función debe mostrar modo nocturno, etc.

De aquí ya sabemos que cada módulo necesita referirse entre sí y comunicarse con el sistema, para ello es necesario diseñar una buena arquitectura para que cada módulo pueda obtener fácilmente los objetos deseados, y la creación de estos objetos pueda también require Depende de la creación de otros objetos. De hecho, las dependencias entre los distintos objetos de SystemUI son relativamente complejas: si crea cada objeto manualmente, necesitará escribir mucho código. Para hacer esto, utilizamos un nuevo método de gestión de componentes: DI (inyección de dependencia)

La idea central de la inyección de dependencia es: al escribir código, ya no depende del desarrollador editar explícitamente cómo se debe crear cada objeto, sino que la biblioteca de inyección de dependencia decide cómo crear el objeto. SystemUI eligió Dagger2 como su biblioteca de inyección de dependencia.

Nota: ¿Qué es necesario explicar aquí la dependencia? Dependencia significa que el componente A necesita utilizar otro componente B en el proceso de completar su función, que se denomina A dependiendo de B. A y B pueden ser clases, objetos, módulos, etc. Entonces, ¿qué es la inyección? La inyección es el proceso de proporcionar dependencias a los componentes.

Ahora hemos diseñado cómo obtener los objetos deseados entre cada módulo: inyección de dependencia de Dagger.

Ahora piense en cómo comunicarse entre SystemUI y el sistema. Esto implica cómo dividir SystemUI. Como componente básico de UI de todo el sistema, SystemUI ocupa una posición muy importante en todo el sistema Android. Para adaptarse a escenarios más diversos, como Android TV y Android Car, pueden tener diferentes SystemUI y, al mismo tiempo, cumplir con el diseño modular. Por tanto, SystemUI está diseñado como una apk que reside en la memoria.

Ahora que SystemUI ha sido diseñado como un apk, se ejecuta por separado en otro proceso. Para comunicarse con el sistema, solo puede interactuar a través de los cuatro componentes principales de Android y usar Binder de Android para interactuar. Luego, SystemUI se convierte en una colección de varios Servicios, Actividad, BroadcastReceiver y ContentProvider. Luego habilite estos componentes cuando sea apropiado.

Según el pensamiento anterior, cada módulo funcional, como la pantalla de bloqueo, la barra de estado y la barra de navegación, se puede mostrar en el lugar correcto siempre que se coloque en el componente apropiado. Al mismo tiempo, para facilitar el acceso a otros módulos, también utilizamos Dagger como ayuda.

Todo parece bien, pero ¿es realmente bueno? ¿Se ha perdido una pregunta importante? ¿Existe una secuencia entre estos módulos? Para ingresar rápidamente al sistema, SystemUI actualmente no exige este requisito. Si un determinado módulo necesita esperar a que otro módulo esté listo antes de que pueda funcionar normalmente, entonces es necesario ajustar la lógica de diseño.

Consulte los componentes de SystemUI

Por lo anterior sabemos que SystemUI se divide en cuatro componentes principales en su conjunto, así que revisemos AndroidManifest.xml para ver qué componentes están definidos.

En la rama android-12.0.0_r34, AndroidManifest.xml tiene 38 actividades, 11 servicios, 4 proveedores y 11 receptores.

A continuación, daré una breve descripción de cada componente y, en capítulos posteriores, presentaremos en detalle cómo se inician estos componentes y qué funciones realizan.

Actividades

  1. LongScreenShotActivity: la vista utilizada para capturas de pantalla largas. Esta actividad se activa cuando el usuario decide tomar una captura de pantalla larga.
  2. ScreenRecordDialog: la vista del cuadro de opciones que aparece al grabar la pantalla.
  3. TunerActivity: esta es una interfaz de ajuste para desarrolladores. Puede utilizar el siguiente comando para abrir la entrada de la interfaz
adb shell pm enable com.android.systemui/com.android.systemui.tuner.TunerActivity

Luego ingrese a la interfaz en Configuración->Sistema->Sintonizador de UI del sistema

  1. DemoMode: los desarrolladores también utilizan el modo Demo de SystemUI. Es un complemento funcional de TunerActivity y se puede activar en las opciones de desarrollador.
  2. ForceReSizableInfoActivity: las aplicaciones emergentes no se pueden ejecutar en modo de pantalla dividida o en pantallas secundarias
  3. UsbPermissionActivity: cuadro emergente de confirmación de permiso USB
  4. UsbResolverActivity: seleccione un cuadro emergente de aplicación para el dispositivo USB
  5. UsbConfirmActivity: muestra una vista emergente para determinar si se debe usar una aplicación, que es la vista posterior de UsbResolverActivity
  6. SensorUseStartedActivity: cuando el sensor está en modo de privacidad, aparece el cuadro emergente cuando desea utilizar el sensor
  7. TvUnblockSensorActivity: Igual que SensorUseStartedActivity, excepto que esta es la vista utilizada en el televisor.
  8. UsbAccessoryUriActivity: Aparece un cuadro que le permite descargar la aplicación correspondiente a este dispositivo USB.
  9. UsbContaminantActivity: Aparece un cuadro indicando que el USB ha sido desactivado, el motivo de la desactivación puede ser que haya artículos robados en el puerto USB, etc.
  10. UsbDebuggingActivity: ventana emergente para permitir la depuración USB
  11. UsbDebuggingActivityAlias: este es el alias de UsbDebuggingActivity
  12. WifiDebuggingActivity: aparece si se permite la depuración inalámbrica en la red
  13. WifiDebuggingActivityAlias: es el alias de WifiDebuggingActivity
  14. WifiDebuggingSecondaryUserActivity: aparece. El usuario que ha iniciado sesión actualmente no puede habilitar la depuración inalámbrica y debe cambiarse al usuario principal.
  15. NetworkOverLimitActivity: el tráfico de datos emergentes ha alcanzado el límite superior
  16. MediaProjectionPermissionActivity: confirmación de permiso de proyección multimedia
  17. TvNotificationPanelActivity: dedicado a TV, aparece un cuadro de mensaje
  18. SlicePermissionActivity: cuadro de viñetas de permiso de corte
  19. PostreCase:Uno de los huevos de pascua
  20. MlandActivity: juego de huevos de pascua
  21. PeopleSpaceActivity: solicita la ubicación de la interfaz de usuario de Pepole Space, nuevas funciones en Android 11
  22. LaunchConversationActivity: cuando se hace clic en la sesión, expande la vista, nuevas funciones en Android 11
  23. WorkLockActivity: desbloquea la interfaz del perfil de trabajo
  24. CreateUserActivity: Crear vista de usuario
  25. Sonámbulo: Salvapantallas
  26. BrightnessDialog: cuadro emergente de brillo
  27. ForegroundServicesDialog: un cuadro emergente que muestra servicios en primer plano
  28. ChooserActivity: muestra un cuadro para permitir al usuario elegir qué aplicación abrir para manejar la intención actual.
  29. Aparece ControlsProviderSelectorActivity "Seleccione la aplicación para agregar un controlador"
  30. ControlsEditingActivity: edite el controlador, arrastre y suelte para editar
  31. ControlsFavoritingActivity:Controlador, preferencias
  32. ControlsActivity: enumera los controladores de dispositivos
  33. WalletActivity: billetera electrónica
  34. ControlsRequestDialog: solicitud de control para agregar un cuadro emergente de controlador de dispositivo

Nota: Los controles aquí son controladores de dispositivos externos, como los controladores de inteligencia para toda la casa.

Lo anterior es solo una descripción general muy simple, y en artículos posteriores aparecerán algunos detalles de la interfaz de usuario y la lógica de los componentes comunes.

Al ver esto, algunos lectores pueden preguntar: Las actividades anteriores no parecen tener una barra de estado ni una pantalla de bloqueo. ¿No están sus puntos de vista en estas actividades?

Para explorar este tema, primero debemos observar los componentes restantes.

Servicios

  1. SystemUIService: Vaya, qué nombre tan refrescante. Este servicio contiene la mayoría de las funciones dentro de SystemUI. También es la máxima prioridad de nuestro análisis del código fuente de SystemUI.
  2. SystemUISecondaryUserService: en el caso de varios usuarios, este servicio garantiza que las funciones SystemUI de varios usuarios sean normales.
  3. SystemUIAuxiliaryDumpService: para desarrollo y uso, vuelque la información de cada componente necesario y visualícela
  4. TakeScreenshotService: servicios relacionados con captura de pantalla
  5. RecordingService: servicios relacionados con la grabación de pantalla
  6. ImageWallpaper: servicio relacionado con fondos de pantalla
  7. PeopleBackupFollowUpJob: Servicios relacionados con la interfaz de usuario de servicio de personas
  8. DessertCaseDream:Pequeño huevo de Pascua
  9. KeyguardService: servicios relacionados con la pantalla de bloqueo
  10. AuxiliaryPersistenceWrapper$DeletionJobService: Servicios relacionados con controladores de dispositivos externos
  11. DozeService: servicios relacionados con Doze

ContentProvider

  1. FileProvider: proporciona archivos
  2. KeyguardSliceProvider: proporciona Slice de pantalla de bloqueo
  3. ClockOptionsProvider: proporciona una vista previa del reloj para el programa selector
  4. PeopleProvider: devuelve una vista previa del mosaico de personas para el acceso directo dado

BroadcastReceiver

  1. ScreenshotServiceErrorReceiver: captura de pantalla fallida en el receptor de transmisión
  2. SysuiRestartReceiver: reinicia el receptor de transmisión SystemUI
  3. ActionProxyReceiver: un receptor de transmisión que intercepta intentos de compartir y editar para facilitar el procesamiento de algunas cosas por adelantado.
  4. DeleteScreenshotReceiver: elimina el receptor de transmisión de captura de pantalla
  5. SmartActionsReceiver: después de que el usuario hace clic en la acción inteligente en la notificación, se utiliza para recibir la transmisión correspondiente y ejecutar la acción inteligente.
  6. ControlsRequestReciver: receptor de transmisión que recibe solicitudes para agregar controladores
  7. TunerService$ClearReciver: receptor de transmisión clara utilizado para llamar a TunerService
  8. KeyboardShortcutsReceiver: receptor de transmisión que muestra u oculta atajos de teclado
  9. MediaOutputDialogReceiver: receptor de transmisión que recibe la intención de salida de medios
  10. PeopleSpaceWidgetPinnedReceiver: este receptor se llama cuando se agrega un widget de mosaico de contacto
  11. PeopleSpaceWidgetProvider: Implementación del widget People Space

Después de leer hasta este punto, los lectores todavía tienen preguntas: ¿dónde se muestran la pantalla de bloqueo y la barra de estado de SystemUI? ¿Se puede mostrar la interfaz de usuario en el servicio? Obviamente no es razonable. Entonces, ¿cómo se muestran la pantalla de bloqueo y la barra de estado de Android?

Consejo: Además de los componentes enumerados anteriormente para mostrar vistas, SystemUI también muestra vistas interactuando directamente con WindowManager. Qué ~~ ¿Suspiras? La arquitectura de diseño de Android es realmente un poco confusa.

No se muestra aquí, se explicará en detalle más adelante. A continuación, debemos abordar el Dagger2 antes mencionado y cómo maneja las relaciones de referencia de varios componentes en SystemUI.

Diseño de componentes internos de SystemUI

Queremos que Dagger2 administre las dependencias de cada componente, por lo que debemos decirle a Dagger2 qué dependencias existen y qué método debemos usar. ¿Usar archivo xml para describir? ¿O utilizar otros métodos?

Dagger2 usa anotaciones de Java para describir las dependencias entre ellos. Al mismo tiempo, para mejorar el rendimiento, Dagger2 generará diferentes objetos Java de acuerdo con las anotaciones durante la compilación y luego organizará todas las dependencias y ciclos de vida en los objetos Java generados.

Las anotaciones utilizadas para representar varias dependencias se denominan dibujar un gráfico para Dagger2. A continuación, combinamos los ejemplos en SystemUI para ver cómo SystemUI dibuja un gráfico para Dagger2.

Aplicación de Dagger2 en SystemUI

En nuestra idea, necesitamos un objeto superior como RootManager, luego podemos obtener cada objeto que necesitamos en función de este RootManager.

En SystemUI, todavía existe un RootManager de este tipo. Es: GlobalRootComponent. Para cada módulo de SystemUI, si desea obtener el objeto que desea, puede obtenerlo a través de GlobalRootComponent.

Nota: Los lectores, al leer esto, definitivamente estarán muy confundidos en cuanto a por qué se llama Componente en lugar de Administrador. Después de todo, Administrador es tan común en Android. Esto se debe a que SystemUI usa la abstracción de Dagger2. En Dagger2, Component representa un componente, de hecho, es un contenedor que contiene todas las dependencias que puede proporcionar. Por lo tanto, GlobalRootComponent es un componente que se puede proporcionar a todas las dependencias.

Entonces, echemos un vistazo a cómo hacer un dibujo para GlobalRootComponent.

//@Singeton:告诉Dagger2所有带有@singtone注解的对象,生命周期一致。此处表示全局唯一
//@Component(xxx):告诉Dagger2,定义了一个Component组件
//modules={xxx}:告诉Dagger2,这个组件依赖这些模块。关于模块的概念,见后文
@Singleton
@Component(modules = {
    
    
        GlobalModule.class,
        SysUISubcomponentModule.class,
        WMModule.class})
//此处是interface接口定义,Dagger2会生成对应的实现类,并按照我们给Dagger2的注解图,管理好
//各个对象的依赖和创建
public interface GlobalRootComponent {
    
    

    //@Component.Builder:
    //告诉Dagger2,这个是创建GlobalRootComponent的Builder类
    //请务必要思考:为什么此处的对象创建要用Builder模式,而不是工厂模式?
    @Component.Builder
    interface Builder {
    
    
        @BindsInstance
        Builder context(Context context);

        GlobalRootComponent build();
    }

    //提供一个方法,这个方法的返回类型,就是这个组件可以提供的依赖,此处表示可以提供
    //WMComponent.Builder对象
    WMComponent.Builder getWMComponentBuilder();

    //表示此组件可以提供,SysUIComponent.Builder对象
    SysUIComponent.Builder getSysUIComponent();

    //注:上面两个方法提供的返回类型可以看到,他们依然是一个Component

    //表示可以提供ThreadFactory对象
    ThreadFactory createThreadFactory();
}

En el ejemplo anterior, mencionamos el concepto de módulo. Antes de presentar este concepto, pensemos en una pregunta: ¿Qué pasa si hay muchas dependencias en GlobalRootComponent? Si cada uno se dibuja en el diagrama, se verá desordenado, por lo que dagger2 proporciona una función para dividir lógicamente estas diferentes dependencias usando módulos. Entonces sólo necesita especificar lo siguiente al dibujar el Componente:

@Component(modules={
    
    xxx.class})

Seleccionamos SysUISubComponentModule para ver, el código fuente es el siguiente:

//@Module:告诉Dagger2,这个module内的所有依赖,逻辑划分为:SysUISubcomponentModule
//subcomponents = {SysUIComponent.class}:告诉Dagger2,这个模块含有SysUIComponent子组件
//关于子组件的概念,我们下文介绍
@Module(subcomponents = {
    
    SysUIComponent.class})
public abstract class SysUISubcomponentModule {
    
    
}

En el ejemplo anterior mencionamos el subcomponente. Antes de introducir este concepto, pensemos en otra pregunta: si un componente proporciona un objeto a otros usuarios, ¿el objeto proporcionado debe crearse cada vez o sólo una vez? ¿Paño de lana? Esto involucra el ciclo de vida de los objetos. Para administrar mejor el ciclo de vida, recomendamos colocar los objetos que pertenecen al mismo ciclo de vida en un subcomponente. Por lo tanto, todos los objetos del subcomponente SysUIComponent anterior pertenecerán al mismo ciclo de vida. Por supuesto, los subcomponentes no solo pueden aislar el ciclo de vida, sino también aislar el módulo para que el código sea más claro.

Entonces, veamos cómo se informa este subcomponente a Dagger2.

//@SysUISingleton:告诉Dagger2所有SysUISingleton注解的对象生命周期相同
//@Subcomponent:告诉Dagger2,这是一个子组件
//modules={xxx}:告诉dagger2,这个子组件有这么些module的需要依赖
@SysUISingleton
@Subcomponent(modules = {
    
    
        DefaultComponentBinder.class,
        DependencyProvider.class,
        SystemUIBinder.class,
        SystemUIModule.class,
        SystemUIDefaultModule.class})
public interface SysUIComponent {
    
    

    //告诉Dagger2生命周期
    @SysUISingleton
    //告诉Dagger2这个子组件的Builder接口定义
    @Subcomponent.Builder
    interface Builder {
    
    
        //省略若干相同部分

        //@BindsInstance:告诉Dagger2,将t绑定到这个Builder对象中
        //在Dagger2根据我们画的图,会根据这个Builder接口,生成一个SysUIComponentBuilder对象
        //在这个对象中,会有一个成员,类型为Optional<TaskSurfaceHelper>名字为setTaskSurfaceHelper.
        //然后这个setTaskSurfaceHelper()接口函数的实现,就会将参数传入的t保存在setTaskSurfaceHelper成员中。
        //这个过程就叫做:绑定实例,也即@BindsInstance的语义
        @BindsInstance
        Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t);

        //任何一个Builder接口,都必须有一个build()函数,且返回类型为需要构建的对象类型,此处即为SysUIComponent
        SysUIComponent build();
    }

    //定义了一个默认方法,这个方法什么也没有做
    default void init() {
    
    
        // Do nothing
    }

    //告诉Dagger2它的生命周期
    @SysUISingleton
    //subcomponent和component一样,如果想要对外提供依赖,就可以定义任何一个函数,函数的返回类型就是
    //被提供对象的类型。
    BootCompleteCacheImpl provideBootCacheImpl();


    //省略若干相同部分

    //当返回类型为空,而传入类型不为空的时候,表示需要向传入类型对象(SystemUIAppComponentFactory)
    //中被@inject标记的成员赋值,叫做注入
    //理论上,函数名为任意值,但是此种函数,几乎只会完成注入的功能,因此此函数最后都叫做inject
    void inject(SystemUIAppComponentFactory factory);

    //省略若干相同部分
}

En la función de inyección anterior, podemos ver cómo se ve SystemUIAppComponentFactory. El código fuente es el siguiente:

public class SystemUIAppComponentFactory extends AppComponentFactory {
    
    

    //@Inject:告诉Dagger2,这个成员,需要Dagger2的注入。
    //可是Dagger2又是如何知道,该怎么创建ContextComponentHelper的呢?
    //这就是我们给Dagger2画图的作用,我们已经提前画好图给Dagger2,告诉它应该
    //怎么创建这个ContextComponentHelper
    @Inject
    public ContextComponentHelper mComponentHelper;

}

A continuación, echemos un vistazo a cómo dibujamos imágenes para ContextComponentHelper. El código fuente es el siguiente:

public interface ContextComponentHelper {
    
    
    //省略若干无用部分
}

Del código fuente anterior, podemos saber que no tiene ninguna anotación, es decir, no está dibujado en el diagrama de Dagger2. Hay otras clases dibujadas en el diagrama de Dagger2. La clase de implementación de ContextComponentHelper es: ContextComponentResolver. El código es el siguiente:

//@SysUISingleton:告诉Dagger2它的生命周期
@SysUISingleton
public class ContextComponentResolver implements ContextComponentHelper {
    
    
    private final Map<Class<?>, Provider<Activity>> mActivityCreators;
    private final Map<Class<?>, Provider<Service>> mServiceCreators;
    private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
    private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
    private final Map<Class<?>, Provider<BroadcastReceiver>> mBroadcastReceiverCreators;

    //@Inject:此处就是告诉Dagger图,注入Dagger2的各种辅助功能,帮助创建这个对象
    //在创建对象的时候,需要它的各种参数,而这些参数又应该怎么被Dagger2提供呢?
    //只要我们把需要的参数,画好图给Dagger2即可,过程就和这个ContextComponentResolver一样啦,
    //在构造器上面标注一下@Inject就可以了
    @Inject
    ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
            Map<Class<?>, Provider<Service>> serviceCreators,
            Map<Class<?>, Provider<SystemUI>> systemUICreators,
            Map<Class<?>, Provider<RecentsImplementation>> recentsCreators,
            Map<Class<?>, Provider<BroadcastReceiver>> broadcastReceiverCreators) {
    
    
        mActivityCreators = activityCreators;
        mServiceCreators = serviceCreators;
        mSystemUICreators = systemUICreators;
        mRecentsCreators = recentsCreators;
        mBroadcastReceiverCreators = broadcastReceiverCreators;
    }

    //省略若干无用部分
}

Al ver esto, ya tenemos una idea general de cómo usar Dagger2 (cómo proporcionarle una imagen). Pero si lo piensas detenidamente, todavía hay un problema: si algunas clases no las creamos nosotros, entonces no podemos agregar @Inject al constructor, entonces, ¿cómo podemos informarle a Dagger2? Cuando necesite este objeto, ¿cómo debería hacerlo? ¿ser creado? En este momento, Dagger2 proporciona otra anotación @Provides.

Tomamos GlobalModule como ejemplo para ilustrar, el código fuente es el siguiente:

//@Module:告诉Dagger2,定义一个逻辑模块,这个模块包含FrameworkServicesModule,GlobalConcurrencyModule
@Module(includes = {
    
    
        FrameworkServicesModule.class,
        GlobalConcurrencyModule.class})
public class GlobalModule {
    
    

    //@Provides:告诉Dagger2,如果需要DisplayMetrics对象,就调用provideDisplayMetrics()函数即可
    //至于这个函数需要的参数Context,该怎么创建,Dagger2已经能够从我们给它的图中自动找到了
    @Provides
    public DisplayMetrics provideDisplayMetrics(Context context) {
    
    
        DisplayMetrics displayMetrics = new DisplayMetrics();
        context.getDisplay().getMetrics(displayMetrics);
        return displayMetrics;
    }

}

Hasta ahora, hemos introducido aproximadamente cómo hacer dibujos en Dagger2 (es decir, cómo usar anotaciones), como cómo usar @Component, @SubComponent, @Inject, @Provides, @Module, etc. No se han introducido en Dagger2, pero esto es suficiente para el resto de este artículo. Para otras anotaciones, puede consultar directamente la documentación de Dagger2. Este artículo solo se centra en el análisis de SystemUI.

Sin embargo, lo anterior es solo un dibujo y no menciona cómo usarlo. A continuación, combinaremos el proceso de inicio de SystemUI para ver cómo usar la imagen de Dagger2 dibujada arriba.

Proceso de inicio de SystemUI

El inicio de cualquier Apk comienza desde sus cuatro componentes principales. Antes de iniciar los cuatro componentes principales, comprobará si hay una aplicación personalizada. Si la hay, la aplicación se creará primero.

A partir de Android 9, se agregó AppComponentFactory para realizar las operaciones correspondientes antes de crear los cuatro componentes principales. Al igual que la aplicación, se configura en AndroidManifest.xml de la siguiente manera:

<application
        android:name=".SystemUIApplication"
        .
        .
        .
        android:appComponentFactory=".SystemUIAppComponentFactory">
        <!--省略若干不相干话题-->
</application>

Desde esta configuración podremos ver el siguiente proceso de inicio:

SystemUIAppComponentFactory->SystemUIApplication->un componente que se iniciará (cuatro componentes principales de Android).

Antes de analizar más a fondo este proceso, primero echemos un vistazo a quién inició SystemUI.

El punto de partida de system_server

Pero algunos lectores pueden preguntar: ¿Quién inició SystemUI? Verá que se inician otras aplicaciones al hacer clic en el icono ¿Quién inicia SystemUI?

Respuesta correcta: SystemUI se inicia mediante un proceso llamado system_server en el sistema Android. system_server se inicia cuando se enciende la computadora y luego system_server inicia varios servicios clave, incluido el inicio de SystemUI. Esto se explicará en detalle más adelante al analizar system_server.

Aquí solo se proporciona una breve descripción de cómo system_server inicia SystemUI:

  1. Después de que el sistema inicie el proceso system_server, ejecutará
new SystemServer().run();
  1. En el método run(), se iniciarán varios servicios, entre ellos:
  • Iniciar servicio de arranque
  • Servicios principales
  • otro servicio
  1. Al iniciar otros servicios, se iniciará SystemUI (a través de Intent). Para obtener los componentes específicos que inician SystemUI,
    puede obtenerlos a través del PackageManager de Android.

  2. PacakgeManager obtiene el nombre del componente específico leyendo la configuración config_systemUIServiceComponent. Esta configuración en el sistema Android
    es

<string name="config_systemUIServiceComponent" translatable="false"
            >com.android.systemui/com.android.systemui.SystemUIService</string>

Se puede ver que este es exactamente el componente que definimos en SystemUI.

Entonces podemos resumir el proceso de inicio de SystemUI.

  1. Se completa el inicio del sistema Android, inicie system_server
  2. system_server, según la configuración, inicia los componentes de SystemUI a través de Intent
  3. Antes de que SystemUI inicie el componente, primero creará el objeto SystemUIAppComponentFactory y luego llamará a su método correspondiente.
  4. A continuación, SystemUI creará SystemUIApplication y luego llamará a su método correspondiente.
  5. Finalmente, SystemUI creará SystemUIService y llamará al método correspondiente. Antes de crear SystemUIService, llamará al método correspondiente del objeto SystemUIAppComponentFactory creado en el paso 3.

Por qué utilizar SystemUIAppComponentFactory

El código fuente de SystemUIAppComponentFactory es el siguiente:

public class SystemUIAppComponentFactory extends AppComponentFactory {
    
    

    private static final String TAG = "AppComponentFactory";
    @Inject
    public ContextComponentHelper mComponentHelper;

    public SystemUIAppComponentFactory() {
    
    
        super();
    }

    @NonNull
    @Override
    //在创建Application之前,这个函数被调用
    public Application instantiateApplicationCompat(
            @NonNull ClassLoader cl, @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //调用父类方法,创建Application,此处会创建AndroidManifest.xml中配置的类
        //也即SystemUIApplication
        Application app = super.instantiateApplicationCompat(cl, className);
        //倘若创建的组件是ContextInitializer,则注册一个回调
        //请一定注意:虽然此处创建了Application,但是它还不能当做Context来使用
        if (app instanceof ContextInitializer) {
    
    
            ((ContextInitializer) app).setContextAvailableCallback(
                    context -> {
    
    
                        //1.在回调中,首先创建SystemUIFactory对象
                        SystemUIFactory.createFromConfig(context);
                        //2.通过这个SystemUIFactory得到SysUIComponent
                        //3.注入SystemUIAppComponentFactory中的成员,见上一小节
                        SystemUIFactory.getInstance().getSysUIComponent().inject(
                                SystemUIAppComponentFactory.this);
                    }
            );
        }

        return app;
    }

    @NonNull
    @Override
    //ContentProvider被创建之前,该函数被回调
    public ContentProvider instantiateProviderCompat(
            @NonNull ClassLoader cl, @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //省略若干
        //此处没有列出内容,原因是:它的逻辑和上一个函数一样
    }

    @NonNull
    @Override
    public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
            @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //省略若干
        //此处没有列出内容,原因是:它的逻辑和上一个函数一样
    }

    @NonNull
    @Override
    public Service instantiateServiceCompat(
            @NonNull ClassLoader cl, @NonNull String className, Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //判断是否为空,如果是,则再次注入
        //第一次注入,在instantiateApplicationCompat()函数设置的回调中,
        //这个回调由SystemUIApplication的onCreate()触发
        if (mComponentHelper == null) {
    
    
            // This shouldn't happen, but does when a device is freshly formatted.
            // Bug filed against framework to take a look: http://b/141008541
            SystemUIFactory.getInstance().getSysUIComponent().inject(
                    SystemUIAppComponentFactory.this);
        }
        //注意:这里的Service的创建
        //1. 先查询mComponentHelper中是否有对应的Service
        //2. 如果有则直接用,如果没有则调用父类方法创建
        //对于SystemUIService而言,它的构造函数有@Inject注解,因此当调用mComponentHelper.resolveService时,能够正确返回SystemUIService
        //请思考:为什么这个不要系统自己创建?
        //答案:因为SystemUIService,需要有其他依赖对象,若是由系统创建,那么必然会有
        //像SystemUIService.setXXX()之类的函数,会增加代码和逻辑。如果由Dagger2来创建则不会有
        //这些烦恼
        Service service = mComponentHelper.resolveService(className);
        if (service != null) {
    
    
            return service;
        }
        return super.instantiateServiceCompat(cl, className, intent);
        
    }

    @NonNull
    @Override
    public BroadcastReceiver instantiateReceiverCompat(@NonNull ClassLoader cl,
            @NonNull String className, @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //省略若干
        //此处没有列出内容,原因是:它的逻辑和上一个函数一样
    }

}

En esta clase, las funciones más importantes son tres puntos:

  1. Creación de componentes correspondientes, como SystemUIApplication, SystemUIService, etc.
  2. Inicialización de mComponentHelper, es decir, mediante inyección
  3. Antes de crear el componente de Servicio, primero puede consultar si ya existe un componente en mComponentHelper y, de ser así, utilizarlo directamente.

Antes de ver la creación de SystemUIApplication y SystemUIService, todavía tenemos que pensar nuevamente en una pregunta: ¿
Por qué usar la clase SystemUIAppComponentFactory? ¿Es esta clase realmente razonable? ¿Hay una mejor alternativa?

Creo que la respuesta está en la creación de SystemUIService. Es el uso de la clase SystemUIAppComponentFactory lo que nos permite inyectar mejor dependencias.

Antes de ingresar a SystemUIService, lo primero que se crea es SystemUIApplication. Veamos qué hace.

SistemaUIAplicación

El código fuente es el siguiente:

public class SystemUIApplication extends Application implements
        SystemUIAppComponentFactory.ContextInitializer {
    
    

    public SystemUIApplication() {
    
    
        super();
        Log.v(TAG, "SystemUIApplication constructed.");
        // SysUI may be building without protolog preprocessing in some cases
        ProtoLog.REQUIRE_PROTOLOGTOOL = false;
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();
        Log.v(TAG, "SystemUIApplication created.");
        //用于跟踪启动和关闭的时序数据
        TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
                Trace.TRACE_TAG_APP);
        log.traceBegin("DependencyInjection");
        //这就是初始化各种Dagger2依赖的地方,这个回调在SystemUIAppComponentFactory中被设置
        mContextAvailableCallback.onContextAvailable(this);
        //有了Dagger2,就是直接使用对应的组件
        mRootComponent = SystemUIFactory.getInstance().getRootComponent();
        mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent();
        mComponentHelper = mSysUIComponent.getContextComponentHelper();
        mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
        log.traceEnd();

        //设置主题
        setTheme(R.style.Theme_SystemUI);

        //判断是否为主进程
        if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
    
    
            //监听系统的启动广播
            IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
            bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);

            //设置线程渲染优先级
            int sfPriority = SurfaceControl.getGPUContextPriority();
            Log.i(TAG, "Found SurfaceFlinger's GPU Priority: " + sfPriority);
            if (sfPriority == ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_REALTIME_NV) {
    
    
                Log.i(TAG, "Setting SysUI's GPU Context priority to: "
                        + ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
                ThreadedRendererCompat.setContextPriority(
                        ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
            }

            //注册广播接收器
            registerReceiver(new BroadcastReceiver() {
    
    
                @Override
                public void onReceive(Context context, Intent intent) {
    
    
                    //mBootCompleteCache表示的是:是否SytemUI的各种服务启动完成
                    //这些服务的启动,可能早于系统启动完成广播,也可能晚于系统启动完成广播

                    //1. 如果SystemUI的各种服务已经启动完成则直接返回
                    if (mBootCompleteCache.isBootComplete()) return;

                    if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
                    //2. 如果没有启动完成,则挨个启动
                    unregisterReceiver(this);
                    mBootCompleteCache.setBootComplete();
                    if (mServicesStarted) {
    
    
                        final int N = mServices.length;
                        for (int i = 0; i < N; i++) {
    
    
                            mServices[i].onBootCompleted();
                        }
                    }
                }
            }, bootCompletedFilter);

            //监听是否Local改变
            //如果Local改变,则通知中的显示就需要改变,如中英文切换等
            IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
            registerReceiver(new BroadcastReceiver() {
    
    
                @Override
                public void onReceive(Context context, Intent intent) {
    
    
                    if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
    
    
                        if (!mBootCompleteCache.isBootComplete()) return;
                        // Update names of SystemUi notification channels
                        NotificationChannels.createAll(context);
                    }
                }
            }, localeChangedFilter);
        } else {
    
    
            //如果是子进程则会进入此部分逻辑

            //如果是主用户下的子进程,则什么也不做,直接返回
            String processName = ActivityThread.currentProcessName();
            ApplicationInfo info = getApplicationInfo();
            if (processName != null && processName.startsWith(info.processName + ":")) {
    
    
                return;
            }
            //如果不是主用户,则需要去启动必要的SystemUI组件
            startSecondaryUserServicesIfNeeded();
        }
    }

    //省略若干,简单代码

}

El código de SystemUIApplication es relativamente simple y se ha marcado en los comentarios. Siguiente vistazo a SystemUIService

Servicio UI del sistema

El código fuente es el siguiente:

public class SystemUIService extends Service {
    
    

    //省略若干,简单代码

    //@Inject:嘿嘿,这就是给Dagger画的图,好让Dagger2知道怎么创建SystemUIService
    @Inject
    public SystemUIService(
            @Main Handler mainHandler,
            DumpHandler dumpHandler,
            BroadcastDispatcher broadcastDispatcher,
            LogBufferFreezer logBufferFreezer,
            BatteryStateNotifier batteryStateNotifier) {
    
    
        //省略赋值代码
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();

        //对没错,startServicesIfNeeded作为整个SystemUI关键服务的启动源头,就在这里了。
        //在进入分析之前,先思考:为什么要放在这里执行呢?就不能直接放在SystemUIApplication中吗?
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();

        //LogBufferFreezer接收bugreport开始的广播,然后停止对应的LogBuffer的记录
        mLogBufferFreezer.attach(mBroadcastDispatcher);

        //是否监听电池的状态,并且会提示在通知栏上
        if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) {
    
    
            mBatteryStateNotifier.startListening();
        }

        //调试代码,用debug.crash_sysui触发RescueParty
        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
    
    
            throw new RuntimeException();
        }

        //Binder调试相关
        //如果太多binder调用就触发onLimitReached回调
        if (Build.IS_DEBUGGABLE) {
    
    
            //设置Binder代理计数开
            BinderInternal.nSetBinderProxyCountEnabled(true);
            //配置Binder代理触发BinderProxyLimitListener回调的最高和最低阈值,最低表示:只有降到最低以下,才能再次触发
            BinderInternal.nSetBinderProxyCountWatermarks(1000,900);
            //设置BinderProxyLimitListener监听
            BinderInternal.setBinderProxyCountCallback(
                    new BinderInternal.BinderProxyLimitListener() {
    
    
                        @Override
                        public void onLimitReached(int uid) {
    
    
                            Slog.w(SystemUIApplication.TAG,
                                    "uid " + uid + " sent too many Binder proxies to uid "
                                    + Process.myUid());
                        }
                    }, mMainHandler);
        }

        //启动DumpService,如果系统运行bugreport,SystemUIAuxiliaryDumpService会将SystemUI中的一些关键数据dump出来
        startServiceAsUser(
                new Intent(getApplicationContext(), SystemUIAuxiliaryDumpService.class),
                UserHandle.SYSTEM);
    }

    //省略若干,简单代码
}

A continuación, veamos el contenido específico de la función startServicesIfNeeded()

función startServicesIfNeeded()

Esta función se encuentra en la clase SystemUIApplication y el código fuente es el siguiente:

public void startServicesIfNeeded() {
    
    
    //1. 获取需要start的服务列表
    //2. 然后调用startServicesIfNeed()继续启动
    //注意:看到这里,其实大家应该大胆假设,getSystemUIServiceComponents函数
    //是不是通过Dagger2的依赖得到的。如果不是为什么?
    String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
    startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}

private void startServicesIfNeeded(String metricsPrefix, String[] services) {
    
    
    //省略判断

    mServices = new SystemUI[services.length];

    //启动完成缓存对象的修改,简单,略
    
    //首先获取DumpManager
    final DumpManager dumpManager = mSysUIComponent.createDumpManager();

    //trace跟踪点,略

    //挨个启动服务
    // 1. 首先查看mComponentHelper是否有缓存,如果有则直接使用
    // 2. 如果没有则反射创建
    // 3. 创建完成调用start()
    // 4. 判断是否系统启动完成,如果完成则调用onBootCompleted()
    // 5. 将启动的服务,加入DumpManager中,以便bugreport触发其dump
    final int N = services.length;
    for (int i = 0; i < N; i++) {
    
    
        String clsName = services[i];
        if (DEBUG) Log.d(TAG, "loading: " + clsName);
        log.traceBegin(metricsPrefix + clsName);
        long ti = System.currentTimeMillis();
        try {
    
    
            SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
            if (obj == null) {
    
    
                Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
                obj = (SystemUI) constructor.newInstance(this);
            }
            mServices[i] = obj;
        } catch (ClassNotFoundException
                | NoSuchMethodException
                | IllegalAccessException
                | InstantiationException
                | InvocationTargetException ex) {
    
    
            throw new RuntimeException(ex);
        }

        if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
        mServices[i].start();
        log.traceEnd();

        // Warn if initialization of component takes too long
        ti = System.currentTimeMillis() - ti;
        if (ti > 1000) {
    
    
            Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");
        }
        if (mBootCompleteCache.isBootComplete()) {
    
    
            mServices[i].onBootCompleted();
        }

        dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
    }
    mSysUIComponent.getInitController().executePostInitTasks();
    log.traceEnd();

    mServicesStarted = true;
}

Como puede ver en lo anterior, la función principal de SystemUIService es llamar a las funciones start() y onBootCompleted() de cada servicio. Inicio completo.

Ahora piense por qué esta parte del contenido debería colocarse en SystemUIApplication y ¿por qué no se puede colocar directamente en SystemUIService?

Antes de responder a esta pregunta, primero echemos un vistazo a cómo la función getSystemUIServiceComponents() obtiene los servicios que deben iniciarse.

La función getSystemUIServiceComponents obtiene el servicio que se va a iniciar

El código fuente es el siguiente:

public String[] getSystemUIServiceComponents(Resources resources) {
    
    
    return resources.getStringArray(R.array.config_systemUIServiceComponents);
}

La función anterior se obtiene a través de la matriz de cadenas configurada. Quizás tenga curiosidad: ¿por qué es diferente de Dagger2? ¿Por qué configurar una matriz de cadenas para algo tan conveniente?

¿Por qué está configurado de esta manera? Creo que hay varias razones principales:

  1. Al compilar código AOSP, puede utilizar la función de anulación para colocar estos archivos de recursos en un directorio personalizado con fines de modificación.
  2. Por razones históricas, antes estaba configurado así, jaja.

Ahora pensemos por qué la lógica funcional de iniciar el servicio no se puede colocar en SystemUIservice sino en SystemUIApplication.
Si desea ponerlo en SystemUIApplication, ¿por qué no iniciarlo a través de SystemUIApplication, sino utilizar SystemUIService para iniciarlo?

  1. Además de iniciar los servicios SystemUIService, en el caso de varios usuarios, también es necesario iniciar algunos servicios. En este momento, la aplicación SystemUI

    SystemUIApplication se llama primero sin llamar a SystemUIService. Porque la activación de SystemUIService la inicia system_server.

    Además, la lógica de inicio del sistema de monitoreo debe procesarse de manera unificada, por lo que resulta natural colocar la lógica de inicio en SystemUIApplication.

Nota: Obviamente, esto genera un error. Si SystemUI informa un error a mitad de camino y deja de ejecutarse, cuando se ejecuta nuevamente, ¿se pueden
inicializar correctamente los distintos servicios iniciados por SystemUIService? Obviamente no, esto sucede a menudo cuando escribo este artículo.

  1. Dado que la lógica de inicio se incluyó en SystemUIApplication, ¿SystemUIApplication no puede iniciar esta parte del servicio?

    ¿Por qué debería utilizarse un SystemUIService independiente como punto de entrada para comenzar? Para responder a esta pregunta, necesita saber que las aplicaciones de Android se inician iniciando un componente que se va a ejecutar. Es decir, si system_server quiere ejecutar SystemUI, debe iniciar un determinado componente, no solo la Aplicación.

    Por lo tanto, necesitamos un componente para el inicio, que es SystemUI, es el encargado de iniciar cada servicio específico. Además de monitorear la transmisión de inicio y cooperar con múltiples usuarios, esta parte del contenido se completa en SystemUIApplication.

    Y debido a que SystemUIService requiere una inyección de dependencia, se creó SystemUIAppComponentFactory para implementar la inyección de dependencia correspondiente.

Hasta ahora, hemos descubierto completamente el proceso de inicio de SystemUIService, SystemUIApplication, SystemUIAppComponentFactory
y por qué asignan funciones de esta manera. El resumen es el siguiente.

  1. Después de que se inicia system_server, comienza a iniciar varios servicios.
  2. Al iniciar otros servicios, primero obtendrá el nombre del componente para iniciar systemui a través de PackageManager y luego iniciará el componente systemui de acuerdo con el nombre.
  3. El nombre obtenido en el paso anterior es SystemUIService.
  4. Cuando se inicia SystemUIService, SystemUIApplication se creará primero y se llamará a SystemUIAppComponentFactory para agregar la inyección de dependencia correspondiente antes de la creación.
  5. Una vez creada SystemUIApplication, escuchará la transmisión de inicio del sistema.
  6. Luego cree SystemUIService, antes de crearlo, se llamará al método correspondiente de SystemUIAppComponentFactory para agregar inyección de dependencia.
  7. Después de crear SystemUIService, inicie varios servicios a través de SystemUIApplication

En este punto, se completa todo el inicio de SystemUI.

Agregar un servicio personalizado a SystemUI

Con el análisis anterior, ahora falta probarlo: escribir un módulo de servicio personalizado, en este módulo solo imprimimos su proceso de inicio.

  1. Cree una nueva clase llamada PrintLogService. Esta clase puede heredar SystemUI. como sigue
public class PrintLogService extends SystemUI{
    
    

    private String TAG = "PrintLogService";

    //使用@Inject标记,让Dagger2自动管理依赖
    @Inject
    public PrintLogService(Context context) {
    
    
        super(context);
    }

    //简单打印log
    @Override
    public void start() {
    
    
        Slog.d(TAG, "Start PrintLogService");
    }
    //简单打印log
    @Override
    protected void onBootCompleted() {
    
    
        Slog.d(TAG,"PrintLogService boot completed");
    }

}
  1. Coloque el nombre de esta clase en la matriz de configuración, config_systemUIServiceComponents, de la siguiente manera:
<!-- 最后一行加入我们自定义的服务 -->
    <string-array name="config_systemUIServiceComponents" translatable="false">
        <item>com.android.systemui.util.NotificationChannels</item>
        <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
        <item>com.android.systemui.recents.Recents</item>
        <item>com.android.systemui.volume.VolumeUI</item>
        <item>com.android.systemui.statusbar.phone.StatusBar</item>
        <item>com.android.systemui.usb.StorageNotification</item>
        <item>com.android.systemui.power.PowerUI</item>
        <item>com.android.systemui.media.RingtonePlayer</item>
        <item>com.android.systemui.keyboard.KeyboardUI</item>
        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
        <item>@string/config_systemUIVendorServiceComponent</item>
        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
        <item>com.android.systemui.LatencyTester</item>
        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
        <item>com.android.systemui.ScreenDecorations</item>
        <item>com.android.systemui.biometrics.AuthController</item>
        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
        <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
        <item>com.android.systemui.theme.ThemeOverlayController</item>
        <item>com.android.systemui.accessibility.WindowMagnification</item>
        <item>com.android.systemui.accessibility.SystemActions</item>
        <item>com.android.systemui.toast.ToastUI</item>
        <item>com.android.systemui.wmshell.WMShell</item>
        <item>com.android.systemui.PrintLogService</item>
    </string-array>

  1. Utilice el siguiente comando para compilarlo y enviarlo al teléfono móvil.
mmm frameworks/base/packages/SystemUI
adb root
adb remount
adb shell rm -rf system_ext/priv-app/SystemUI
adb push out/**/system_ext/priv-app/SystemUI /system_ext/priv-app/

Luego use kill para finalizar el proceso SystemUI existente.

  1. Desde el registro podemos ver el siguiente resultado.

Insertar descripción de la imagen aquí

¡Esto significa que nuestro servicio personalizado comenzó con éxito! ! !

¡Este artículo está terminado! !

En el artículo, solo mencionamos brevemente mContextAvailableCallback.onContextAvailable(this); que
trata sobre la inicialización de varios componentes en Dagger2. El próximo artículo comenzará desde esta función para explorar la inicialización de varios componentes en SystemUI y comprender SystemUI. Cómo cada componente debería ser usado.

Supongo que te gusta

Origin blog.csdn.net/xiaowanbiao123/article/details/132409010
Recomendado
Clasificación