Introducing Wire Protocol Buffers

A new, lightweight implementation of Protocol Buffers for Android.

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.

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 be clean, developer-friendly data objects:
  • They should be highly readable
  • They should be deeply immutable
  • They should have meaningful equals, hashCode, and toStringmethods
  • 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

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 of get, set, has, and clear), and at least eighteen methods for repeated fields!

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;
}
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 * 37 + (email != null ? email.hashCode() : 0);
result = result * 37 + (phone != null ? phone.hashCode() : 0);
hashCode = result;
}
return result;
}
public static final class Builder extends Message.Builder<Person> {
// not shown
}
}

How it Works

An instance of a message class can only be created by a corresponding nested Builder class. Wire generates a single method per field in each builder in order to support chaining:

Person person = new Person.Builder()
.name("Omar")
.id(1234)
.email("omar@wire.com")
.phone(Arrays.asList(new PhoneNumber.Builder()
.number("410-555-0909")
.type(PhoneType.MOBILE)
.build()))
.build();
@ProtoField(tag = 1, type = STRING, label = REQUIRED)
public final String name;
if (person.phone != null) {
for (PhoneNumber phone : person.phone)
if (phone.type == PhoneType.MOBILE) {
sendSms(person.name, phone.number, message);
break;
}
}
}
byte[] data = person.toByteArray();
Wire wire = new Wire();
Person newPerson = wire.parseFrom(data, Person.class);

Try it out!

We encourage you to try Wire, contribute to the code, and let us know how it works in your apps!

Square Corner Blog

Buying and selling sound like simple things - and they should be. Somewhere along the way, they got complicated. At Square, we're working hard to make commerce easy for everyone.

Unlisted

Square Engineering

Written by

The official account for @Square Engineering.

Square Corner Blog

Buying and selling sound like simple things - and they should be. Somewhere along the way, they got complicated. At Square, we're working hard to make commerce easy for everyone.