Skip to content
Ider

沉淀我所学习,累积我所见闻,分享我所体验

Primary Navigation Menu
Menu
  • Home
  • About Ider
    • Who Ider?
    • Why Ider?
    • How Ider?
    • Where Ider?
    • What Ider?

ProtoBuf

2021-01-14
14 January
On January 14, 2021
In Design Patterns(设计模式), English Posts(英文写作), Knowledge Base(心得笔库), Mobile Development(移动开发), Software Engineering(软件工程)

ProtoBuf 2.0 method count optimization for android development

The Protocol Buffer library was not initially built for Android, it just turned out to have a Java version to be used in Android, but it’s not optimized for Android apps, for example: it doesn’t consider the method count limit in Android.

So, in this article, I’d like to share some work I found specifically on Protocol Buffer Version 2 that can reduce the method count. If your app is also heavily relying on Protocol Buffer, I hope these approaches are useful for you too.

General Approaches

1. Use Protocol Buffer Java Lite Runtime

Just as the name indicates, the dependency library is much smaller than the regular Protocol Buffer Java runtime, the generated code is also much slimmer. However, the APIs are compatible between those two versions, so the call sites would not be affected when changing the library.

2. Don’t use <Message>OrBuilder interface

For each Protocol Buffer message definition in .proto file, the Protocol Buffer compiler generates an interface named <Message>OrBuilder (<Message> is the name defined in .proto file). This interface would be implemented by the concrete <Message> class and the corresponding Builder.

It might be attractive to use it as a variable type thereby you can depend on abstractions to not concrete classes. But calling methods on Java interface would make Dex take count of those methods.

In reality, every place can directly use either <Message> or Builder, then the optimization tool (like R8, ProGuard) can safely remove the methods declared on the interface.

Special Tricks

Protocol Buffer Java Lite is very good, but if I open the magic box of generated Java classes, I notice it’s still not optimal for Android. There are a couple places I could modify to make it more effective for Android applications.

Instead of copying the Java files and making the change, which is error prone when engineers update the .proto file, I created a script to automate the job. Just add the execution of this script at the end of the Protocol Buffer gradle task, so it works just as a complement of Protocol Buffer code generation process.

[codesyntax lang=”groovy” lines=”normal”]

protobuf {
  protoc {
    artifact = 'com.google.protobuf:protoc:3.7.0'
  }
  plugins {
    javalite {
      artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
    }
  }
  generateProtoTasks {
    all().each { task ->
      task.builtins {
        remove java
      }
      task.plugins {
        javalite { }
      }

      // `getOutputDir()` can only be read during configuration, 
      // so it’s out of action block
      def outputDir = task.getOutputDir(task.plugins[0])
      task.doLast {
          exec {
              commandLine file('protobuf-optimizer.py')
              args outputDir
          }
      }    
    }
  }
}

[/codesyntax]

In the script, I have made the following modification on generated java code.

1. Change the modifier of Builder constructor from private to package

Simply running the sample addressbook.proto from Protocol Buffer tutorial side, you would get a class like the following snippet:

[codesyntax lang=”java” lines=”normal”]

public static final class Person {
    private Person() {}

    protected final Object dynamicMethod(
            com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,
            Object arg0, Object arg1) {
        switch (method) {
            case NEW_BUILDER: {
                return new Builder();
            }
        }
    }

    public static final class Builder {
        private Builder() {}
    }

}

[/codesyntax]

When analyzing the APK file, we could would find some synthetic classes and methods:

This is because the outer class is accessing the private constructor of the inner class. The generated synthetic class would contribute an extra constructor to the Dex file. Therefore, by simply removing the private from Builder(), you can remove such classes and methods.
Read More →

Facebook
Twitter
LinkedIn
RSS
ZhiHu

Recent Posts

  • 三年居家工作感受
  • Pixel Watch智能手表和Pixel 5, 6 Pro 及 7 Pro手机
  • 我拥有过的无线耳机
  • 毕业工作一个月,我差点被开除
  • 我拥有过的移动硬盘
  • ProtoBuf 2.0 method count optimization for android development
  • 面过100场行为面试后

Categories

  • Algorithm Analysis(算法分析)
  • Article Collection(聚宝收藏)
  • Data Structures(数据结构)
  • Design Patterns(设计模式)
  • English Posts(英文写作)
  • Front Interface(界面构想)
  • IT Products(数码产品)
  • Knowledge Base(心得笔库)
  • Language Tips(语言初试)
  • Mathematical Theory(数学理论)
  • Mobile Development(移动开发)
  • Programming Life(程序人生)
  • Reading Notes(阅而后知)
  • Software Engineering(软件工程)
  • Special Tricks(奇技妙招)
  • Tangential Speech(漫话杂谈)

Tags

Aero Android API Bash Binary Search Bitwise Operation Book C/C++ Career Chrome Conference CSS Debug Device DOM Extension Framework Game Gradle Hearthstone HTML Initialization Intellij Interview iOS Java JavaScript jQuery Keyword Language Issues Mac Microsoft Mobile Modifier Objective-C PHP Principle Reference Regular Expression Static String Tools Tutorial UI XML

Blogroll

  • Ahmed's Blog
  • Gert Lombard's Blog
  • Gordon Luk
  • Jack & Allison
  • 开发部落

Archives

Designed using Chromatic. Powered by WordPress.