Marco de complemento de Android Análisis del principio de sombra

1. Prefacio :

1. Introducción:

Shadow es un marco de complemento de Android desarrollado de forma independiente por Tencent, que ha sido probado por cientos de millones de usuarios en línea. Shadow no solo comparte el código clave de la tecnología de complemento con código abierto, sino que también comparte completamente todos los diseños necesarios para la implementación en línea.

En comparación con otros marcos de complementos en el mercado, Shadow tiene principalmente las siguientes características:

  • Reutilice el código fuente de la aplicación de instalación independiente : el código fuente de la aplicación de complemento se puede instalar y ejecutar normalmente.
  • Reflexión cero y no implementación Hack de la tecnología de complemento : en teoría, se ha determinado que no hay necesidad de un desarrollo compatible de ningún sistema, y ​​no hay una llamada API oculta, lo que no entra en conflicto con la estrategia de Google de restringir el acceso a interfaces SDK no públicas.
  • Marco de complemento completamente dinámico : es difícil implementar un marco de complemento perfecto al mismo tiempo, pero Shadow hace que todas estas implementaciones sean dinámicas, de modo que el código del marco de complemento se convierte en parte del complemento. La iteración de complementos ya no está limitada por el paquete de host de una versión anterior del marco de complemento.
  • El incremento de host es muy pequeño : gracias a la implementación dinámica completa, la cantidad de código que se integra realmente en el programa host es extremadamente pequeña (15 KB, aproximadamente 160 métodos).
  • Implementación de Kotlin : el código central core.loader, core.transform está completamente implementado en Kotlin, el código es conciso y fácil de mantener.

2. Dirección del proyecto

Dirección del proyecto: GitHub - Tencent/Shadow: Marco de complemento de Android dinámico completo de reflexión cero

2. Análisis de principio :

Shadow afirma no tener puntos Hook. El principio central es utilizar el método de proxy para cambiar el período de compilación de la actividad original en una clase de proxy para representar todos los ciclos de vida de la actividad del anfitrión.

2.1 Soluciones tradicionales

Dado que se menciona que la característica de Shadow es que no hay gancho, entonces, naturalmente, primero hablaremos brevemente sobre el método de gancho para realizar la creación de componentes. La forma tradicional es enganchar la instrumentación o classLoader, que originalmente convirtió la tarea de iniciar HostActivity en la tarea de iniciar TargetActivity, realizando así el inicio de TargetActivity en el complemento.

Para artículos de complementos específicos, consulte mis cursos de la serie de complementos:

https://blog.csdn.net/rzleilei/category_11590922.html?spm=1001.2014.3001.5482

¿Hay alguna desventaja en los ganchos? Eso es natural, de lo contrario, Shadow no se concentraría en promocionar su función sin gancho. El mayor problema con los anzuelos es el riesgo. Con la actualización de la versión de Android, es probable que el punto de enlace anterior cambie con el cambio de versión. Por ejemplo, la instrumentación se engancha a través de la reflexión a continuación. Dicho código está bien en Android 12, pero si Android 14 establece mInstrumentation como una variable privada, toda nuestra solución de complemento fallará.

 private fun checkInstrumentation(context: Context) {
        if (state.isHookInstrumentation) {
            return
        }
        state.isHookInstrumentation = true
        val myInstrumentation =
            MyInstrumentation()
        //替换Acitivty中的mInstrumentation
        val classLoader = javaClass.classLoader
        val activityThreadClass = classLoader.loadClass("android.app.ActivityThread")
        val activityThreadField =
            activityThreadClass.getDeclaredField("sCurrentActivityThread")
        activityThreadField.isAccessible = true
        val activityThreadGet = activityThreadField.get(null)

        val instrumentationField = activityThreadClass.getDeclaredField("mInstrumentation")
        instrumentationField.isAccessible = true
        instrumentationField.set(activityThreadGet, myInstrumentation)
    }

2.2 Proceso de inicio de actividad en Shadow:

Lo anterior es el diagrama de flujo de inicio completo que dibujé y resolví por la tarde, que básicamente cubre todos los procesos. La siguiente es una explicación de todo el proceso.

1. Primero haga clic como punto de inicio. Hacemos clic en el botón para iniciar la aplicación. En este momento, se llama al método startPluginActivity. El inicio es para iniciar el PluginDefaultProxyActivity que hemos enterrado en el host, y traerá información necesaria. , como el nombre de clase de la clase de destino, etc. Espere.

2. Dado que registramos PluginDefaultProxyActivity en el mainfest, la verificación de AMS pasará y luego se notificará a la Instrumentación para que cree la Actividad correspondiente.

3. Cuando se crea PluginDefaultProxyActivity, se llamará al constructor de la clase principal. Su clase principal es PluginContainerActivity. En el método de construcción de PluginContainerActivity, se genera el delegado del objeto ShadowActivityDelegate de la clase de proxy, y esta clase de proxy mantiene la relación entre el host y la clase de implementación.

3. Después de crear la instrumentación del sistema, se llamará al método onCreate de la Actividad. Como no hay implementación en PluginDefaultProxyActivity, el sistema llamará al método onCreate de su clase padre PluginContainerActivity.

4. En el método onCreate de PluginContainerActivity, la actividad objetivo (generada de acuerdo con el nombre de la clase pasada y otra información) se creará a través del objeto delegado creado anteriormente. La actividad objetivo es nuestra actividad objetivo, que contiene nuestra lógica comercial normal.

5. Después de crear el objeto, se llamará al método onCreate de targetActivity. En nuestra actividad de destino, naturalmente habrá una lógica de uso normal. Como setContentView() y así sucesivamente.

6. Aquí hay un ejemplo de setContentView. Llamar al método setContentView en TargetActivity en realidad llamará al método setContentView en su ShadowActivity padre.

7. Algunas personas naturalmente preguntarán aquí. ¿No se hereda nuestra TargetActivity normal de Activity? ¿No sería muy problemático cambiar la clase padre a ShadowActivity? Aquí Shadow utiliza la tecnología de instrumentación de código de bytes, es decir, cuando se empaqueta el APK, automáticamente lo reemplazará por nosotros.

8. En el método onCreate de ShadowActivity, el método setContentView de la actividad del host real será notificado a través de la clase de proxy.

2.3 Cómo Shadow carga el dex en el complemento

Es el uso normal de DexClassLoader para cargar.

new DexClassLoader(apkFile.getAbsolutePath(), oDexDir.getAbsolutePath(), null, ODexBloc.class.getClassLoader());

2.4 Cómo Shadow carga los paquetes de recursos

  val packageManager = hostAppContext.packageManager
        packageArchiveInfo.applicationInfo.publicSourceDir = archiveFilePath
        packageArchiveInfo.applicationInfo.sourceDir = archiveFilePath
        packageArchiveInfo.applicationInfo.sharedLibraryFiles = hostAppContext.applicationInfo.sharedLibraryFiles
        try {
            return packageManager.getResourcesForApplication(packageArchiveInfo.applicationInfo)
        } catch (e: PackageManager.NameNotFoundException) {
            throw RuntimeException(e)
        }

También se genera a través del administrador de paquetes normal. Pero genera un nuevo Recursos, que es diferente de los Recursos que normalmente se utilizan en el host. Dado que hay diferentes recursos en el complemento y el host, no hay problema de conflicto de ID de recurso.

3. Opinión personal

3.1 Experiencia de usuario

Se ha utilizado durante unos meses sin mayores problemas y la estabilidad sigue siendo buena.

3.2 Ventajas

1. Sin punto de enganche, será un estado estable a largo plazo.

2. No habrá conflicto entre los ID de recursos, lo que es especialmente compatible con modelos especiales como Samsung.

3.3 Desventajas

1. Debido a que es un modo de instrumentación, el atributo de estilo en la Actividad no es efectivo. Debe configurarse en el código de actividad.

2. Shadow debe dividirse en componentes, por lo que la aplicación de complemento debe estar conectada al componente Shadow para una determinada transformación antes de que pueda conectarse normalmente.

3. El costo de acceso sigue siendo alto. Para muchas funciones, debe comprender sus principios para usarlas normalmente, y el acceso con un solo clic aún no es posible.

Supongo que te gusta

Origin blog.csdn.net/AA5279AA/article/details/121103090
Recomendado
Clasificación