首页 > 解决方案 > 应用程序模块化后引用的方法计数增加

问题描述

如:3.5.3;Android Gradle 插件:3.5.0;梯度:5.6.2;

在将“app”模块拆分为几个小模块后,我们观察到应用程序中引用的方法数量急剧增加。但奇怪的是,每个类添加的引用方法少于 Android Apk Analyzer Tool 中提到的总数。

出于测试目的,我已将 WebActivity.class 从“app”模块移至“适配器”模块,并且引用的方法计数增加了 181 个方法。

总结:

app/WebActivity = 63546实际引用的方法,但显示了 65394 个方法。adapter/WebActivity = 63543实际引用的方法,但显示65575 个方法。

我们观察到在添加/拆分 4 个新模块后,“引用方法计数”增加了近 10k。

确切的问题是什么?

应用程序模块化如何将引用的方法数量大幅增加如此之高?

以下是我截取的两个不同 APK 的屏幕截图,唯一的区别是 WebActivity 从“app”模块移动到“适配器”模块,并且增加了 181 个引用方法:

'app' 模块中的 WebActivity 在此处输入图像描述

将 WebActivity 移至“适配器”模块 在此处输入图像描述

在截图中,为什么每个类(用红色标记)添加的引用方法不等于 Apk Analyzer 中给出的总数?

标签: androidandroid-studioapkmodularization

解决方案


很长一段时间以来,我一直在阅读有关代码性能和调优参数的信息。确实,Android 程序是我关注的重点之一。

让我们首先介绍有助于我们解决问题的基本或最重要的概念。

正如Android开发人员所说

模块可以独立构建、测试和调试

因此,模块有自己的Gradle 和 Dependencies。您可以在项目中探索它Hierarchy Viewer

模块化强调维护问题。与性能问题不同。因为模块化有这个重要的影响:

  • 增加继承深度

这是我为了清楚起见而绘制的图表。如你看到的。在使用离散模块时,调用方法 A与没有离散模块2N micro secs相比。N micro secs

在此处输入图像描述

您可能会想到这个问题,引用方法计算与继承深度相关的内容?

答案是:虽然使用模块化增加了引用方法。但是,它不会影响应用程序的性能,主要可能的问题是继承深度,在大多数情况下是可忽略的。

我确实强调模块化中引用方法的增加是由于每个模块 Gradle 和依赖关系

应用程序模块化如何将引用的方法数量大幅增加如此之高?

影响 APK 分析器的条件 参考方法

另请注意,在源代码编译后,缩小和代码收缩也可以显着改变 DEX 文件的内容。

除了上述官方声明之外,我想添加另一个影响 APK 分析器的条件:

开发人员在模块化方面有多少经验?

模块化就像一个家,架构(开发人员)定义了哪里应该是厨房,哪里应该是厕所,哪里应该是厕所。 如果架构决定将 WC 和 Kitchen 结合起来怎么办?是的,这是一场灾难。

如果开发人员不是很有经验,那么在模块化时可能会发生这种情况。


除了额外信息外,回答 OP 问题

在这里,我回答评论中提出的问题

为什么单独的 Gradle 会添加到引用的方法计数?对于单独的依赖项,如果最终结果是单个 APK,那么我认为“应用程序”和功能模块中的重复依赖项不会添加到引用的方法计数中。

因为模块可以构建、测试和调试,所以它们必须有自己的 Gradle 和依赖项。

在编译多模块项目时,编译器会生成几个.dex文件,包括:

  • .dex完全集成依赖项的文件
  • 模块.dex_

依赖.dex文件是所有模块 gradles的集成

让我们看看模块 gradle 如何影响最终的引用方法计数?!

2 APK秒的结果相同,但引用的方法计数不同。

图1 图 2

它们都是空活动,1.7k引用方法计数的差异非常高,具体取决于它们的功能。关键区别在于他们的模块 Gradle其中一个配置为

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}

另一个配置为

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0-alpha01'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
}

尽管它们只是空活动,但 Gradle 中的微小差异会导致1.7k引用方法计数的差异。

App Gradle是

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation project(path: ':module')
}

主要问题是为什么添加单独引用的方法计数与 Apk Analyzer 中的总引用方法计数不同?

这只是一个 IDE 过滤器。当然,如果您只选择一个.dex文件 Reference Method Counts 等于每行 Referenced Method Counts 的 SUM 但如果您多选.dex文件,您将看到 SUM 和实际 Count 的差异,这是因为 Analyzer 首选的引用中的相等性过滤它们。

在您的屏幕截图中,您选择了多个.dex文件,然后选择了 Analyzer 过滤器相等性。

在我们的项目中,我们使用集中式依赖项。gradle 文件,因此不可能有不同的版本。那么,您是否认为即使我们在功能模块中具有相同/精确的依赖项集及其版本,也会增加引用方法的数量?

从理论上讲,它不应该增加引用的方法计数。但是,正如我所解释的,开发人员体验对最终结果的影响很大。

Team Analyzer应该在发布之前检查并修复性能问题,例如

  • 保护规则
  • 缩小和缩小的资源
  • androidManifest.xml
  • 毕业设置

现在我想澄清开发人员体验和代码维护如何影响最终结果。即使您的 APK 使用集中式依赖项

图 3

在上面的例子中,我增加5.1k了引用方法计数,即使我有集中依赖!!!!!!

怎么可能?

答案是:我只是在项目的目录下添加了一个无用的隐藏.jar文件。libs正如你所看到的那样简单,我影响了最终结果。

如您所见,开发人员体验会影响最终结果。因此,实际上引用的方法计数可能会增加,尽管理论上应该。

当我通过禁用并行编译仅编译“app”模块时,为什么引用方法计数没有差异?它应该已经减少了,因为只有“应用程序”模块的依赖项会被使用,对吧?

编译与引用的方法计数没有任何关系。它符合开发人员想要遵守的内容。


结论

我已经涵盖了围绕这个问题的所有可能性。事实上,它可能会出现在不同的情况下,使用此指南的开发人员可以解决问题。

  • 我希望你能找到为什么引用方法增加了,以及为什么在某些情况下它可能会急剧增加。
  • 模块有他们的 Gradle & Dependencies 和模块化增加模块。因此,这些方法参考。
  • 模块化对应用程序性能的影响可以忽略不计,但可以使您的应用程序维护变得更好。
  • 开发人员在模块化方面的经验也对最终结果有很大影响。

重要提示:几乎所有的陈述都是我的调查和研究。实际上,可能存在错误和故障,将来会更新以添加更多信息。



推荐阅读