美文网首页Android面试基础安卓开发博客Android
Android为什么会有65536的方法数量限制

Android为什么会有65536的方法数量限制

作者: 我是吸血鬼 | 来源:发表于2018-03-19 00:27 被阅读1433次

    这里给大家推荐一个Android源码网站:http://androidxref.com/(无需翻墙)

    前言

    65536是什么样的数?2的16次方或者说64KB

    下边这个error是不是很熟悉

    较高版本的Android构建系统下的提示(Android 7.0及以下):

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

    较高版本的Android构建系统的报错信息(Android 8.0)

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

    注意:构建时期发生的错误哦

    为什么会出现64K的限制呢?

    一般排查问题我们需要从问题本身入手,那么log是最重要的信息。

    在构建流程中出现这种问题,根据提示我们大概明白方法数过大,而这些方法是存在于编译后的.class文件中的,而.class最后要存在于dex文件中。

    那么如此分析的话,问题应该存在于dex的打包流程当中,这个需要以后深入了解一下。

    根据前人的一些分析,我们来看看MemberIdsSection文件。代码不多,如下:

    1 /*
    2 * Copyright (C) 2007 The Android Open Source Project
    3 *
    4 * Licensed under the Apache License, Version 2.0 (the "License");
    5 * you may not use this file except in compliance with the License.
    6 * You may obtain a copy of the License at
    7 *
    8 *      http://www.apache.org/licenses/LICENSE-2.0
    9 *
    10 * Unless required by applicable law or agreed to in writing, software
    11 * distributed under the License is distributed on an "AS IS" BASIS,
    12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13 * See the License for the specific language governing permissions and
    14 * limitations under the License.
    15 */
    16
    17package com.android.dx.dex.file;
    18
    19import com.android.dex.DexFormat;
    20import com.android.dex.DexIndexOverflowException;
    21
    22import java.util.Formatter;
    23import java.util.Map;
    24import java.util.TreeMap;
    25import java.util.concurrent.atomic.AtomicInteger;
    26
    27/**
    28 * Member (field or method) refs list section of a {@code .dex} file.
    29 */
    30public abstract class MemberIdsSection extends UniformItemSection {
    31
    32    /**
    33     * Constructs an instance. The file offset is initially unknown.
    34     *
    35     * @param name {@code null-ok;} the name of this instance, for annotation
    36     * purposes
    37     * @param file {@code non-null;} file that this instance is part of
    38     */
    39    public MemberIdsSection(String name, DexFile file) {
    40        super(name, file, 4);
    41    }
    42
    43    /** {@inheritDoc} */
    44    @Override
    45    protected void orderItems() {
    46        int idx = 0;
    47
    48        if (items().size() > DexFormat.MAX_MEMBER_IDX + 1) {
    49            throw new DexIndexOverflowException(getTooManyMembersMessage());
    50        }
    51
    52        for (Object i : items()) {
    53            ((MemberIdItem) i).setIndex(idx);
    54            idx++;
    55        }
    56    }
    57
    58    private String getTooManyMembersMessage() {
    59        Map<String, AtomicInteger> membersByPackage = new TreeMap<String, AtomicInteger>();
    60        for (Object member : items()) {
    61            String packageName = ((MemberIdItem) member).getDefiningClass().getPackageName();
    62            AtomicInteger count = membersByPackage.get(packageName);
    63            if (count == null) {
    64                count = new AtomicInteger();
    65                membersByPackage.put(packageName, count);
    66            }
    67            count.incrementAndGet();
    68        }
    69
    70        Formatter formatter = new Formatter();
    71        try {
    72            String memberType = this instanceof MethodIdsSection ? "method" : "field";
    73            formatter.format("Too many %1$s references to fit in one dex file: %2$d; max is %3$d.%n" +
    74                            "You may try using multi-dex. If multi-dex is enabled then the list of " +
    75                            "classes for the main dex list is too large.%n" +
    76                    "References by package:",
    77                    memberType, items().size(), DexFormat.MAX_MEMBER_IDX + 1);
    78            for (Map.Entry<String, AtomicInteger> entry : membersByPackage.entrySet()) {
    79                formatter.format("%n%6d %s", entry.getValue().get(), entry.getKey());
    80            }
    81            return formatter.toString();
    82        } finally {
    83            formatter.close();
    84        }
    85    }
    86
    87}
    

    在48行到49中,我们看到如下可能抛出异常的情况

    if (items().size() > DexFormat.MAX_MEMBER_IDX + 1) {
         throw new DexIndexOverflowException(getTooManyMembersMessage());
    }
    

    getTooManyMembersMessage()函数内(72行到77行)有如下异常信息字符串构造

    String memberType = this instanceof MethodIdsSection ? "method" : "field";
               formatter.format("Too many %1$s references to fit in one dex file: %2$d; max is %3$d.%n" +
                                "You may try using multi-dex. If multi-dex is enabled then the list of " +
                                "classes for the main dex list is too large.%n" +
                        "References by package:",
                        memberType, items().size(), DexFormat.MAX_MEMBER_IDX + 1);
    

    同时我们还要注意DexFormat类,

        /**
         * Maximum addressable field or method index.
         * The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or
         * meth@CCCC.
         */
       public static final int MAX_MEMBER_IDX = 0xFFFF;
    

    根据注释,我们来到Dalvik 字节码,根据表格中的解释如下图:

    Dalvik

    可以看到类型索引(16 位),由此可以知道,无论是方法数还是字段数都不能超过65536,这也就是为什么在构建流程中出现65536的报错信息。

    由此可以得出结论:

    单个dex的方法或者字段数量不能超过65536

    如何避免64K问题?涉及到dex分包的知识,同时也是涉及到APK瘦身优化等问题。

    很多问题都需要我们去直面,一步步去解决。

    相关文章

      网友评论

      • 琴梨梨:弄一大堆dex是最简单的解决方法,而且在5.0+,多dex不需要额外组件就能运行(art在执行oat时会把所有dex变成一个odex)

      本文标题:Android为什么会有65536的方法数量限制

      本文链接:https://www.haomeiwen.com/subject/aineqftx.html