r8 proguard_如何为导航组件参数生成Proguard R8规则

r8 proguard

背景 (The background)

Android Jetpack’s Navigation component is the modern solution for navigating between screens in Android apps. It supports both activities and fragments (including dialogs). What is more, it allows to pass data to destinations.

Android Jetpack的Navigation组件是用于在Android应用程序中的屏幕之间进行导航的现代解决方案。 它支持活动和片段(包括对话框)。 而且,它允许将数据传递到目的地。

Unfortunately there is a snag. If you read documentation carefully you’ll find the Proguard considerations section. So every time you use non-primitive argument types you have to remember to either annotate its classes with@Keep annotation or add corresponding -keepnames rules to Proguard/R8 configuration (of course if you don't obfuscate your code then this problem is irrelevant but most apps are obfuscated).

不幸的是有一个障碍。 如果仔细阅读文档,则会发现Proguard注意事项部分。 因此,每次使用非原始参数类型时,您都必须记住要使用@Keep注释来注释其类,或将相应的-keepnames规则添加到Proguard / R8配置中(当然,如果您不混淆代码,那么此问题就无关紧要了)但大多数应用都是模糊的)。

However, it’s not a perfect solution. For example when during refactoring you decide to pass something else as an argument (another class) you need to either annotate the new class and remove annotation from previous one or update affected rules respectively. It’s quite inconvenient and error prone (errors in this matter will usually be discovered at runtime of non-debug builds).

但是,这不是一个完美的解决方案。 例如,在重构过程中,您决定将其他内容作为参数传递(另一个类)时,您需要注释新类并从先前的类中删除注释,或者分别更新受影响的规则。 这非常不方便且容易出错(通常会在非调试版本的运行时发现此问题中的错误)。

Gradle来救援! (Gradle to the rescue!)

It would be better if everything will work out of the box without necessity to make any additional changes manually. To achieve that we can create a buildscript task which looks for classes used as Navigation component arguments and then generates Proguard rules.

如果一切都可以直接使用,而不需要手动进行任何其他更改,那将更好。 为此,我们可以创建一个buildscript任务,该任务查找用作Navigation组件参数的类,然后生成Proguard规则。

The algorithm is simple:

该算法很简单:

  • For each XML file with navigation graph:

    对于每个带有导航图的XML文件:
  • For each distinct non-primitive navigation destination argument:

    对于每个不同的非原始导航目标参数:
  • Construct Proguard rule

    构造Proguard规则
  • Write rule to the file (create it if not exist)

    将规则写入文件(如果不存在则创建)
  • Use generate file as Proguard file of the project

    将生成文件用作项目的Proguard文件

Note that in case of library project you need to use consumer Proguard file.

请注意,在库项目的情况下,您需要使用使用者 Proguard文件。

给我看看代码! (Show me the code!)

In general, actions performed by buildscripts should be located inside the task. Task can be defined directly in buildscript. However, for better readability we’ll use buildSrc folder. Let's create a file buildSrc/src/main/kotlin/GenerateNavArgsProguardRulesTask.kt with the skeleton:

通常,构建脚本执行的操作应位于task内部。 任务可以直接在buildscript中定义。 但是,为了提高可读性,我们将使用buildSrc文件夹。 让我们用骨架创建一个文件buildSrc/src/main/kotlin/GenerateNavArgsProguardRulesTask.kt

The class is declared abstract. This is not technically necessary but considered a good practice. Gradle does not instantiate task classes directly but rather creates the wrapping subclasses. Abstract modifier ensures that subclasses can be created and also prevents direct instantiations somewhere in the code.

该类被声明为抽象。 这在技术上不是必需的,但被认为是一种很好的做法。 Gradle不会直接实例化任务类,而是会创建包装子类。 抽象修饰符确保可以创建子类,并且还可以防止在代码中的某些地方直接实例化。

The next step is to declare inputs and outputs:

下一步是声明输入和输出:

The input consists of all the navigation graph files. For simplicity, only default path of main source set is handled. We also assume that there will be no other XML files there. The output is a single file with Proguard rules inside project build directory.

输入包含所有导航图文件。 为简单起见,仅处理主源集的默认路径。 我们还假设那里没有其他XML文件。 输出是在项目构建目录中包含Proguard规则的单个文件。

Note the @InputFiles and @OutputFile annotations. If they are present the task will only run when necessary. Roughly speaking if none of inputs and outputs are changed since the latest invocation a task is assumed to be up-to-date and Gradle won’t waste the time on executing it again.@SkipWhenEmpty as the name suggests cause that task will be skipped if there no input files present. However, it will still execute if list of input files just became empty since previous invocation.

请注意@InputFiles@OutputFile批注。 如果存在,则仅在必要时运行任务。 粗略地讲,如果自上次调用以来输入和输出均未更改,则假定任务是最新的,并且Gradle不会在重新执行该任务上浪费时间。 顾名思义, @SkipWhenEmpty会导致如果没有输入文件,则将跳过任务。 但是,如果自上次调用以来输入文件列表刚刚变为空,它将仍然执行。

Those annotations and as a consequence not executing unnecessary actions have a significant impact on build times. Especially on debug/development builds performed by developers on their local machines.

这些注释以及因此不执行不必要的操作对构建时间有重大影响。 特别是在开发人员在其本地计算机上执行的调试/开发构建中。

Now we know what files to read and where to save the results, so let’s do the main part of the task!

现在我们知道要读取哪些文件以及将结果保存在何处,因此让我们完成任务的主要部分!

The algorithm is as follows:

算法如下:

  1. Find all argument nodes.

    查找所有argument节点。

  2. For all these nodes take argType attribute.

    对于所有这些节点,请使用argType属性。

  3. Filter out primitive types (assuming that their names does not contain a dot).

    筛选出原始类型(假设它们的名称不包含点)。
  4. Remove duplicates (by converting to set).

    删除重复项(通过转换为集合)。
  5. For each item create a -keepnames rule.

    为每个项目创建一个-keepnames规则。

  6. Write each rule to output file.

    将每个规则写入输出文件。

Note that XML parser is namespace aware.

请注意,XML解析器支持名称空间。

最后一点 (The final touches)

We can optionally set task group and description which will be displayed by Gradle (eg. in tasks command) or IDE. Additionally we can make our task cacheable. The final code looks like that:

我们可以选择设置任务组和描述,这些将由Gradle(例如,在tasks命令中)或IDE显示。 另外,我们可以使我们的任务可缓存。 最终代码如下所示:

用法 (Usage)

Tasks need to be registered in order to be invoked. We can also set the dependencies so they will be executed automatically. In build.gradle.kts it may look like this: preBuild task is executed before building and already registered by Android Gradle Plugin. Don't forget to add custom Proguard rules path. Eg. for library project it may be:

任务需要注册才能被调用。 我们还可以设置依赖项,以便它们将自动执行。 在build.gradle.kts它可能看起来像这样: preBuild任务是在构建之前执行的,并且已经由Android Gradle插件注册。 不要忘记添加自定义Proguard规则路径。 例如。 对于图书馆项目,可能是:

结论 (Conclusion)

Automatic generation of Proguard/R8 rules for Navigation Component destination arguments can be easily implemented with help of Gradle. Don’t forget to properly annotate inputs and outputs of custom Gradle tasks to not hinder build process performance.

借助Gradle可以轻松实现自动生成用于导航组件目标参数的Proguard / R8规则。 不要忘记适当地注释自定义Gradle任务的输入和输出,以免影响构建过程的性能。

Thanks to WrocławJUG for the JDD 2019 conference ticket!

感谢WrocławJUG获得JDD 2019会议门票!

Originally published at https://wroclaw.jug.pl.

最初发布在 https://wroclaw.jug.pl

翻译自: https://android.jlelse.eu/how-to-generate-proguard-r8-rules-for-navigation-component-arguments-466e72e75ca7

r8 proguard

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值