Completely solve the problem that the number of Android Application Methods cannot exceed 65K, android65k
Respect Originality:Http://blog.csdn.net/yuanzeyao/article/details/41809423
As an Android developer, I believe you should have heard of the limit on the number of Android methods that cannot exceed kb. As the application functions are constantly enriched, one day you will encounter an exception:
Conversion to Dalvik format failed: Unable toexecute dex: method ID not in [0, 0 xffff]: 65536
Some colleagues may say that it is very easy to solve this problem. We only need to configure one sentence in Project. proterty,
Dex. force. jumbo = true
Yes, with this sentence added, your application can be compiled, but it is easy to appear on some 2.3 system machines.
INSTALL_FAILED_DEXOPT exception
For the above two exceptions, let's analyze the causes:
1. In the Android system, the storage method id in a Dex file uses short data, so your dex method cannot exceed 65 KB.
2. Before the 2.3 system, the VM memory was allocated only 5 MB
Once we understand the cause, we will solve the above problems one by one. First, for the 65k problem, we cannot change the android system structure at the application layer, therefore, we cannot change the data type from short to int or another type. That is to say, the number of methods in a dex cannot exceed 65k, which is an insurmountable gap. We can only reduce the number of methods in a dex, first, the easiest way to think of is to remove some useless Jar packages and set some attributes to public, so that you can remove the get/set method. This method can only temporarily solve the problem, with the passage of time, there will still be more than K methods in one day. After all, an application is generally added with a function and will not be downgraded.
Next, I will introduce two mainstream solutions. One is representative of this solution, which makes some functions plug-ins and dynamically loads them, another solution is a subcontracting solution represented by facebook. It splits the dex file in an apk into multiple dex files and then dynamically loads the dex file. In fact, the core idea of these two solutions is the same. The plug-in will make the new functions to be developed in the future into apk and dex dynamic loading, the subcontracting solution divides the completed functions into multiple dex files for dynamic loading. In fact, I personally think the plug-in solution is better than the package solution to solve the problem of 65k, because the plug-in solution can not only solve the problem of K, but also reduce the size of our applications, while subcontracting can only solve the problem of K.
I wrote a long time ago about plug-in development and dynamic loading. If you are interested, please take a look.
Implementation of Android dynamic APK loading (Fragment or Activity implementation)
Http://blog.csdn.net/yuanzeyao/article/details/38565345
Next we will focus on the subcontracting Mechanism
We know that an apk file contains a dex file, which contains optimized class files. The so-called subcontracting means that a dex file is divided into multiple dex files, here we agree that the first dex is called main. dex, the second is second. dex. when subcontracting, we usually need to put the class that needs to be used for application startup into main. in dex, put the classes that are not immediately needed into second. in dex, for Android systems, it only loads main. dex, second. dex may be a resource file for him. It does not take the initiative to load second. dex, so when I start the application, we need to set it to second. dex creates a Class Loader so that I can use second. the class can be loaded in dex.
There are also many practices on how to load second. dex. There are several main methods to use.
1. The simplest way is to use DexClassLoader for loading and set the parent loader of the DexClassLoader to PathClassLoader.
2. Use DexClassLoader to load, set the parent loader of DexClassLoader to the parent loader of PathClassLoader, and set the parent loader of PahtClassLoader to DexClassLoader.
3. Put the second. dex path to the path of the PathClassLoader.
In the 2nd solution, one scenario cannot be used, for example, when second. dex is loaded through DexClassLoader, but second. dex uses a class in main. dex, this will throw an exception that the class cannot find, so this solution can only have second. dex does not use main. dex class
The above is all about theory. Let's take a look at it.
Here I will introduce two solutions: one is to build an Android project based on gradle, and the other is to build an Android project based on Ant.
Solution 1: Build an Android project based on gradle and implement Subcontracting
Environment requirements: AndroidStudio0.9 or above, and gradle plug-in 0.14.2 or above
1. If your project is in eclipse, You need to import the project to Android. At this time, you need to upgrade the project adt22 or above.
2. Open the build. gradle file of your project and check whether the gradle plug-in is version 0.14.2. After version 0.14.2, The gradle plug-in supports subcontracting.
3. Open the build. gradle file of a Moudle in the project and add the dependency on the android-support-multidex.jar.
4. Remove duplicate classes from third-party jar packages
5. Set the VM heap memory size to avoid OOM during compilation.
6. It seems that the so library will not be added to the project by default when gradle builds the project. To avoid this situation, we need to create the so library directory. For the project converted from eclipse, you also need to specify the src and resource file paths.
7. If your project depends on other libraries, add the multiDexEnabled = true and jniLibs. srcDirs = ['libs'] configurations to each library project.
8. If your project does not have a custom Application. use MultiDexApplication in xml. If your project has a custom Application and the inheritance is Application, you only need to inherit the MultiDexApplication. If your project inherits other applications, you need to rewrite
AttachBaseContext
@Overrideprotected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this);}
After the above configuration, your project should have been successfully subcontracted. If the package is successful, decompress your apk file and you will find two dex files. Through the above configuration process, we find that we cannot control which classes are in main. in dex, which classes are in second. dex can be compatible with API4-API20 by configuring subcontracting through this scheme. it loads second. dex uses 3 of the above scheme
Next, let's take a look at how to build an Android project based on Ant and implement the subcontracting process.
In the above scheme, because we cannot see the script of the gradle build project, we cannot control which classes are in the first dex and which classes are in the second dex. In this scheme, we use Ant construction. Ant allows users to define their own build schemes. For example, we can use a custom build scheme to put some third-party jar packages in the project into second. in dex, please refer to the open-source project for how to implement this
Https://github.com/mmin18/Dex65536.git
Because this project loads second. dex adopts the above scheme 2, such as second. some Third-Party jar packages in dex depend on main. some Classes in dex will be implemented in this scheme, so here I will remove this scheme and replace it with solution 3, that is, the second. dex path is set to the loading path of PathClassLoader.
I only provide solutions in Android 4.4. Other systems are similar.
Load second. dex Method
/** @ Param loader PathClassLoader @ additionalClassPathEntries the dex file to be loaded. Here is our second. dex @ optimizedDirectory is the directory for extracting dex files */private static void install (ClassLoader loader, List <File> delimiter, File optimizedDirectory) throws resume, IllegalAccessException, NoSuchFieldException, InvocationTargetException, noSuchMethodException {/* The patched class loader is expected to be a descendant of * dalvik. system. baseDexClassLoader. we modify its * dalvik. system. dexPathList pathList field to append additional DEX * file entries. * /// find the pathList Value Field pathListField = findField (loader, "pathList") through reflection; Object dexPathList = pathListField. get (loader); ArrayList <IOException> suppressedExceptions = new ArrayList <IOException> (); // set second. dex is added to the load path of PathClassLoader. expandFieldArray (dexPathList, "dexElements", makeDexElements (dexPathList, new ArrayList <File> (optional), optimizedDirectory, suppressedExceptions); if (optional. size ()> 0) {for (IOException e: suppressedExceptions) {Log. w (TAG, "Exception in makeDexElement", e);} Field suppressedExceptionsField = findField (loader, "handler"); IOException [] dexElementsSuppressedExceptions = (IOException []) suppressedExceptionsField. get (loader); if (dexElementsSuppressedExceptions = null) {dexElementsSuppressedExceptions = suppressedExceptions. toArray (new IOException [suppressedExceptions. size ()]);} else {IOException [] combined = new IOException [suppressedExceptions. size () + dexElementsSuppressedExceptions. length]; suppressedExceptions. toArray (combined); System. arraycopy (dexElementsSuppressedExceptions, 0, combined, suppressedExceptions. size (), dexElementsSuppressedExceptions. length); dexElementsSuppressedExceptions = combined;} suppressedExceptionsField. set (loader, dexElementsSuppressedExceptions );}}
After successful packet splitting, decompress the APK file and access the assertfolder. We can see that libs.apk is the dex file compiled by a third-party jar.
The root cause for the second problem INSTALL_FAILED_DEXOPT mentioned above is that the memory of the dalvik virtual machine is only 5 MB Before Version 2.3, therefore, plug-in or subcontract solutions still encounter this problem on some mobile phones. After all, we only reduce the number of packages in each dex, but the total number of methods is not reduced, therefore, the fundamental solution to this problem is to modify the VM memory to 8 MB, which cannot be implemented at the Java layer, but can be implemented at the c layer. For specific implementation procedures, refer to open-source projects:
Https://github.com/viilaismonster/LinearAllocFix.git
As for some of the methods used in this method, can be found in the android-support-multidex.jar, here is not all posted, if there is not clearly written, welcome to leave a message to discuss...