Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

gRPC and protocol buffers for Android, Kotlin, and Java.

License

NotificationsYou must be signed in to change notification settings

vlad-kasatkin/wire

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

“A man got to have a code!” - Omar Little

See theproject website for documentation and APIs.

As our teams and programs grow, the variety and volume of data also grows. Success will turn yoursimple data models into complex ones! Whether your application is storing data to disk ortransmitting it over a network, the structure and interpretation of that data should be clear.Consumers work best with data they understand!

Schemas describe and document data models. If you have data, you should have a schema.

Protocol Buffers

Google's Protocol Buffers are built around a great schema language:

  • It's cross platform and language independent. Whatever programming language you use, you'll beable to use proto schemas with your application.

  • Proto schemas are backwards-compatible and future-proof. You can evolve your schema as yourapplication loses old features and gains new ones.

  • It's focused. Proto schemas describe your data models. That's it.

Here's a sample message definition:

syntax="proto2";packagesquareup.dinosaurs;optionjava_package="com.squareup.dinosaurs";import"squareup/geology/period.proto";messageDinosaur {// Common name of this dinosaur, like "Stegosaurus".optionalstringname=1;// URLs with images of this dinosaur.repeatedstringpicture_urls=2;optionalsquareup.geology.Periodperiod=5;}

And here's an enum definition:

syntax="proto2";packagesquareup.geology;optionjava_package="com.squareup.geology";enumPeriod {// 145.5 million years ago — 66.0 million years ago.CRETACEOUS=1;// 201.3 million years ago — 145.0 million years ago.JURASSIC=2;// 252.17 million years ago — 201.3 million years ago.TRIASSIC=3;}

This schema language is Protocol Buffers' best feature. You might even use it purely fordocumentation purposes, such as to describe a JSON API.

Protocol Buffers also defines a compact binary encoding of messages that conform to the schema. Thisencoding is fast to encode, fast to decode, small to transmit, and small to store. The binaryencoding uses numeric tags from the schema, like the5 forperiod above.

For example, let's encode this dinosaur:

{  name: "Stegosaurus",  period: JURASSIC}

The encoded value is just 15 bytes:

Hex  Description 0a  tag: name(1), field encoding: LENGTH_DELIMITED(2). 1 << 3 | 2 0b  "Stegosaurus".length() 53  'S' 74  't' 65  'e' 67  'g' 6f  'o' 73  's' 61  'a' 75  'u' 72  'r' 75  'u' 73  's' 28  tag: period(5), field encoding: VARINT(0). 5 << 3 | 0 02  JURASSIC(2)

Why Wire?

The Protocol Buffers schema language and binary encoding are both defined by Google. Wire is anindependent implementation from Square that's specifically designed for Android and Java.

For each message type defined in the schema, Wire generates an immutable model class and itsbuilder. The generated code looks like code you'd write by hand: it's documented, formatted, andsimple. Wire's APIs should feel at home to programmers who likeEffective Java.

That said, there are some interesting design decisions in Wire:

  • Wire messages declarepublic final fields instead of the usual getter methods. This cuts downon both code generated and code executed. Less code is particularly beneficial for Androidprograms.

  • Wire avoids case mapping. A field declared aspicture_urls in a schema yields a Java fieldpicture_urls and not the conventionalpictureUrls camel case. Though the name feels awkwardat first, it's fantastic whenever you usegrep or more sophisticated search tools. No moremapping when navigating between schema, Java source code, and data. It also provides a gentlereminder to calling code that proto messages are a bit special.

  • Primitive types are always boxed. If a field is absent, its value isnull. This is used fornaturally optional fields, such as a dinosaur whose period is unknown. A field may also be nulldue to schema evolution: if tomorrow we add acarnivore boolean to our message definition,today's data won’t have a value for that field.

Here's the compact generated code for theDinosaur message defined above:

// Code generated by Wire protocol buffer compiler, do not edit.// Source file: squareup/dinosaurs/dinosaur.proto at 9:1packagecom.squareup.dinosaurs;importcom.squareup.geology.Period;importcom.squareup.wire.Message;importcom.squareup.wire.ProtoAdapter;importcom.squareup.wire.WireField;importjava.util.List;importokio.ByteString;publicfinalclassDinosaurextendsMessage<Dinosaur,Dinosaur.Builder> {publicstaticfinalProtoAdapter<Dinosaur>ADAPTER =ProtoAdapter.newMessageAdapter(Dinosaur.class);privatestaticfinallongserialVersionUID =0L;publicstaticfinalStringDEFAULT_NAME ="";publicstaticfinalPeriodDEFAULT_PERIOD =Period.CRETACEOUS;/**   * Common name of this dinosaur, like "Stegosaurus".   */@WireField(tag =1,adapter ="com.squareup.wire.ProtoAdapter#STRING"  )publicfinalStringname;/**   * URLs with images of this dinosaur.   */@WireField(tag =2,adapter ="com.squareup.wire.ProtoAdapter#STRING",label =WireField.Label.REPEATED  )publicfinalList<String>picture_urls;@WireField(tag =5,adapter ="com.squareup.geology.Period#ADAPTER"  )publicfinalPeriodperiod;publicDinosaur(Stringname,List<String>picture_urls,Periodperiod) {this(name,picture_urls,period,ByteString.EMPTY);  }publicDinosaur(Stringname,List<String>picture_urls,Periodperiod,ByteStringunknownFields) {super(unknownFields);this.name =name;this.picture_urls =immutableCopyOf("picture_urls",picture_urls);this.period =period;  }@OverridepublicBuildernewBuilder() {Builderbuilder =newBuilder();builder.name =name;builder.picture_urls =copyOf("picture_urls",picture_urls);builder.period =period;builder.addUnknownFields(unknownFields());returnbuilder;  }@Overridepublicbooleanequals(Objectother) {if (other ==this)returntrue;if (!(otherinstanceofDinosaur))returnfalse;Dinosauro = (Dinosaur)other;returnequals(unknownFields(),o.unknownFields())        &&equals(name,o.name)        &&equals(picture_urls,o.picture_urls)        &&equals(period,o.period);  }@OverridepublicinthashCode() {intresult =super.hashCode;if (result ==0) {result =unknownFields().hashCode();result =result *37 + (name !=null ?name.hashCode() :0);result =result *37 + (picture_urls !=null ?picture_urls.hashCode() :1);result =result *37 + (period !=null ?period.hashCode() :0);super.hashCode =result;    }returnresult;  }publicstaticfinalclassBuilderextendscom.squareup.wire.Message.Builder<Dinosaur,Builder> {publicStringname;publicList<String>picture_urls;publicPeriodperiod;publicBuilder() {picture_urls =newMutableList();    }/**     * Common name of this dinosaur, like "Stegosaurus".     */publicBuildername(Stringname) {this.name =name;returnthis;    }/**     * URLs with images of this dinosaur.     */publicBuilderpicture_urls(List<String>picture_urls) {checkElementsNotNull(picture_urls);this.picture_urls =picture_urls;returnthis;    }publicBuilderperiod(Periodperiod) {this.period =period;returnthis;    }@OverridepublicDinosaurbuild() {returnnewDinosaur(name,picture_urls,period,buildUnknownFields());    }  }}

The Java code to create and access proto models is compact and readable:

Dinosaurstegosaurus =newDinosaur.Builder()    .name("Stegosaurus")    .period(Period.JURASSIC)    .build();System.out.println("My favorite dinosaur existed in the " +stegosaurus.period +" period.");

Each type has a correspondingProtoAdapter that can encode a message to bytes and decode bytesback into a message.

Dinosaurstegosaurus = ...byte[]stegosaurusBytes =Dinosaur.ADAPTER.encode(stegosaurus);byte[]tyrannosaurusBytes = ...Dinosaurtyrannosaurus =Dinosaur.ADAPTER.decode(tyrannosaurusBytes);

When accessing a field, useWire.get() to replace null values with the corresponding default:

Periodperiod =Wire.get(stegosaurus.period,Dinosaur.DEFAULT_PERIOD);

This is equivalent to the following:

Period period = stegosaurus.period != null ? stegosaurus.period : Dinosaur.DEFAULT_PERIOD;

Wire Kotlin

Since version 3.0.0, Wire can generate Kotlin code. SeeWire Compiler & Gradle Plugin to learn how to configure your build.

Kotlin is a pragmatic and expressive programming language that makes it easy to model data. Here'show we used Kotlin to model Protocol Buffers messages:

  • Messages feel likedata classes, but in fact they're not. Compiler still generatesequals(),hashCode(),toString() andcopy() for you. Wire does not generatecomponentN() functionsthough, we believe that destructuring declarations are not a good fit for Protocol Buffers: achange in the schema that removes or adds a field might lead to a situation when yourdestructuring declaration still compiles but now describes a completely different subset offields, rendering your code incorrect.

  • copy() is a substitute for theBuilder, which is not used anymore. If your program relies ontheBuilder to be present, you may generate code in Java interoperability mode -Wire Compiler & Gradle Plugin explains how that works.

  • Fields are generated as properties. While this is idiomatic in Kotlin, Java code will now haveto access fields using getters. If your program relies on accessing fields directly, use Javainteroperability mode - the compiler will generate@JvmField annotations for each field.

  • The nullability of each field's type depends on its label:required,repeated andmapfields get non-nullable types, whereasoptional fields are of nullable types.

  • With the exception ofrequired fields, each field has a default value:

    • null foroptional fields,
    • emptyList() forrepeated fields,
    • emptyMap() formap fields.

Here's the sameDinosaur message in Kotlin:

// Code generated by Wire protocol buffer compiler, do not edit.// Source file: squareup/dinosaurs/dinosaur.protopackagecom.squareup.dinosaursimportcom.squareup.geology.Periodimportcom.squareup.wire.FieldEncodingimportcom.squareup.wire.Messageimportcom.squareup.wire.ProtoAdapterimportcom.squareup.wire.ProtoReaderimportcom.squareup.wire.ProtoWriterimportcom.squareup.wire.WireFieldimportkotlin.Anyimportkotlin.AssertionErrorimportkotlin.Booleanimportkotlin.Deprecatedimportkotlin.DeprecationLevelimportkotlin.Intimportkotlin.Nothingimportkotlin.Stringimportkotlin.collections.Listimportkotlin.hashCodeimportkotlin.jvm.JvmFieldimportokio.ByteStringclassDinosaur(/**   * Common name of this dinosaur, like "Stegosaurus".*/  @field:WireField(    tag = 1,    adapter = "com.squareup.wire.ProtoAdapter#STRING"  )valname:String? =null,/**   * URLs with images of this dinosaur.*/  @field:WireField(    tag = 2,    adapter = "com.squareup.wire.ProtoAdapter#STRING",    label =WireField.Label.REPEATED  )valpicture_urls:List<String> = emptyList(),  @field:WireField(    tag = 5,    adapter = "com.squareup.geology.Period#ADAPTER"  )valperiod:Period? =null,unknownFields:ByteString =ByteString.EMPTY) : Message<Dinosaur, Nothing>(ADAPTER, unknownFields) {  @Deprecated(    message="Shouldn't be used in Kotlin",    level=DeprecationLevel.HIDDEN  )overridefunnewBuilder():Nothing {throwAssertionError()  }overridefunequals(other:Any?):Boolean {if (other===this)returntrueif (other!isDinosaur)returnfalsereturn unknownFields== other.unknownFields&& name== other.name&& picture_urls== other.picture_urls&& period== other.period  }overridefunhashCode():Int {var result=super.hashCodeif (result==0) {      result= name.hashCode()      result= result*37+ picture_urls.hashCode()      result= result*37+ period.hashCode()super.hashCode= result    }return result  }overridefuntoString():String {val result= mutableListOf<String>()if (name!=null) result+="""name=$name"""if (picture_urls.isNotEmpty()) result+="""picture_urls=$picture_urls"""if (period!=null) result+="""period=$period"""return result.joinToString(prefix="Dinosaur{", separator=",", postfix="}")  }funcopy(name:String? = this.name,picture_urls:List<String> = this.picture_urls,period:Period? = this.period,unknownFields:ByteString = this.unknownFields  ):Dinosaur=Dinosaur(name, picture_urls, period, unknownFields)companionobject {    @JvmFieldvalADAPTER:ProtoAdapter<Dinosaur>=object:ProtoAdapter<Dinosaur>(FieldEncoding.LENGTH_DELIMITED,Dinosaur::class    ) {overridefunencodedSize(value:Dinosaur):Int=ProtoAdapter.STRING.encodedSizeWithTag(1, value.name)+ProtoAdapter.STRING.asRepeated().encodedSizeWithTag(2, value.picture_urls)+Period.ADAPTER.encodedSizeWithTag(5, value.period)+        value.unknownFields.sizeoverridefunencode(writer:ProtoWriter,value:Dinosaur) {ProtoAdapter.STRING.encodeWithTag(writer,1, value.name)ProtoAdapter.STRING.asRepeated().encodeWithTag(writer,2, value.picture_urls)Period.ADAPTER.encodeWithTag(writer,5, value.period)        writer.writeBytes(value.unknownFields)      }overridefundecode(reader:ProtoReader):Dinosaur {var name:String?=nullval picture_urls= mutableListOf<String>()var period:Period?=nullval unknownFields= reader.forEachTag { tag->when (tag) {1-> name=ProtoAdapter.STRING.decode(reader)2-> picture_urls.add(ProtoAdapter.STRING.decode(reader))5-> period=Period.ADAPTER.decode(reader)else-> reader.readUnknownField(tag)          }        }returnDinosaur(          name= name,          picture_urls= picture_urls,          period= period,          unknownFields= unknownFields        )      }overridefunredact(value:Dinosaur):Dinosaur= value.copy(        unknownFields=ByteString.EMPTY      )    }  }}

Creating and accessing proto models is easy:

val stegosaurus=Dinosaur(    name="Stegosaurus",    period=Period.JURASSIC)println("My favorite dinosaur existed in the${stegosaurus.period} period.")

Here's how you can modify the object to add extra fields:

val stegosaurus= stegosaurus.copy(    picture_urls=listOf("https://www.flickr.com/photos/tags/Stegosaurus/"))println("Here are some photos of${stegosaurus.name}:${stegosaurus.picture_urls}")

Wire gRPC

Since version 3.0.0, Wire supportsgRPC.

Generating Code With Wire

Wire's compiler is available via a Maven plugin. Put.proto sources in your project'ssrc/main/proto directory, then use the plugin to generate.java files. The plugin willautomatically add the generated Java code to your project's source roots.

<build>  <plugins>    <plugin>      <groupId>com.squareup.wire</groupId>      <artifactId>wire-maven-plugin</artifactId>      <version>3.1.0</version>      <executions>        <execution>          <phase>generate-sources</phase>          <goals>            <goal>generate-sources</goal>          </goals>          <configuration>            <includes><!-- proto package names to generate code for-->              <include>squareup.dinosaurs.*</include>              <include>squareup.geology.*</include>            </includes>          </configuration>        </execution>      </executions>    </plugin>  </plugins></build>

Wire can read.proto files from the local file system and from within.jar files.

The compiler can optionally prune your schema to a subset of root types and their transitivedependencies. This is useful when sharing a schema between projects: a Java service and Android appmay each use a subset of a larger shared schema.

If you don't use Maven, the compiler also has a command line interface. Just substitutewire-compiler-VERSION-jar-with-dependencies.jar with the path to your jar.Download the latest precompiled jar.

% java -jar wire-compiler-VERSION-jar-with-dependencies.jar \    --proto_path=src/main/proto \    --java_out=out \    squareup/dinosaurs/dinosaur.proto \    squareup/geology/period.protoWriting com.squareup.dinosaurs.Dinosaur to outWriting com.squareup.geology.Period to out

Supplying the--android flag to the compiler causes Wire messages to implementParcelable.

If you use Proguard, then you need to addkeep rules. The simplest option is to tell Proguard notto touch the Wire runtime library and your generated protocol buffers (of course these simple ruleswill miss opportunities to shrink and optimize the code):

-keep class com.squareup.wire.** { *; }-keep class com.yourcompany.yourgeneratedcode.** { *; }

Get Wire

Thewire-runtime package contains runtime support libraries that must be included in applicationsthat use Wire-generated code.

With Maven:

<dependency>  <groupId>com.squareup.wire</groupId>  <artifactId>wire-runtime</artifactId>  <version>3.1.0</version></dependency>

With Gradle:

api"com.squareup.wire:wire-runtime:3.1.0"

Snapshots of the development version are available inSonatype'ssnapshots repository.

Unsupported

Wire does not support:

  • Groups - they are skipped when parsing binary input data

Wire supports custom options on messages and fields. Other custom options are ignored. Pass--excludes=google.protobuf.* to the compiler to omit options from the generated code.

Further Documentation

SeeGoogle's excellent documentation on the structure and syntax of proto schemas.

About

gRPC and protocol buffers for Android, Kotlin, and Java.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Kotlin55.6%
  • Java44.3%
  • Other0.1%

[8]ページ先頭

©2009-2025 Movatter.jp