Introduction:
This chapter mainly explains that CrashHandler is used to monitor App crash information and solve the problem that the number of Android methods exceeds 65536 through Google’s multiDex solution. Android dynamically loads dex and decompiles.
The main content
- Use CrashHandler to obtain the crash information of the application
- Use multidex to resolve method count overbounds
- Android dynamic loading technology
- Preliminary decompilation
The specific content
Use CrashHandler to obtain the crash information of the application
How do I detect crashes and learn about detailed crash information? The first step is to implement an uncaughtExceptionHandler object, retrieve the exception information in its uncaughtException method and store it on an SD card or upload it to the server. Then the Thread setDefaultUncaughtExceptionHandler set exception handler for the current process of all threads.
CrashHandler source
We set up a CrashHandler for the thread during Application initialization so that Crash will handle exceptions through our own exception handler.
public class BaseApplication extends Application {
@Override
public void onCreate(a) {
super.onCreate();
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(this); }}Copy the code
Use multidex to resolve method count overbounds
The maximum number of methods that can be contained in a single dex file in Android is 65536, which includes all methods in the FrameWork, dependent JAR packages, and application code itself. Will be:
com.android.dex.DexIndexOverflowException: method ID not in[0.0xffff] :65536
Copy the code
It is possible that on some of the lower versions of the phone, errors occur even when the method limit is not exceeded.
E/dalvikvm: Optimization failed
E/installd: dexopt failed on '/data/dalvik-cache/..... '
Copy the code
First of all, Dexpot is a program. When the application is installed, the system will optimize the DEX file through Dexopt. During the optimization process, Dexopt adopts a buffer of fixed size to store all method messages in the application. This buffer is called linearAlloc. The linearAlloc buffer is 8MB or 16MB in newer versions of Android. In Android 2.2 and 2.3, it is only 5MB. This is because if there are too many methods, even if the number of methods does not exceed 65535, you may not be able to install due to storage space failure.
Solution:
- Plug-in: it is a heavyweight technical solution. By splitting a DEX into two or more dex, the problem of method number crossing can be solved to a certain extent. But there are compatibility issues to consider, so you need to weigh whether or not you want to use this solution.
- Multidex: This is a solution Google came up with in 2014. The android-support-multidex.jar provided by Google needs to be introduced before Android5.0; Multidex is supported by default from 5.0 onwards. It can load multiple dex files from apK files.
Use steps:
- Modify the build.gradle file in the corresponding project directory and add multiDexEnabled in defaultConfig
True Indicates the configuration item. 2. In the build. Gradle dependence on adding multidex dependencies: compile ‘com. Android. Support: multidex: # 1.0.0’ 3. Add support for multidex in the code. The first option is to specify Application as MultiDexApplication in the manifest file. The second option is to have the Application inherit from MultiDexApplication. The third option is to override the attachBaseContext method, which is executed before onCreate.
public class BaseApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this); }}Copy the code
Using the above configuration items, Gradle will not generate multiple dex files if the number of methods in the application does not exceed the threshold. When the number of methods exceeds the threshold, Gradle will pack two or more dex files in apK. When you need to specify the classes contained in the main dex file, use the -multi-dex-list option to implement this function.
// Add to the build.gradle file in the corresponding project directory
afterEvaluate {
println "afterEvaluate"
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
def listFile = project.rootDir.absolutePath + '/app/maindexlist.txt'
println "root dir:" + project.rootDir.absolutePath
println "dex task found: " + dx.name
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex'
dx.additionalParameters += '--main-dex-list=' + listFile
dx.additionalParameters += '--minimal-main-dex'}}Copy the code
maindexlist.txt
com/ryg/multidextest/TestApplication.class
com/ryg/multidextest/MainActivity.class
// Multidex These nine classes must be in the main Dex
android/support/multidex/MultiDex.class
android/support/multidex/MultiDexApplication.class
android/support/multidex/MultiDexExtractor.class
android/support/multidex/MultiDexExtractor$1.class
android/support/multidex/MultiDex$V4.class
android/support/multidex/MultiDex$V14.class
android/support/multidex/MultiDex$V19.class
android/support/multidex/ZipUtil.class
android/support/multidex/ZipUtil$CentralDirectory.class
Copy the code
Note that the nine classes in the Jar of Multidex must be packaged into the main dex, because multidex is required in the attachBaseContext method of Application. Install (this) requires multidex.
Disadvantages of Multidex:
- The startup speed slows down. The extra dex file is loaded when the application is started, which slows down the startup speed of the application or even generates ANR.
- Due to the bug of Dalvik linearAlloc, applications using multidex cannot run on phones before Android4.0, requiring extensive compatibility testing.
Android dynamic loading technology
Dynamic loading is also called plug-in. As your project grows larger, you can reduce your application’s memory and CPU footprint by plug-ins. You can also hot-plug, which means you can update certain modules without releasing a new version.
Learn about the author’s open source framework for plug-ins: dynamic-load-APk
Each plug-in solution needs to solve three fundamental problems
The concept of host and plug-in: Host refers to ordinary APK, while plug-in generally refers to processed DEX or APK. In the mainstream plug-in framework, apK is used as a plug-in after processing. The processing method is often related to compilation and packaging. In addition, many plug-in frameworks need to use the concept of proxy Activity, and most of the plug-in Activity starts with a proxy Activity to achieve.
Any resource file starting with R in the resource access plug-in is not accessible. The Activity is primarily done with ContextImpl, which has a mBase member variable of type ContextImpl. Context has two abstract methods to getResources, getAsssets() and getResources(); Simply implementing these two methods solves the resource problem.
protected void loadResources(a) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, mDexPath);
mAssetManager = assetManager;
} catch (Exception e) {
e.printStackTrace();
}
Resources superRes = super.getResources();
mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(),
superRes.getConfiguration());
mTheme = mResources.newTheme();
mTheme.setTo(super.getTheme());
}
Copy the code
From the loadResources() implementation, the method of loading Resources is reflection. By calling the addAssetPath method of the AssetManager, we can load a resource from an APK into the Resources object. The path passed can be zip or resource directory, so pass apK’s path directly to it and the resource is loaded into AssetManager. A new Resources object is then created through the AssetManager to access the Resources in the plug-in APK.
We then implement getAssets() and getResources() in the proxy Activity. Refer to the author’s plug-in open source framework for proxy activities.
@Override
public AssetManager getAssets(a) {
return mAssetManager == null ? super.getAssets() : mAssetManager;
}
@Override
public Resources getResources(a) {
return mResources == null ? super.getResources() : mResources;
}
Copy the code
After the apK is called by the host program, the Activity in APK is actually an ordinary object, which does not have the nature of an Activity, because the system starts an Activity and has to do a lot of initialization work. However, it is difficult for us to complete the initialization work done by the system by starting the activity through reflection in the application layer, so most features of the activity are not available, including the activity lifecycle management, which requires us to manage.
Refer to the author’s introduction to the Open Source framework for details.
ClassLoader management to avoid type conversion errors caused by multiple ClassLoaders loading the same class. Store the classloaders of different plug-ins in a HashMap.
Preliminary decompilation
- Decompilate APK using Dex2JAR and JD-GUI
- Use apkTool for secondary packaging of APK
The above online information is particularly much, do not repeat.