Study on dynamic loading mechanism in Android

In the current hardware and software environment, native app and web app have obvious advantages in user experience, but in actual projects, some clients will be upgraded frequently due to frequent business changes, resulting in poor user experience, which is precisely the advantage of web app. This paper combs and practices the data of dynamic loading jar of Android on the Internet, and shares it with you here, trying to improve the disadvantage of frequent upgrade.

Android application development in general, conventional development methods and code architecture can meet our common needs. But some special problems often lead us to further meditation. We generate epiphany from meditation, resulting in new forms of technology.

How to develop an Android application that can customize controls? Just like eclipse, plug-ins can be loaded dynamically; How to make Android applications execute unpredictable code on the server? How to encrypt Android applications and only self decrypt them during execution, so as to prevent them from being cracked

Friends familiar with Java technology may realize that we need to use class loader to flexibly load and execute classes. This is a relatively mature technology in Java, but most of us are still very strange in Android.

Class loading mechanism

Like other Java virtual machines, Dalvik virtual machine first needs to load the corresponding classes into memory when running the program. In the Java standard virtual machine, class loading can be read from the class file or other forms of binary stream. Therefore, we often use this to manually load class when the program is running, so as to achieve the purpose of dynamic code loading and execution

However, Dalvik virtual machine is not a standard Java virtual machine after all, so they have the same place and differences in class loading mechanism. We must treat them differently

For example, when using a standard Java virtual machine, we often customize the class loader inherited from classloader. The class is then loaded from a binary stream through the defineclass method. However, this is not feasible in Android, so there is no need to take detours. Referring to the source code, we know that the defineclass method of classloader in Android specifically calls the defineclass local static method of vmclassloader. The local method does nothing except throw an "unsupported operationexception", and even the return value is empty

Dalvik virtual machine class loading mechanism

If classloader doesn't work well in Dalvik virtual machine, how can we dynamically load classes? Android has derived two classes from classloader for us: dexclassloader and PathClassLoader. In particular, a commented out code in PathClassLoader:

On the other hand, this proves that the defineclass function is indeed castrated in the Dalvik virtual machine. The two class loaders inherited from classloader essentially overload the findclass method of classloader. When executing loadclass, we can refer to the source code of classloader:

Therefore, both dexclassloader and PathClassLoader belong to class loaders that comply with the parental delegation model (because they do not overload the loadclass method). That is, before loading a class, they go back and check whether they and their class loaders have loaded the class. If it has been loaded, it will be returned directly without repeated loading.

Dexclassloader and PathClassLoader actually load classes through the dexfile class. Incidentally, the Dalvik virtual machine recognizes the DEX file, not the class file. Therefore, the files we load for classes can only be DEX files or files containing DEX files Apk or Jar file.

Some people may think that since dexfile can load classes directly, why should we use subclasses of classloader? When dexfile loads a class, it specifically calls the member method loadclass or loadclassbinaryname. Where loadclassbinaryname needs to add "." in the class name containing the package name Convert to "/" let's take a look at the loadclass Code:

There is a comment before this code, which intercepts the key part, that is, if you are not calling this from a class loader, this is most likely not going to do what you want Use {@link Class#forName(String)} instead. This is why we need to use the classloader subclass. As to how it verifies whether this method is invoked in ClassLoader, I have not studied it. If you are interested, you can go further.

There is a detail that may not be easy to notice. PathClassLoader generates a dexfile object through the constructor new dexfile (path); The dexclassloader obtains the dexfile object through its static method loaddex (path, outpath, 0). The difference between the two is that dexclassloader needs to provide a writable outpath path to release Apk package or The DEX file in the jar package. In other words, PathClassLoader cannot actively release DEX from the zip package, so it only supports direct operation of DEX format files or installed apks (because the installed apks have cached DEX files in the cache). Dexclassloader can support apk、. Jar and DEX file, and the DEX file will be released in the specified outpath path path.

In addition, when loading classes, PathClassLoader calls loadclassbinaryname of dexfile, while dexclassloader calls loadclass. Therefore, when using PathClassLoader, the full class name needs to be replaced with "/"

Actual operation

The tools used are quite conventional: javac, DX, eclipse, etc. among them, the DX tool is better to indicate -- no strict, because the path of the class file may not match

After loading the class, we can usually use the class through the java reflection mechanism, but this is not efficient, and the old reflection code is complex and messy. A better approach is to define an interface and write it into the container side. The class to be loaded inherits from this interface and has a constructor with empty parameters, so that we can generate objects through the newinstance method of class, and then forcibly convert the objects into interface objects, so we can directly call member methods. The following are the specific implementation steps:

Step 1:

Write dynamic code classes:

The implementation class code is as follows:

In this way, dynamic classes are developed

Step 2:

The dynamic classes developed above are packaged into Jar. Note here that only the implementation class dynamic. Jar is packaged Java, do not package the interface class iddynamic java,

Then copy the packaged jar file to the platform tools directory in the Android installation directory, and use the DX command: (my jar file is dynamic. Jar)

dx --dex --output=dynamic_ temp. jar dynamic. jar

This generates dynamic_ temp. Jar, this jar and dynamic What's the difference between jars?

In fact, the main work of this command is: first, set dynamic Jar is compiled into dynamic DEX file (bytecode file recognized by Android virtual machine), and then put dynamic The DEX file is compressed into dynamic_ temp. Jar, of course, you can also compress it into ZIP format, or directly compiled into Apk files are OK, which will be mentioned later.

It's not finished here, because what do you want to use to connect the dynamic class and the target class? That's the interface of dynamic classes, so I have to make a call at this time Jar package. At this time, you only need to type the interface class iddynamic Java

Then put this The jar file is referenced into the target class. Let's take a look at the implementation of the target class:

An iddynamic interface variable is defined, and dexclassloader and PathClassLoader are used to load classes. Let's talk about it first

As mentioned above, dexclassloader inherits the classloader class. The parameter description is as follows:

The first parameter is the path of the DEX compressed file: This is the dynamic file we compiled above_ temp. The directory where jar is stored, of course, can also be Zip and Apk format

The second parameter is: the directory where DEX is stored after decompression: This is the directory where it will be stored jar,. zip,. The directory where the DEX file extracted from the APK file is stored is different from the PathClassLoader method. At the same time, you can see that there is no parameter in the PathClassLoader method. This is really the difference between the two classes:

PathClassLoader cannot actively release DEX from the zip package, so it only supports direct operation of DEX format files or installed apks (because the installed apks have cached DEX files in the data / Dalvik directory of the phone). Dexclassloader can support apk、. Jar and DEX file, and the DEX file will be released in the specified outpath path path.

However, we can specify the storage directory of the decompressed DEX file through the dexclassloader method, but we generally do not do so, because this undoubtedly exposes the DEX file, so we generally do not jar/. zip/. Apk compressed files are stored in a place where users can notice, and the directory where DEX is decompressed cannot be seen by users.

The third and fourth parameters are not used very much, so we won't explain them here.

It should also be noted that when using the PathClassLoader method, the first parameter is the path where DEX is stored. Here, the following is passed:

The specified APK installation path. This value can only be obtained in this way, otherwise class loading will fail

Step 3:

Run target class:

The work to be done is:

If you use the dexclassloader method to load classes: you need to Jar or Zip or Apk files are placed in the specified directory. I put them in the root directory of SD card for convenience

If the PathClassLoader method is used to load the class: in this case, you need to first set dynamic Install APK into the mobile phone, otherwise the activity cannot be found. At the same time, note:

Here's com dynamic. Impl is an action that needs to be defined in the specified APK. This name is agreed between the dynamic APK and the target APK

Click showbanner to display a toast and successfully run the code in the dynamic class!

In fact, a better way is to use dynamic jar. zip. Apk files are obtained from the network, which is safe and reliable. At the same time, the local target project can execute different logic without changing the code

Some ideas about code encryption

Initially, it was envisaged to encrypt the DEX file, and then write the decryption code in the native layer through JNI. After decryption, it is directly transmitted to the binary stream, and then the class is loaded into memory through defineclass.

You can also do this now, but because you can't use defineclass directly, you must pass the file path to the Dalvik virtual machine kernel. Therefore, the decrypted file needs to be written to the disk, increasing the risk of cracking.

The way that the Dalvik virtual machine kernel only supports loading classes from DEX files is not flexible. Because there is no very in-depth study of the kernel, I can't determine whether the Dalvik virtual machine itself does not support it or Android castrated it during transplantation. However, I believe that Dalvik or Android open source projects are working towards supporting raw data definition classes.

We can see in the document that Google says: jar or APK file with "classes. DEX" (May expand this to include "raw DEX" in the future.); We can also see the figure of rawdexfile in the Dalvik source code of Android (but there is no specific implementation)

Before rawdexfile came out, we can only use this encryption method with certain risks. Pay attention to the path and permission management of the released DEX file. In addition, after loading the class, the temporary decryption file should be deleted immediately unless for other purposes.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>