应用程序缩小以后下边看不到_使用R8缩小您的应用程序

应用程序缩小以后下边看不到

Posted by Søren Gjesse, Software Engineer and Christoffer Adamsen‎, Software Engineer

作者:软件工程师SørenGjesse和软件工程师Christoffer Adamsen

Apps with a smaller download size and install footprint are far more likely to be installed and stay installed, particularly in emerging markets. With the R8 compiler, you now have more comprehensive support to achieve that through shrinking, obfuscation, and optimization.

具有较小下载大小和安装占用空间的应用程序更有可能被安装并保持安装状态,尤其是在新兴市场中。 使用R8编译器,您现在可以通过收缩,混淆和优化获得更全面的支持。

In this post, we provide an overview of the features available in R8, the reduction in code size you might expect, and show how to enable these features in R8.

在这篇文章中,我们概述了R8中可用的功能,可能会减少的代码大小,并展示了如何在R8中启用这些功能。

R8收缩功能 (R8 shrinking features)

R8 offers four features designed to reduce the size of your Android app:

R8提供了四个旨在减小Android应用程序大小的功能:

  • Tree shaking: Using static code analysis to find and remove unreachable code and uninstantiated types.

    摇树 :使用静态代码分析来查找和删除无法访问的代码和未实例化的类型。

  • Optimization: Optimizing code for size by removing dead code, selective inlining, unused argument removal, and class merging.

    优化 :通过删除无效代码,选择性内联,未使用的参数删除和类合并来优化代码大小。

  • Identifier renaming a.k.a. obfuscation: Using short names and squashing package namespace.

    重命名标识符(又名混淆) :使用短名称和压缩包名称空间。

  • Reducing debug information: Canonicalizing debug information and compressing line number information.

    减少调试信息 :规范化调试信息并压缩行号信息。

为什么我们需要缩小R8 (Why we need R8 shrinking)

When you write an app, all the code should serve a purpose and implement features in the app. However, most apps use third-party libraries, such as Jetpack, OkHttp, Guava, Gson and Google Play Services, and apps written in Kotlin always include the Kotlin standard library. When you use one of these third-party libraries, often only a very small part of each individual library is used in your app. Without shrinking, all the library code is retained in your app.

编写应用程序时,所有代码都应达到目的并在应用程序中实现功能。 但是,大多数应用程序使用JetpackOkHttpGuavaGsonGoogle Play服务等第三方库,并且用Kotlin编写的应用程序始终包含Kotlin标准库 。 当您使用这些第三方库之一时,通常每个单独库的一小部分就会在您的应用中使用。 无需缩小,所有库代码都保留在您的应用程序中。

Your code may also be larger than it needs to be because verbose code sometimes improves readability and maintainability: for example, you may strive to use meaningful variable names and the builder pattern to make it easier for others to review and understand your code. But these patterns come at the expense of code size — very often, the code you write yourself offers plenty of opportunity for shrinking.

您的代码也可能比实际需要的大,因为冗长的代码有时可以提高可读性和可维护性:例如,您可能会努力使用有意义的变量名和构建器模式,以使其他人更容易查看和理解您的代码。 但是这些模式是以牺牲代码大小为代价的–通常,您自己编写的代码为缩小代码提供了很多机会。

启用应用程序的R8缩小 (Enable R8 shrinking of your app)

To have R8 shrink your app for release builds set minifyEnable to true in your app’s main build.gradle file, like this:

要使R8缩小应用程序的发行版本,请在应用程序的主build.gradle文件中将minifyEnable设置为true,如下所示:

android {
  buildTypes {
    release {
      minifyEnabled true
    }
  }
}

Don’t let the minifyEnable name confuse you — it turns on R8 shrinking.

不要让minifyEnable名称使您minifyEnable困惑-它会导致R8缩小。

R8将减少多少您的应用程序大小? (How much will R8 reduce your app’s size?)

R8 can substantially reduce your app’s size. For example, last year’s Google I/O app was 18.55 MB with 150,220 methods and 3 dex files before shrinking. After shrinking, the app reduced to 6.45 MB with 45,831 methods and 1 dex file. R8 saved 65% in dex size (measured with Android Studio 3.5.1 and the IOSched sample app at this commit).

R8可以大大减小应用程序的大小。 例如,去年的Google I / O应用为18.55 MB,包含150,220种方法和3个dex文件,然后才缩小。 缩小后,应用程序使用45,831个方法和1个dex文件减少到6.45 MB。 R8节省了65%的dex大小( 此提交时使用Android Studio 3.5.1和IOSched示例应用程序 进行了测量 )。

基本收缩算法 (The basic shrinking algorithm)

For simplicity, let’s take an example of a standalone program in the Java programming language:

为简单起见,让我们以Java编程语言为例说明一个独立程序:

class com.example.JavaHelloWorld {
  private void unused() {
    System.out.println("Unused");
  }


  private static void greeting() {
    System.out.println("Hello, world!");
  }


  public static void main(String[] args) {
    greeting();
  }
}

The entry point for the program is the static void main method, which we specify using the following keep rule:

程序的入口点是static void main方法,我们使用以下keep规则指定该方法:

-keep class com.example.JavaHelloWorld {
      public static void main(java.lang.String[]);
}

The R8 shrinking algorithm works as follows:

R8缩小算法的工作原理如下:

  • First, it traces all reachable code from the well-known entry points of the program — these entry points are defined by R8 keep rules. For example, in this Java code example, R8 would start at the main method.

    首先,它从程序的众所周知的入口点跟踪所有可访问的代码-这些入口点由R8 keep规则定义。 例如,在此Java代码示例中,R8将在main方法处开始。

  • In the example, R8 traces from the main method to the greeting method. The greeting method calls into the runtime, so tracing stops there.

    在示例中,R8从main方法跟踪到greeting方法。 Greeting方法将调用运行时,因此跟踪从此处停止。
  • With tracing complete, R8 uses an optimization called tree shaking to remove unused code. In the example, tree shaking removes the method unused since R8’s tracing step detects that that method is not reachable from any well-known entry points.

    跟踪完成后,R8使用称为树摇的优化来删除未使用的代码。 在该示例中,树摇晃删除了未使用的方法,因为R8的跟踪步骤检测到该方法无法从任何已知的入口点到达。
  • Next, R8 renames identifiers to shorter names that take up less space in DEX files. In the example, R8 might rename the method greeting to the shorter name a:

    接下来,R8将标识符重命名为较短的名称,这些名称在DEX文件中占用较少的空间。 在示例中,R8可以将greeting方法重命名为短名称a

class com.example.JavaHelloWorld {


  private static void a() {
    System.out.println("Hello, world!");
  }


  public static void main(String[] args) {
    a();
  }
}
  • Finally code optimizations are then applied. One of these is inlining when that leads to less code. In this example moving the body of the method a into main directly will make the code smaller:

    最后,然后应用代码优化。 其中之一是内联,这会导致更少的代码。 在此示例中,将方法a主体直接移到main中将使代码更小:

class com.example.JavaHelloWorld {


  public static void main(String[] args) {
    System.out.println("Hello, world!");
  }
}

As you can see, the resulting code is much smaller than the original.

如您所见,生成的代码比原始代码小得多。

为R8缩小准备应用程序 (Preparing an app for R8 shrinking)

Like a standalone Java program, an Android application has a number of well-known entry points: activities, services, content providers, and broadcast receivers. The aapt2 tool handles these entry points for you by generating keep rules based on your Android Manifest file.

像独立的Java程序一样,Android应用程序具有许多众所周知的入口点:活动,服务,内容提供者和广播接收者。 aapt2工具通过基于Android Manifest文件生成保留规则来为您处理这些入口点。

In addition to these well-known entry points, there are other standard keep rules needed for Android applications. These rules are provided by the Android Gradle Plugin in a default configuration file which you specify when configuring your build:

除了这些众所周知的入口点,Android应用程序还需要其他标准的保持规则。 这些规则由Android Gradle插件在默认配置文件中提供,您可以在配置构建时指定该默认配置文件:

android {
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
    }
  }
}

反映在应用程序代码中 (Reflection in app code)

Reflection results in code entry points that R8 does not recognize when tracing your code. Remember that reflection can also happen in third-party libraries and as third-party libraries are effectively part of your app, you — as the app developer — effectively become responsible for the reflection performed in these libraries as well as in your own code. Libraries might come with their own rules included, but remember that many libraries are not written with Android or with shrinking in mind, so they might need additional configuration.

反射会导致在跟踪代码时R8无法识别的代码入口点。 请记住,反射也可能发生在第三方库中,并且由于第三方库实际上是您的应用程序的一部分,因此您(作为应用程序开发人员)将有效地负责这些库以及您自己的代码中执行的反射。 库可能附带了它们自己的规则,但是请记住,许多库不是使用Android编写的,还是出于收缩的考虑,因此它们可能需要其他配置。

Take this example of a Kotlin class with a field called name and a main method that creates an instance and serializes that instance to JSON:

以Kotlin类为例,该类具有一个名为name的字段和一个创建实例并将该实例序列化为JSON的main方法:

class Person(val name: String)


fun printJson() {
   val gson = Gson()
   val person = Person("Søren Gjesse")
   println(gson.toJson(person))
}

After shrinking the code, running the program outputs an empty JSON object {}. This is because R8 only sees the field name as written (which happens in the Person constructor) but never read, so R8 removes it. Person ends up with no fields causing the empty JSON object. However, the field is read by the Gson serializer, but Gson uses reflection techniques to do so, so R8 does not see that this field is read.

收缩代码后,运行程序将输出一个空的JSON对象{} 。 这是因为R8仅将字段名视为已写(在Person构造函数中发生),但从未读取,因此R8会将其删除。 Person最终没有导致空JSON对象的字段。 但是,该字段由Gson序列化程序读取,但是Gson使用反射技术来执行此操作,因此R8无法看到此字段已被读取。

To keep the name field, add a keep rule in your proguard-rules.pro file:

要保留name字段,请在您的proguard-rules.pro文件中添加一个保留规则:

-keep class com.example.myapplication.Person {
    public java.lang.String name;
}

This rule instructs R8 not to touch the field called name in the class Person. With this in place, running the code gives the expected JSON object of {“name”:”Søren Gjesse”}.

此规则指示R8不要触摸Person类中名为name的字段。 将其放置在适当的位置后,运行代码即可获得预期的JSON对象{“name”:”Søren Gjesse”}

Finally, when setting up your project, be sure to add the proguard-rules.pro file to your build.gradle configuration:

最后,在设置项目时,请确保将proguard-rules.pro文件添加到build.gradle配置中:

android {
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                    'proguard-rules.pro'
    }
  }
}

学到更多 (Learn more)

Interested in learning more about how the R8 shrinker works? Check out the R8 developer documentation and watch this talk from Android Dev Summit 2019 to learn about R8 in general and a deep dive into one of R8’s more advanced optimizations — class inlining:

有兴趣了解有关R8收缩器如何工作的更多信息吗? 查看R8开发人员文档,并观看来自Android Dev Summit 2019的演讲,以全面了解R8并深入了解R8的更高级优化之一-类内联:

You can click here to go straight to the section on class inlining.

您可以单击此处直接转到类内联部分。

翻译自: https://medium.com/androiddevelopers/shrinking-your-app-with-r8-909efac25de4

应用程序缩小以后下边看不到

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值