针对android方法数64k的限制,square做出的努力。精简protobuf
1、早期的Dalvik VM内部使用short类型变量来标识方法的id,dex限制了程序的最大方法数是65535,如果超过最大限制,无法编译,把dex.force.jumbo=true添加到project.properties文件中可以通过编译,在低端手机无法安装,报错误INSTALL_FAILED_DEXOPT。
2、dex文件解决8M时,低端机安装也会出现INSTALL_FAILED_DEXOPT错误,因为dexopt用了一个固定大小的缓冲区存储所有的方法名,2.3(含)之前的版本只有5M大小,最新的版本有8M或者16M
问题解决
1、如方法数量超过上线,必须精简方法数量。可以对复杂模块采用jni的方式实现,也可以对边缘模块采用本地插件化的方式解决。
2、如只是dex文件过大,可以采用混淆的方法减小dex文件尺寸,如混淆后还是过大,参考第一个方法。
上面的方式只能解决你在开发中遇到的问题, 并且治标不治本,下面的内容才是根本解决原文地址:http://corner.squareup.com/2013/08/introducing-wire.html
Introducing Wire Protocol Buffers
What is Wire?
Wire is a new, open-source implementation of Google's Protocol
Buffers. It's meant for Android devices but can be used on anything that runs Java
language code.
At Square, we use Protocol Buffers extensively. Protocol Buffers are a language- and platform-neutral schema for describing
and transmitting data. Developers can use the same schemas across diverse environments, including the environments we care
about at Square, such as Java and Ruby servers and Android and iOS devices.
Protocol Buffer .proto
source files are human-readable and can contain comments, so it's easy to document your schema right
where it is defined. Protocol Buffers define a compact binary wire format that allows schemas to evolve without breaking
existing clients. Typically, a code generator is used that reads .proto
files, and emits source code in the language of
your choice. This approach helps to speed development since the resulting code is expressed in the same language as the rest
of your application, allowing tools such as IDE autocompletion to do their job fully.
Wire Features
As we began to run into limitations of the standard Protocol Buffer implementation in our Android apps, we made a wish list
of the features we wanted for a future implementation:
- Messages should contain a minimum number of generated methods
- Messages should be clean, developer-friendly data objects:
- They should be highly readable
- They should be deeply immutable
- They should have meaningful
equals
,hashCode
, andtoString
methods - They should support the chained Builder pattern
- They should inherit documentation from the
.proto
source files - Protocol Buffer enums should map onto Java enums
- Ideally, everything should be buildable using Java-based tools
Before we decided to build a new library we looked at several alternatives, including the recent
Nano Android
Protocol Buffer library. While Nano Protocol Buffers generate very few methods, they didn't meet our other goals.
Ultimately, we decided to create our own library from scratch, built on Square's
ProtoParser and JavaWriter libraries.
It's handy to be able to use generated Protocol Buffer classes as full-fledged data objects in your app. By includingequals
, hashCode
, and toString
methods, messages can participate in Java collections. Since the generated code is clean
and compact, stepping into it in the debugger is not a problem. And because comments in your.proto
files are copied into
the generated Java source code, the documentation for your messages and fields is right there in your IDE.
Reducing the Method Count 减少放法数量
In the past, Android developers attempting to use Protocol Buffers have paid a steep price. The standard Protocol Buffer
implementation (protoc
) generates at least nine methods for each optional or required field in your schema (variants ofget
, set
, has
, and clear
), and at least eighteen methods for repeated fields!
Having all this flexibility is great in non-constrained environments — whatever method you need is probably just a few
auto-completed keystrokes away. But in Android environments the Dalvik bytecode
format imposes a hard limit of 64K methods in a single
application. For the Square Register app, generated Protocol Buffer code was taking up a large chunk of our method space. By
switching to Wire, we removed almost 10,000 methods from our application and gained a lot of breathing room to add new
features.
Wire Example
Consider the classic Person
protocol buffer definition:
message Person {
// The customer's full name.
required string name = 1;
// The customer's ID number.
required int32 id = 2;
// Email address for the customer.
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
// The user's phone number.
required string number = 1;
// The type of phone stored here.
optional PhoneType type = 2 [default = HOME];
}
// A list of the user's phone numbers.
repeated PhoneNumber phone = 4;
}
The Person
class Wire generates is below (the complete generated code is
here):
public final class Person extends Message {
/** The customer's full name. */
@ProtoField(tag = 1, type = STRING, label = REQUIRED)
public final String name;
/** The customer's ID number. */
@ProtoField(tag = 2, type = INT32, label = REQUIRED)
public final Integer id;
/** Email address for the customer. */
@ProtoField(tag = 3, type = STRING)
public final String email;
/** A list of the user's phone numbers. */
@ProtoField(tag = 4, label = REPEATED)
public final List<PhoneNumber> phone;
private Person(Builder builder) {
super(builder);
this.name = builder.name;
this.id = builder.id;
this.email = builder.email;
this.phone = immutableCopyOf(builder.phone);
}
@Override public boolean equals(Object other) {
if (!(other instanceof Person)) return false;
Person o = (Person) other;
return equals(name, o.name)
&& equals(id, o.id)
&& equals(email, o.email)
&& equals(phone, o.phone);
}
@Override public int hashCode() {
int result = hashCode;
if (result == 0) {
result = name != null ? name.hashCode() : 0;
result = result * 37 + (id != null ? id.hashCode() : 0);
result = result