Android dynamic loading bytecode and android dynamic bytecode
Overview
In the face of frequent changes to App business logic, if the App is upgraded once for each change, it will reduce the user experience of the App, then the App performs a modular upgrade (this is different from the incremental upgrade) is a good solution, allowing users to change the business logic in the App without feeling at all. To implement this modular upgrade, dynamic loading of bytecode (jar/dex) is the theoretical basis for achieving this requirement.
Android system loads bytecode
The Android Virtual Machine (Dalvik VM) cannot identify the bytecode in a common jar package. Therefore, you need to use the bytecode Conversion Tool to convert the jar into dex. All the bytecode in the jar package is classified into classes. dex. This bytecode can be recognized by the Dalvik virtual machine.
Instance description
To download the source code, you need to prepare two projects: An Android Project (AndroidPractice) and an Android project to load bytecode. An ordinary java Project (DexModule) outside China. Let's take a look at the engineering structure. Note that the IDynamicLoad interface is required in both projects and has the same package name.
Bytecode loading Engineering Structure
DexModule Project Description
There are only two classes in the DexModule project. One is the interface IDynamicLoad, which is the key to the module upgrade mentioned in the article. You only need to specify the interface in advance and publish the interface to the caller, the specific implementation is completely transparent to the caller, so that the implementation of the interface can be changed at will, preferably the implementation class DynamicLoad of the interface. A simple implementation is to return a string.
package com.vjson.module;public interface IDynamicLoad { public String dexLoad();}
<span style="font-weight: normal;">package com.vjson.module;public class DynamicLoad implements IDynamicLoad { @Override public String dexLoad() { return "dexload practice"; }}</span>
Export jar package
When exporting a jar package, be sure not to export the IDynamicLoad interface file. Because an interface file already exists in the Android project, otherwise the bytecode may conflict when loading the file.
Export jar package
Bytecode Conversion
Use the bytecode Conversion Tool mentioned above to convert jar to the dex recognized by Dalvik VM. The-o parameter of the d2j-jar2dex.sh command specifies the output file.
d2j-jar2dex.sh -o module.jar dynamicLoad.jar
Then, place the generated module. jar in the mobile phone's sdcard.
adb push module.jar /mnt/sdcard/
Load dex
The DexClassLoader class is used to load bytecode. It is used to extract (extract) classes from the jar package. dex, and load the bytecode to the memory. Next, load the required class using the loadClass method. Check the detailed code below and pay attention to the highlighted lines.
package com.vjson.practice;import java.io.File;import android.annotation.TargetApi;import android.app.Activity;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.Toast;import com.vjson.dynamicload.R;import com.vjson.module.IDynamicLoad;import dalvik.system.DexClassLoader;public class MainActivity extends Activity { private Button mBtn; private IDynamicLoad mDynamicLoad; @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public static final IDynamicLoad loadByteCode() { File jarFile = new File(Environment.getExternalStorageDirectory().toString() + File.separator + "module.jar"); File optmizedPath = BaseApplication.sInstance.getDir("dex", MODE_PRIVATE); DexClassLoader loader = new DexClassLoader(jarFile.getAbsolutePath(), optmizedPath.getAbsolutePath(), null, BaseApplication.sInstance.getClassLoader()); IDynamicLoad dynaicLoad = null; try { Class<?> clazz = loader.loadClass("com.vjson.module.DynamicLoad"); dynaicLoad = (IDynamicLoad) clazz.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return dynaicLoad; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDynamicLoad = loadByteCode(); mBtn = (Button) findViewById(R.id.btn); mBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String str = mDynamicLoad.dexLoad(); Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show(); } }); }}
Note that 26 lines of code can be used to obtain the private directory of the application through the getDir ("dex", MODE_PRIVATE) method of context. This is due to security reasons starting from Android4.1.2 to prevent code injection attacks, the bytecode must be placed in the private directory, that is, the data/application package name /. The classes. dex that comes out of the jar package is placed under this directory.
Summary
This article mainly introduces the bytecode loading technology in Android, which provides a theoretical basis for the subsequent Android modular upgrade. The essence of this technology is to define interfaces, communicates with the Implementation end through the interface caller. The integrity verification and security verification of the jar package will be explained in the modular upgrade.