关于突破google65k方法的限制,让你的应用不在爆棚

android项目的又一历史性的时刻,随着业务逻辑的增加,我们的代码量的增加,我们的应用爆棚了。我们代码的总的方法数已经超过了goole规定的65535个方法,导致了jvm不能编译通过我们的代码。下面是导致这个问题的具体原因;
        Android 系统中,一个 App 的所有代码都在一个 Dex 文件里面。 Dex 是一个类似Jar的存储了多有 Java 编译字节码的归档文件。因为 Android 系统使用 Dalvik 虚拟机,所以需要把使用 Java Compiler 编译之后的 class 文件转换成 Dalvik 能够执行的 class 文件。这里需要强调的是, Dex Jar 一样是一个归档文件,里面仍然是 Java 代码对应的字节码文件。当 Android 系统启动一个应用的时候,有一步是对 Dex 进行优化,这个过程有一个专门的工具来处理,叫 DexOpt DexOpt 的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个 ODEX 文件,即 Optimised Dex 。执行 ODex 的效率会比直接执行 Dex 文件的效率要高很多。但是在早期的Android系统中, DexOpt 有一个问题, DexOpt 会把每一个类的方法 id 检索起来,存在一个链表结构里面。但是这个链表的长度是用一个 short 类型来保存的,导致了方法 id 的数目不能够超过 65536 个。当一个项目足够大的时候,显然这个方法数的上限是不够的。尽管在新版本的 Android 系统中, DexOpt 修复了这个问题,但是我们仍然需要对低版本的 Android 系统做兼容.

    下面是google给的官方解决方案:

Building Apps with Over 65K Methods

In this document

  1. About the 65K Reference Limit
    1. Multidex support prior to Android 5.0
    2. Multidex support for Android 5.0 and higher
  2. Avoiding the 65K Limit
  3. Configuring Your App for Multidex with Gradle
    1. Limitations of the multidex support library
  4. Optimizing Multidex Development Builds
    1. Using Build Variants in Android Studio
  5. Testing Multidex Apps

See Also

  1. ProGuard

As the Android platform has continued to grow, so has the size of Android apps. When your application and the libraries it references reach a certain size, you encounter build errors that indicate your app has reached a limit of the Android app build architecture. Earlier versions of the build system report this error as follows:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

More recent versions of the Android build system display a different error, which is an indication of the same problem:

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

Both these error conditions display a common number: 65,536. This number is significant in that it represents the total number of references that can be invoked by the code within a single Dalvik Executable (dex) bytecode file. If you have built an Android app and received this error, then congratulations, you have a lot of code! This document explains how to move past this limitation and continue building your app.

Note: The guidance provided in this document supersedes the guidance given in the Android Developers blog postCustom Class Loading in Dalvik.

About the 65K Reference Limit


Android application (APK) files contain executable bytecode files in the form of Dalvik Executable (DEX) files, which contain the compiled code used to run your app. The Dalvik Executable specification limits the total number of methods that can be referenced within a single DEX file to 65,536, including Android framework methods, library methods, and methods in your own code. Getting past this limit requires that you configure your app build process to generate more than one DEX file, known as a multidex configuration.

Multidex support prior to Android 5.0

Versions of the platform prior to Android 5.0 use the Dalvik runtime for executing app code. By default, Dalvik limits apps to a single classes.dex bytecode file per APK. In order to get around this limitation, you can use the multidex support library, which becomes part of the primary DEX file of your app and then manages access to the additional DEX files and the code they contain.

Multidex support for Android 5.0 and higher

Android 5.0 and higher uses a runtime called ART which natively supports loading multiple dex files from application APK files. ART performs pre-compilation at application install time which scans for classes(..N).dex files and compiles them into a single .oat file for execution by the Android device. For more information on the Android 5.0 runtime, see Introducing ART.

Avoiding the 65K Limit


Before configuring your app to enable use of 65K or more method references, you should take steps to reduce the total number of references called by your app code, including methods defined by your app code or included libraries. The following strategies can help you avoid hitting the dex reference limit:

  • Review your app's direct and transitive dependencies - Ensure any large library dependency you include in your app is used in a manner that outweighs the amount of code being added to the application. A common anti-pattern is to include a very large library because a few utility methods were useful. Reducing your app code dependencies can often help you avoid the dex reference limit.
  • Remove unused code with ProGuard - Configure the ProGuard settings for your app to run ProGuard and ensure you have shrinking enabled for release builds. Enabling shrinking ensures you are not shipping unused code with your APKs.

Using these techniques can help you avoid the build configuration changes required to enable more method references in your app. These steps can also decrease the size of your APKs, which is particularly important for markets where bandwidth costs are high.

Configuring Your App for Multidex with Gradle


The Android plugin for Gradle available in Android SDK Build Tools 21.1 and higher supports multidex as part of your build configuration. Make sure you update the Android SDK Build Tools tools and the Android Support Repository to the latest version using the SDK Manager before attempting to configure your app for multidex.

Setting up your app development project to use a multidex configuration requires that you make a few modifications to your app development project. In particular you need to perform the following steps:

  • Change your Gradle build configuration to enable multidex
  • Modify your manifest to reference the MultiDexApplication class

Modify your app Gradle build file configuration to include the support library and enable multidex output, as shown in the following Gradle build file snippet:

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.0"

    defaultConfig {
        ...
        minSdkVersion 14
        targetSdkVersion 21
        ...

        // Enabling multidex support.
        multiDexEnabled true
    }
    ...
}

dependencies {
  compile 'com.android.support:multidex:1.0.0'
}

Note: You can specify the multiDexEnabled setting in the defaultConfig, buildType, or productFlavor sections of your Gradle build file.

In your manifest add the MultiDexApplication class from the multidex support library to the application element.

xml version="1.0" encoding="utf-8"?>
 xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.multidex.myapplication">
    
        ...
        android:name="android.support.multidex.MultiDexApplication">
        ...
    

When these configuration settings are added to an app, the Android build tools construct a primary dex (classes.dex) and supporting (classes2.dex, classes3.dex) as needed. The build system will then package them into an APK file for distribution.

Note: If your app uses extends the Application class, you can override the attachBaseContext() method and call MultiDex.install(this) to enable multidex. For more information, see the MultiDexApplication reference documentation.

Limitations of the multidex support library

The multidex support library has some known limitations that you should be aware of and test for when you incorporate it into your app build configuration:

  • The installation of .dex files during startup onto a device's data partition is complex and can result in Application Not Responding (ANR) errors if the secondary dex files are large. In this case, you should apply code shrinking techniques with ProGuard to minimize the size of dex files and remove unused portions of code.
  • Applications that use multidex may not start on devices that run versions of the platform earlier than Android 4.0 (API level 14) due to a Dalvik linearAlloc bug (Issue 22586). If you are targeting API levels earlier than 14, make sure to perform testing with these versions of the platform as your application can have issues at startup or when particular groups of classes are loaded. Code shrinking can reduce or possibly eliminate these potential issues.
  • Applications using a multidex configuration that make very large memory allocation requests may crash during run time due to a Dalvik linearAlloc limit (Issue 78035). The allocation limit was increased in Android 4.0 (API level 14), but apps may still run into this limit on Android versions prior to Android 5.0 (API level 21).
  • There are complex requirements regarding what classes are needed in the primary dex file when executing in the Dalvik runtime. The Android build tooling updates handle the Android requirements, but it is possible that other included libraries have additional dependency requirements including the use of introspection or invocation of Java methods from native code. Some libraries may not be able to be used until the multidex build tools are updated to allow you to specify classes that must be included in the primary dex file.

Optimizing Multidex Development Builds


A multidex configuration requires significantly increased build processing time because the build system must make complex decisions about what classes must be included in the primary DEX file and what classes can be included in secondary DEX files. This means that routine builds performed as part of the development process with multidex typically take longer and can potentially slow your development process.

In order to mitigate the typically longer build times for multidex output, you should create two variations on your build output using the Android plugin for Gradle productFlavors: a development flavor and a production flavor.

For the development flavor, set a minimum SDK version of 21. This setting generates multidex output much faster using the ART-supported format. For the release flavor, set a minimum SDK version which matches your actual minimum support level. This setting generates a multidex APK that is compatible with more devices, but takes longer to build.

The following build configuration sample demonstrates the how to set up these flavors in a Gradle build file:

android {
    productFlavors {
        // Define separate dev and prod product flavors.
        dev {
            // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
            // to pre-dex each module and produce an APK that can be tested on
            // Android Lollipop without time consuming dex merging processes.
            minSdkVersion 21
        }
        prod {
            // The actual minSdkVersion for the application.
            minSdkVersion 14
        }
    }
          ...
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                                                 'proguard-rules.pro'
        }
    }
}
dependencies {
  compile 'com.android.support:multidex:1.0.0'
}

After you have completed this configuration change, you can use the devDebug variant of your app, which combines the attributes of the dev productFlavor and the debug buildType. Using this target creates a debug app with proguard disabled, multidex enabled, and minSdkVersion set to Android API level 21. These settings cause the Android gradle plugin to do the following:

  1. Build each module of the application (including dependencies) as separate dex files. This is commonly referred to as pre-dexing.
  2. Include each dex file in the APK without modification.
  3. Most importantly, the module dex files will not be combined, and so the long-running calculation to determine the contents of the primary dex file is avoided.

These settings result in fast, incremental builds, because only the dex files of modified modules are recomputed and repackaged into the APK file. The APK that results from these builds can be used to test on Android 5.0 devices only. However, by implementing the configuration as a flavor, you preserve the ability to perform normal builds with the release-appropriate minimum SDK level and proguard settings.

You can also build the other variants, including a prodDebug variant build, which takes longer to build, but can be used for testing outside of development. Within the configuration shown, the prodRelease variant would be the final testing and release version. If you are executing gradle tasks from the command line, you can use standard commands withDevDebug appended to the end (such as ./gradlew installDevDebug). For more information about using flavors with Gradle tasks, see the Gradle Plugin User Guide.

Tip: You can also provide a custom manifest, or a custom application class for each flavor, allowing you to use the support library MultiDexApplication class, or calling MultiDex.install() only for the variants that need it.

Using Build Variants in Android Studio

Build variants can be very useful for managing the build process when using multidex. Android Studio allows you to select these build variants in the user interface.

To have Android Studio build the "devDebug" variant of your app:

  1. Open the Build Variants window from the left-sidebar. The option is located next to Favorites.
  2. Click the name of the build variant to select a different variant, as shown in Figure 1.

Figure 1. Screen shot of the Android Studio left panel showing a build variant.

Note: The option to open this window is only available after you have successfully synchronized Android Studio with your Gradle build file using the Tools > Android > Sync Project with Gradle Files command.

Testing Multidex Apps


When using instrumentation tests with multidex apps, additional configuration is required to enable the test instrumentation. Because the location of code for classes in multidex apps is not within a single DEX file, instrumentation tests do not run properly unless configured for multidex.

To test a multidex app with instrumentation tests, configure the MultiDexTestRunner from the multidex testing support library. The following sample build.gradle file demonstrates how to configure your build to use this test runner:

android {
  defaultConfig {
      ...
      testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner"
  }
}

Note: With Android Plugin for Gradle versions lower than 1.1, you need to add the following dependency for multidex-instrumentation:

dependencies {
    androidTestCompile('com.android.support:multidex-instrumentation:1.0.1') {
         exclude group: 'com.android.support', module: 'multidex'
    }       
}

You may use the instrumentation test runner class directly or extend it to fit your testing needs. Alternatively, you can override onCreate in existing instrumentations like this:

public void onCreate(Bundle arguments) {
    MultiDex.install(getTargetContext());
    super.onCreate(arguments);
    ...
}

Note: Use of multidex for creating a test APK is not currently supported

          接下来就是采用了MutiDexApplication或者覆写了Application中的attachBaseContext()方法之后所做的限制。Application 中的静态全局变量会比MutiDex instal()方法优先加载,所以建议避免在Application类中使用main classes.dex文件中可能使用到的静态变量.

心得和体会,这个问题是每一个大的应用都会发生的事情,随着业务量的增加,应用工程的庞大,那么google这个限制我们迟早会遇到。

具体操作步骤

1.在build.gradle  中添加脚本,

2.在build->support导入相关包

3.application继承MutiDexApplication

你可能感兴趣的:(关于突破google65k方法的限制,让你的应用不在爆棚)