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:

  1. 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:

  1. 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.
  2. 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

  1. Decompilate APK using Dex2JAR and JD-GUI
  2. Use apkTool for secondary packaging of APK

The above online information is particularly much, do not repeat.