The article directories

Android plug-ins series of articles

  1. Dex file in “plug-in”
  2. Class loader analysis
  3. Get the class loader
  4. Parent delegation mechanism

I. Dex file in “plug-in”

At present, large Android projects are basically componentized + plug-in development, and the project architecture is componentized framework. Some Module modules that are frequently modified are set as “plug-in” modules and compiled into independent APK files. Deploy as “plug-ins” to be called by “host” modules;

When the application is running, click to start the interface in APK of a “plug-in”, first download the corresponding APK file of the plug-in, put it in the built-in storage area, and then load the APK file, mainly the Class bytecode data in the DEX file of the Class loader;

In this case, the App Module is the “host” Module and the Plugin Module is the “plug-in” Module, both of which are apps of the “Phone & Tablet Module” type.

The pluginactivity.class bytecode file can be found in the “classes.dex” file. After you find the module, you can load it into the application and jump to the interface.

Class loader analysis

Class loading is to load bytecode data into the Java VIRTUAL machine stack in the runtime data area of the Java virtual machine through the class loading engine.

ClassLoader loading involves parental delegation. The top ClassLoader in Android is BootClassLoader, followed by PathClassLoader. PathClassLoader is basically the same as DexClassLoader;

DexClassLoader inherits BaseDexClassLoader without any logic. It just calls the constructor of BaseDexClassLoader for initialization.

public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) { super(dexPath, null, librarySearchPath, parent); }}Copy the code

Source address: / libcore/dalvik/SRC/main/Java/dalvik/system/DexClassLoader. Java

PathClassLoader also inherits BaseDexClassLoader, which defines the same constructor as DexClassLoader;

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

Source address: / libcore/dalvik/SRC/main/Java/dalvik/system/PathClassLoader. Java

Therefore, the functions of PathClassLoader and DexClassLoader are basically the same. Both can be used to load Dex files.

PathClassLoader is officially used by Google. DexClassLoader is provided for developers. Use DexClassLoader as much as possible when using classloader.

Get the class loader

Call the following method in the Activity to get different levels of ClassLoader;

getClassLoader()
getClassLoader().getParent()
getClassLoader().getParent().getParent()
Copy the code

Code examples:

package kim.hsl.plugin; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG, "getClassLoader() : " + getClassLoader()); Log.i(TAG, "getClassLoader().getParent() : " + getClassLoader().getParent()); Log.i(TAG, "getClassLoader().getParent().getParent() : " + getClassLoader().getParent().getParent()); }}Copy the code

Print result:

I/MainActivity: getClassLoader() : dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/kim.hsl.plugin-_uowA-lCCTOuGQrqkuXOeg==/base.apk"],nativeLibraryDirectories=[/data/app/kim.hsl.plugin-_uowA-l CCTOuGQrqkuXOeg==/lib/arm64, /system/lib64]]] I/MainActivity: getClassLoader().getParent() : java.lang.BootClassLoader@fc7ec45 I/MainActivity: getClassLoader().getParent().getParent() : nullCopy the code

Results analysis:

Call the getClassLoader() method directly in the Activity to get the PathClassLoader,

Call getClassLoader().getparent () to get the BootClassLoader;

In addition, there is no higher ClassLoader, getClassLoader().getparent ().getparent ();

The parent class of BaseDexClassLoader is ClassLoader. ClassLoader is an abstract class. BootClassLoader is not involved in inheritance.

In ClassLoader, there is a ClassLoader parent field, which is passed in by the constructor. GetClassLoader ().getparent () is not a ClassLoader. Instead, the specified parent class references the ClassLoader parent field,

public class BaseDexClassLoader extends ClassLoader {
}
Copy the code

Source reference: / libcore/dalvik/SRC/main/Java/dalvik/system/BaseDexClassLoader. Java

public abstract class ClassLoader { // The parent class loader for delegation // Note: VM hardcoded the offset of this field, thus all new fields // must be added *after* it. private final ClassLoader parent; private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; }}Copy the code

Source reference: / libcore/ojluni/SRC/main/Java/Java/lang/this Java

4. Parent delegation mechanism

Class loader level: from high to low: BootClassLoader -> PathClassLoader/DexClassLoader;

Parent delegate mechanism:

MyClassLoader (); Student (); parent (); The class asks its parent PathClassLoader if the Student object has been loaded, and if not;

The BootClassLoader continues to ask the parent class whether the Student object has been loaded. If it has been loaded, the BootClassLoader returns the class object. If it has not been loaded, the BootClassLoader starts to delegate the subclass to load.

BootClassLoader assigns the subclass PathClassLoader to load the Student object, and the PathClassLoader assigns MyClassLoader to load the Student object, MyClassLoader finds that it has no subclasses and starts class loading Student;

In the loadClass method of ClassLoader, the loadClass method is called first

// First, check if the class has already been loaded Class<? > c = findLoadedClass(name);Copy the code

If not, check whether the parent class is empty. If not, call the loadClass method of the parent class.

if (parent ! = null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); }Copy the code

The parent class also calls the loadClass method of the parent class. If the call reaches the top level and there is no parent class, the load starts.

ClassLoader ClassLoader

public abstract class ClassLoader { protected Class<? > loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class<? > c = findLoadedClass(name); if (c == null) { try { if (parent ! = null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } return c; }}Copy the code

Source reference: / libcore/ojluni/SRC/main/Java/Java/lang/this Java