关于Android方法数量限制的问题

限制Android方法数量的原因是:


Android应用以DEX文件的形式存储字节码文件,在Dalvik字节码规范里,方法引用索引method referenceindex只有16,65536个。

 

Op & Format

Mnemonic / Syntax

Arguments

6e..72 35c

invoke-kind {vC, vD, vE, vF, vG}, meth@BBBB

B: method reference index (16 bits)

 

 

注意是method reference,这里限制的是自己代码、Android框架、第三方库三者方法数量的总和。

 

dex里方法只能索引65536个,但是报错不在这里。Android打包dex的过程如下:


//Main.Java

-> main() -> run() ->不分包执行runMonoDex()(或者分包执行runMultiDex())-> writeDex()

// DexFile

->toDex() -> toDex0()

// MethodIdsSection extends MemberIdsSection extends UniformItemSection extends  Section

->Section 的prepare() -> UniformItemSection的prepare0() ->MemberIdsSection的orderItems() -> getTooManyMembersMessage()

//Main.java

->getTooManyIdsErrorMessage()

 

MemberIdsSection里执行了这么一段代码

 protected void orderItems() {

        int idx = 0;

 

        if (items().size() >DexFormat.MAX_MEMBER_IDX + 1) {

            throw newDexIndexOverflowException(getTooManyMembersMessage());

        }

 

        for (Object i : items()) {

            ((MemberIdItem) i).setIndex(idx);

            idx++;

        }

    }

getTooManyMembersMessage核心代码如下:构建了错误信息字符串。

private String getTooManyMembersMessage() {

             try {

            String memberType = this instanceofMethodIdsSection ? "method" : "field";

            formatter.format("Too many %s references:%d; max is %d.%n" +

                   Main.getTooManyIdsErrorMessage() + "%n" +

                    "References bypackage:",

                    memberType, items().size(),DexFormat.MAX_MEMBER_IDX + 1);

            return formatter.toString();

        }

    }

}

 

可见代码里检测了方法数量的上限,超过就会报错。这里的限制DexFormat.MAX_MEMBER_IDX

出自

public final classDexFormat {

  /**

     * Maximum addressable field or methodindex.

     * The largest addressable member is0xffff, in the "instruction formats" spec as field@CCCC or

     * meth@CCCC.

     */

    public static final int MAX_MEMBER_IDX =0xFFFF;

}

因此方法不能超过65536。这里的根源还是在dex字节码规范里方法引用索引的限制,但是强制报错是在这里限制的。

另外,域的数量也有此限制。

 

错误信息:

Error:Errorconverting bytecode to dex:

Cause:com.android.dex.DexIndexOverflowException:field ID not in [0, 0xffff]: 65536(笔者开发时遇到的是域数量问题,方法数量问题错误信息于此类似)

:XXXXXX:transformClassesWithDexForDebug FAILED

Error:Executionfailed for task ':XXXXXX:transformClassesWithDexForDebug'.

>com.android.build.api.transform.TransformException:com.android.ide.common.process.ProcessException:java.util.concurrent.ExecutionException:com.android.ide.common.process.ProcessException:org.gradle.process.internal.ExecException: Process 'command 'C:\ProgramFiles\Java\jdk1.8.0_73\bin\java.exe'' finished withnon-zero exit value 2

解决方法:

Google官方出的分包方案,采用MultiDex支持库

1,配置building.gradle,开启MultiDex

  

android {

 

    defaultConfig {

        multiDexEnabled true

    }

}

dependencies{

    compile'com.android.support:multidex:1.0.0'

}

 

2,配置应用

方法1:在AndroidManifest.xml的application中声明android.support.multidex.MultiDexApplication;

<?xmlversion="1.0" encoding="utf-8"?>

<manifestxmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.android.multidex.myapplication">

    <application

        ...

       android:name="android.support.multidex.MultiDexApplication">

        ...

    </application>

</manifest>

方法2让你自己的Application类继承MultiDexApplication

方法3:让你自己的Application类重写attachBaseContext 方法,

@Override

protected voidattachBaseContext(Context base) {

super.attachBaseContext(base);

MultiDex.install(this);

}

 

其他错误情况:

Error:java.lang.OutOfMemoryError: Java heap space.

Please assign morememory to Gradle in the project's gradle.properties file.

For example, thefollowing line, in the gradle.properties file, sets the maximum Java heap sizeto 1,024 MB:

<em>org.gradle.jvmargs=-Xmx1024m</em>

:XXXXXX:transformClassesWithDexForDebugFAILED

Error:Executionfailed for task ':XXXXXX:transformClassesWithDexForDebug'.

>com.android.build.api.transform.TransformException: java.lang.RuntimeException:com.android.ide.common.process.ProcessException:java.util.concurrent.ExecutionException:com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException:Process 'command 'C:\Program Files\Java\jdk1.8.0_73\bin\java.exe''finished with non-zero exit value 3

 

解决方法:

Building.gradle

   dexOptions {

        javaMaxHeapSize "4g"

    }

 

javaMaxHeapSize对应正则表达式是"\\d+[kKmMgGtT]?"4g指4G字节。

DexOptions类包含了获取JavaMaxHeapSize的方法,对应Building.gradle里dexOptions设置的javaMaxHeapSize。


参考:

http://source.android.com/devices/tech/dalvik/dalvik-bytecode.html

http://jayfeng.com/2016/03/10/%E7%94%B1Android-65K%E6%96%B9%E6%B3%95%E6%95%B0%E9%99%90%E5%88%B6%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83/

http://developer.android.com/intl/es/tools/building/multidex.html

posted @ 2017-03-29 17:27  一点点征服  阅读(1153)  评论(0编辑  收藏  举报