Android Plug-in Development Guide - Class Loader

1 Introduction

The class loader in Java was mentioned in the summary of JVM virtual machine knowledge points. We know that the loading of classes in Java is done by class loaders, which are usually provided by the JVM. These class loaders are also the basis for all previous programs to run. These class loaders provided by the JVM are usually called system class loaders. In addition, developers can create their own class loaders by inheriting from the ClassLoader base class. Class loaders can be divided into four categories:

  • Start class loader (Bootstrap ClassLoader); mainly responsible for loading JDK files in jre\lib\rt.jar.
  • Extension ClassLoader (Extension ClassLoader); mainly responsible for the Jar package files located in the jre\lib\ext directory.
  • Application ClassLoader (Application ClassLoader); responsible for loading related classes from the classpath environment variable, the application class loader is a subclass of the extended class loader. By default AppClassLoader is used to load application classes.
  • Custom class loader (User ClassLoader);

The relationship between them is shown in the figure:

insert image description here

 We know that unlike Java programs, dex files are used to package bytecode files in Android. The ClassLoader in Java loads jar files, so Android also has its own set of class loaders.

2. Android class loader

Similarly, Android can also be divided into two types: system class loader and custom class loader. There are also three types of system class loaders, namely:

  • BootClassLoader; when the system starts, it is used to load the common classes of the system, the internal class of ClassLoader;
  • PathClassLoader; loading system classes and application classes, generally not recommended for developers to use;
  • DexClassLoader; load the dex file and its apk or jar containing the dex file. It also supports loading from SD card, which means that dex related files can be loaded when the application is not installed.

Let's take a look at the inheritance structure diagram of these three class loaders:

insert image description here

 It can be seen from the above inheritance structure that there are actually many class loaders. Here we focus on the class loader DexClassLoader, because this class loader can load apk, dex and jar from the outside. View its source code in AS as:

package dalvik.system;

import java.io.File;

public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        super((String)null, (File)null, (String)null, (ClassLoader)null);
        throw new RuntimeException("Stub!");
    }
}

You can see that the package it belongs to is dalvik.system, and its parent class is BaseDexClassLoader. Four parameters need to be passed in:

  • dexPath: A collection of dex-related file paths, multiple paths are separated by a file separator, and the default file separator is ":";
  • optimizedDirectory: The decompressed dex file storage path, this path must be an internal storage path, such as the cache under the application;
  • librarySearchPath: A collection of paths containing C/C++ libraries, multiple paths are separated by file separators, and can be null;
  • parent: parent loader;

Similarly, the class loader in Android also follows the parent delegation mechanism, that is, when ClassLoader loads a class, it will first delegate its parent class parentLoader to load this class, and search up this tree. If there is no ClassLoader If this class can be loaded, it will be delegated downward until it is loaded by itself. The significance of this is for performance, each loading will consume time, but if the parent class has already been loaded, it can be used directly.

For example, define a simple test code:

public class OtherActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other);
        Log.e("TAG", "系统常用类String:" + String.class.getClassLoader().toString());
        Log.e("TAG", "应用程序自定义OtherActivity:" + OtherActivity.class.getClassLoader().toString());
        Log.e("TAG", "系统类Context:" + OtherActivity.this.getClassLoader().toString());
        Log.e("TAG", "系统常用类Context的ClassLoader的parent:" + getClassLoader().getParent().toString());
    }
}

The result is:

java.lang.BootClassLoader
dalvik.system.PathClassLoader
dalvik.system.PathClassLoader
java.lang.BootClassLoader

indicates that PathClassLoader is directly obtained by using getClassLoader in the current Activity, and its parent class is BootClassLoader. The ClassLoader for the classes commonly used in the system is BootClassLoader. The DexClassLoader needed to load the plugin does not appear here yet. Because the construction method of this class was introduced earlier, let's make a simple case here.

First prepare a dex or apk file. For example, I create a new module here and create a simple Toast tool class:

insert image description here
Then compile the project, switch the project view to project, and you can see the compiled files:

insert image description here 

 Then upload this file to the root directory of the SD card:

insert image description hereThen write the test method:

 

public class OtherActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other);


        File file = new File(Environment.getExternalStorageDirectory(), "plugin-debug.apk");
        DexClassLoader dexClassLoader = new DexClassLoader(
                file.getAbsolutePath(),
                getDir("cache_plugin", MODE_PRIVATE).getAbsolutePath(),
                null,
                getClassLoader()
        );
        try {
            Class<?> aClass = dexClassLoader.loadClass("com.weizu.plugin.ToastUtils");
            Method showInfo = aClass.getMethod("showInfo", Context.class);
            showInfo.setAccessible(true);
            showInfo.invoke(aClass.newInstance(), OtherActivity.this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Pay attention to the file access permissions, test results:

insert image description here

 

Guess you like

Origin blog.csdn.net/u013773608/article/details/130065130