[Preguntas de la entrevista de Android] El último tema de la entrevista en 2023: carga de clase de reflexión de Java y proxy dinámico (1)

1 ¿Cuál es la diferencia entre PathClassLoader y DexClassLoader?

¿Qué está tratando de investigar esta pregunta?

El principio del mecanismo de carga de clases en Android

Puntos de conocimiento de inspección

Mecanismo de carga de clases ClassLoader

Cómo responden los candidatos

ClassLoader es lo que solemos llamar un cargador de clases.

Introducción a ClassLoader

Cualquier programa Java se compone de uno o más archivos de clase. Cuando el programa se está ejecutando, los archivos de clase deben cargarse en la JVM antes de que puedan usarse. El mecanismo de carga de clases de Java es responsable de cargar estos archivos de clase. La función de ClassLoader es simplemente cargar archivos de clase y proporcionarlos para su uso cuando el programa se está ejecutando. Hay un campo classLoader dentro de cada objeto Class para identificar qué ClassLoader lo carga.

class Class<T> {
    
    
  ...
  private transient ClassLoader classLoader;
  ...
}

ClassLoader es una clase abstracta y tiene muchas clases de implementación concretas, las más importantes son:

  • BootClassLoader

    Se utiliza para cargar archivos de clase de capa de Android Framework.

  • PathClassLoader

    Para cargadores de clases de aplicaciones de Android. Puede cargar dex especificado y classes.dex en jar, zip, apk

  • DexClassLoader

    Se usa para cargar el dex especificado y las clases.dex en jar, zip, apk

  • InMemoryDexClassLoader

    Agregado en Android 8.0, usado para cargar dex en la memoria

DexClassLoader y PathClassLoader

En versiones anteriores a Android 5.0, la diferencia entre los dos es:

  1. DexClassLoader: se pueden cargar jar, apk y dex, y se pueden cargar desde la tarjeta SD
  2. PathClassLoader: solo puede cargar archivos apk que se hayan instalado en el sistema (es decir, en el directorio /data/app)

Pero con la actualización de la versión de Android, este ya no es el caso para Android 5.0 y posteriores.

PathClassLoaderVeamos primero el y en Android DexClassLoader:

public class DexClassLoader extends BaseDexClassLoader {
    
    
  
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
    
    
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

public class PathClassLoader extends BaseDexClassLoader {
    
    
   
    public PathClassLoader(String dexPath, ClassLoader parent) {
    
    
        super(dexPath, null, null, parent);
    }

   
    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
    
    
        super(dexPath, null, libraryPath, parent);
    }
}

Ambos heredan de la misma clase principal: , y la diferencia entre los dos es que se debe pasar un directorio optimizadoPathClassLoader para almacenar el resultado de dexopt, mientras que el último no.DexClassLoaderBaseDexClassLoaderDexClassLoader

dexopto:

En Dalvik , cuando la máquina virtual carga un archivo dex, verifica y optimiza el archivo dex, y el resultado de la optimización del archivo dex se convierte en un archivo odex (dex optimizado), que usa algunos códigos de operación optimizados, y la documentación de dex es similar.

De hecho, ya sea que lo sea PathClassLoadero no DexClassLoader, puede ver que no hay otros métodos para anular la clase principal. Si el directorio de optimización deOptimizedDirectory es nulo, es decir PathClassLoader, por debajo de Android5.0, se usará el directorio optimizado predeterminado: /data/dalvik-cache/ .

Al instalar un APK en este directorio, el sistema almacenará automáticamente archivos odex en él: data@[email protected]@classes.dex
inserte la descripción de la imagen aquí

Al usar PathClassLoaderla carga, si el APK cargado aún no está instalado en el teléfono móvil, informará: El directorio de caché de Dex no se puede escribir: /data/dalvik-cache , nuestra aplicación en sí no tiene permiso de escritura para este directorio. Entonces PathClassLoader solo puede cargar los archivos dex en el APK instalado.

Bajo ART, el método de carga ha sufrido un cambio completamente diferente. Durante la instalación, dex2oat (operación de compilación anticipada de AOT) se ejecuta en el archivo dex y se compila en un archivo ejecutable OAT (en realidad, un archivo ELF) (código de máquina). Y si el archivo Oat no se puede cargar con éxito durante la carga, aún intentará cargar desde el dex original. Por lo tanto, en ART, puede cargar PathClassLoadercualquier DexClassLoaderdex especificado, así como también classes.dex en jar, zip y apk. Sin embargo, la carga desde el dex original provocará la falla de dex2oat, lo que acelerará la carga y reducirá la eficiencia operativa.

Después de Android N, se adoptan la interpretación, el modo mixto AOT y JIT .

En Android 8.1 y versiones posteriores, DexClassLoaderse convierte en:

public class DexClassLoader extends BaseDexClassLoader {
    
    
35    /**
36     * Creates a {@code DexClassLoader} that finds interpreted and native
37     * code.  Interpreted classes are found in a set of DEX files contained
38     * in Jar or APK files.
39     *
40     * <p>The path lists are separated using the character specified by the
41     * {@code path.separator} system property, which defaults to {@code :}.
42     *
43     * @param dexPath the list of jar/apk files containing classes and
44     *     resources, delimited by {@code File.pathSeparator}, which
45     *     defaults to {@code ":"} on Android
46     * @param optimizedDirectory this parameter is deprecated and has no effect
47     * @param librarySearchPath the list of directories containing native
48     *     libraries, delimited by {@code File.pathSeparator}; may be
49     *     {@code null}
50     * @param parent the parent class loader
51     */
52    public DexClassLoader(String dexPath, String optimizedDirectory,
53            String librarySearchPath, ClassLoader parent) {
    
    
54        super(dexPath, null, librarySearchPath, parent);
55    }
56}

En este momento, el directorio optimizado en DexClassLoader también está arreglado para pasar nulo, por lo que no hay diferencia entre los dos.

Resumir

  • Android 4.4 y anteriores:
    • DexClassLoader: se pueden cargar jar, apk y dex, y se pueden cargar desde la tarjeta SD
    • PathClassLoader: solo puede cargar archivos apk que se hayan instalado en el sistema (es decir, en el directorio /data/app)
  • Android 5,0 ~ Android 8,0:
    • DexClassLoader: se pueden cargar jar, apk y dex, y se pueden cargar desde la tarjeta SD
    • PathClassLoader: se pueden cargar jar, apk y dex, y se pueden cargar desde la tarjeta SD, pero hará que sea imposible realizar la operación dex2oat
  • Android 8.1 y superior:
    • DexClassLoader es exactamente lo mismo que PathClassLoader

2 ¿Qué es el mecanismo de encomienda de los padres y por qué es necesario el mecanismo de encomienda de los padres?

¿Qué está tratando de investigar esta pregunta?

Principio del mecanismo de carga de clases

Puntos de conocimiento de inspección

mecanismo de carga de clases

Cómo responden los candidatos

Mecanismo de Delegación de Padres

El mecanismo de delegación principal significa que cuando un cargador de clases recibe una solicitud de carga de clases, el cargador de clases primero delegará la solicitud al cargador de clases principal. Lo mismo es cierto para cada cargador de clases. Solo cuando el cargador de clases principal no puede encontrar la clase especificada dentro de su propio rango de búsqueda, el cargador de clases secundario intentará cargarla por sí mismo.

public abstract class ClassLoader{
    
    
    //父类加载器
    ClassLoader parent;

    protected ClassLoader(ClassLoader parentLoader) {
    
    
            this(parentLoader, false);
    }

    ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
    
    
        if (parentLoader == null && !nullAllowed) {
    
    
            throw new NullPointerException("parentLoader == null && !nullAllowed");
        }
        parent = parentLoader;
    }

    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
    
    
        //先找缓存
        Class<?> clazz = findLoadedClass(className);
        if (clazz == null) {
    
    
             ClassNotFoundException suppressed = null;
            if(parent !=null){
    
    
                try {
    
    
                     //交给父类加载器加载
                    clazz = parent.loadClass(className, false);
                } catch (ClassNotFoundException e) {
    
    
                    suppressed = e;
                }
            }
            if (clazz == null) {
    
    
                try {
    
    
                    //父类加载器加载不到,自己加载
                    clazz = findClass(className);
                } catch (ClassNotFoundException e) {
    
    
                    e.addSuppressed(suppressed);
                    throw e;
                }
            }
        }

        return clazz;
    }
}

El papel del mecanismo de delegación de padres

1. Evite la carga duplicada

2. Seguridad, para garantizar que la clase del sistema no pueda ser alterada.

La máquina virtual de Java solo determinará que se trata de una clase cuando los nombres de las diferentes clases sean los mismos y los cargadores que cargan la clase sean todos iguales. Si no hay un mecanismo de delegación principal, varios cargadores de clases pueden cargar la misma clase, por lo que la clase puede reconocerse como dos clases diferentes y surgirán problemas al asignar valores entre sí.

El mecanismo de delegación de padres puede garantizar que cuando varios cargadores carguen una clase, al final será cargada por un cargador, asegurando que los resultados finales de la carga sean los mismos.

No existe un modelo de delegación parental, si se permite que todos los cargadores de clases se carguen solos, si el usuario escribe una clase llamada java, no se puede garantizar el comportamiento básico en el sistema de tipos y la aplicación se convertirá en un desastre.

3 ¿Cuáles son los métodos de carga de clases en Android? ¿Cual es la diferencia?

¿Qué está tratando de investigar esta pregunta?

El proceso de carga de clases en el mecanismo de carga de clases

Puntos de conocimiento de inspección

  1. proceso de carga de clases
  2. tiempo de carga de la clase

¿Cómo deben responder los candidatos?

La forma en que Android carga las clases es en realidad la carga de clases de Java. La máquina virtual carga la información que describe la clase desde el archivo Class a la memoria, y verifica, convierte, analiza e inicializa los datos, y finalmente se convierte en un tipo Java que la máquina virtual puede usar directamente.

tiempo de carga de la clase

Las clases se cargan automáticamente cuando:

  1. Usando new para instanciar un objeto, crear una instancia de una subclase primero cargará su clase principal
  2. Acceso a métodos estáticos de una clase.
  3. Acceso a las propiedades estáticas de una clase
  4. Hacer llamadas reflexivas a las clases
  5. La clase principal se define en el programa Java y la clase se cargará cuando se inicie el método principal.

Las cinco situaciones anteriores activarán la carga de la clase y completarán la inicialización de la clase. Además de las situaciones anteriores, también podemos llamar ClassLoader#loadClass(name)o Class.forName(name)cargar activamente. De hecho, Class.forName(name)la carga de la clase especificada también se completa a través de ClassLoader.

public static Class<?> forName(String className) throws ClassNotFoundException {
    
    
    //得到调用者的类,如main方法所在类
	Class<?> caller = Reflection.getCallerClass();
    //ClassLoader.getClassLoader(caller):获得main方法所在类的类加载器,使用其完成className的加载
	return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

Pero ClassLoader#loadClass(name)la Class.forName(name) diferencia es que además de completar la carga de clases, este último también inicializa la clase y ejecuta el bloque estático en la clase. En cambio, ClassLoader#loadClasssolo se realiza la carga de clases.

Por supuesto, también podemos usar: Class.forName(String name, boolean initialize, ClassLoader loader). A través del segundo parámetro initialize, puede elegir si inicializar la clase.

por fin

Esta pregunta de la entrevista continuará actualizándose, ¡preste atención! ! ! !
¡Los amigos que necesiten esta pregunta de entrevista pueden escanear el código QR a continuación para obtenerla gratis! ! !
¡Al mismo tiempo, al escanear el código QR a continuación, también puede ingresar al grupo para disfrutar del servicio del robot ChatGPT! ! !

Supongo que te gusta

Origin blog.csdn.net/datian1234/article/details/131987451
Recomendado
Clasificación