denne opplæringen gir en Grunnleggende Innføring i Dart-programmererens bruk av protokollbuffere ved hjelp av proto3-versjonen av protokollbuffere-språket. Ved å gå gjennom å lage et enkelt eksempelprogram, viser det deg hvordan Du Definerer meldingsformater I en .proto
fil.
Dette er ikke en omfattende guide til bruk av protokollbuffere i Dart . Hvis du vil ha mer detaljert referanseinformasjon, kan du se Språkveiledningen For Protokollbuffer, Språkveiledningen For Dart, Referansen For Dart API, Veiledningen For Dart – Generert Kode og Kodingsreferansen.
Hvorfor bruke protokollbuffere?
eksemplet vi skal bruke er et veldig enkelt «adressebok» – program som kan lese og skrive folks kontaktinformasjon til og fra en fil. Hver person i adresseboken har et navn, EN ID, en e-postadresse og et telefonnummer.
Hvordan serialiserer og henter du strukturerte data som dette? Det er noen måter å løse dette problemet på:
- du kan oppfinne en ad hoc måte å kode dataelementene i en enkelt streng – for eksempel koding av 4 ints som «12:3:-23:67». Dette er en enkel og fleksibel tilnærming, selv om det krever å skrive engangs koding og parsing kode, og parsing pålegger en liten kjøretidskostnad. Dette fungerer best for koding av svært enkle data.
- Serialiser dataene TIL XML. DENNE tilnærmingen kan være veldig attraktiv siden XML er (slags) lesbar og det er bindende biblioteker for mange språk. Dette kan være et godt valg hvis du vil dele data med andre applikasjoner/prosjekter. XML er imidlertid notorisk romintensiv, og koding / dekoding kan pålegge en stor ytelsesstraff på applikasjoner. Også, navigere EN XML DOM treet er betydelig mer komplisert enn å navigere enkle felt i en klasse normalt ville være.
Protokollbuffere er den fleksible, effektive, automatiserte løsningen for å løse akkurat dette problemet. Med protokollbuffere skriver du en .proto
beskrivelse av datastrukturen du ønsker å lagre. Fra det oppretter protokollbufferkompilatoren en klasse som implementerer automatisk koding og parsing av protokollbufferdataene med et effektivt binært format. Den genererte klassen gir getters og settere for feltene som utgjør en protokollbuffer og tar seg av detaljene for å lese og skrive protokollbufferen som en enhet. Det er viktig at protokollbufferformatet støtter ideen om å utvide formatet over tid på en slik måte at koden fortsatt kan lese data kodet med det gamle formatet.
hvor finner du eksempelkoden
vårt eksempel Er et sett med kommandolinjeprogrammer for å administrere en adressebokdatafil, kodet ved hjelp av protokollbuffere.Kommandoen dart add_person.dart
legger til en ny oppføring i datafilen. Kommandoendart list_people.dart
analyserer datafilen og skriver ut dataene til konsollen.
du kan finne det komplette eksemplet ieksempler katalogav GitHub-depotet.
Definere protokollformatet
for å opprette adressebokprogrammet må du starte med en.proto
fil. Definisjonene i en .proto
fil aresimple: du legger til en melding for hver datastruktur du vil serialisere, og angi deretter et navn og en type for hvert felt i meldingen. I vårt eksempel er.proto
– filen som definerer meldingene addressbook.proto
.
filen.proto
starter med en pakkedeklarasjon som hjelperfor å forhindre navnekonflikter mellom ulike prosjekter.
syntax = "proto3";package tutorial;import "google/protobuf/timestamp.proto";
Deretter har du dine meldingsdefinisjoner. En melding er bare et aggregatinneholder et sett med skrevne felt. Mange standard enkle datatyper er tilgjengelige som felttyper, inkludert bool
int32
float
double
og string
. Du kan også legge til ytterligere struktur i meldingene dine ved å bruke andre meldingstyper som felttyper.
message Person { string name = 1; int32 id = 2; // Unique ID number for this person. string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phones = 4; google.protobuf.Timestamp last_updated = 5;}// Our address book file is just one of these.message AddressBook { repeated Person people = 1;}
i eksemplet ovenfor inneholder Person
meldingenPhoneNumber
meldinger, mens AddressBook
messagecontains Person
meldinger. Du kan til og med definere meldingstypernested inne i andre meldinger – som du kan se, erPhoneNumber
typen definert inne Person
. Du kan også definere enum
typer hvis du vil at et av feltene skal ha en forhåndsdefinert liste over verdier – her vil du angi at et telefonnummer kan være et av MOBILE
HOME
ellerWORK
.
markørene «= 1», «= 2» på hvert element identifiserer den unike «taggen» som feltet bruker i den binære kodingen. Tag tallene 1-15 krever en mindre byte å kode enn høyere tall, slik som en optimalisering kan du velge å bruke disse kodene for de brukte eller gjentatte elementer, forlater koder 16 og høyere for mindre brukte valgfrie elementer. Hvert element i et gjentatt felt krever omkoding av kodenummeret, så gjentatte felt er spesielt gode kandidater for denne optimaliseringen.
hvis en feltverdi ikke er angitt, brukes adefault-verdi: zerofor numeriske typer, den tomme strengen for strenger, false for bools. For embeddedmessages er standardverdien alltid» standardforekomst «eller» prototype » av meldingen, som ikke har noen av feltene angitt. Kaller accessor å få verdien av et felt som ikke er eksplisitt angitt alltid returnerer feltet ‘ standardverdien.
hvis et felt er repeated
, kan feltet gjentas et hvilket som helst antall ganger (inkludert null). Ordren av de gjentatte verdiene vil bli bevart i protokollbufferen. Tenk på gjentatte felt som dynamisk størrelse arrays.
du finner en komplett veiledning for å skrive .proto
filer-inkludert alle mulige felttyper-i Språkveiledningen For Protokollbuffer. Ikke gå på jakt etter fasiliteter som ligner på klassearv, men protokollbuffere gjør det ikke.
Kompilere protokollbuffere
nå som du har en .proto
, er det neste du må gjøre å generere klassene du må lese og skrive AddressBook
(og dermed Person
og PhoneNumber
) meldinger. For å gjøre dette må du kjøre protokollbufferkompilatoren protoc
på .proto
:
- hvis du ikke har installert kompilatoren, last ned pakken og følg instruksjonene I README.
- Installer Dart Protocol Buffer plugin som beskrevet i SIN README. Den kjørbare
bin/protoc-gen-dart
må være iPATH
for protokollbufferenprotoc
for å finne den. - kjør nå kompilatoren, og spesifiser kildekatalogen – der programmets kildekode lever-gjeldende katalog brukes hvis du ikke oppgir en verdi), målkatalogen (der du vil at den genererte koden skal gå, ofte den samme som
$SRC_DIR
) og banen til.proto
. I dette tilfellet vil du påberope deg:protoc -I=$SRC_DIR --dart_out=$DST_DIR $SRC_DIR/addressbook.proto
fordi Du vil Ha Dart-kode, bruker du
--dart_out
– alternativet-lignende alternativer finnes for andre støttede språk –
dette genererer addressbook.pb.dart
i den angitte målkatalogen.
PROTOKOLLBUFFEREN API
Generereraddressbook.pb.dart
gir deg følgende nyttige typer:
- En
AddressBook
klasse med enList<Person> get people
getter. - A
Person
klasse med tilbehørsmetoder forname
id
email
ogphones
. - A
Person_PhoneNumber
klasse, med tilbehørsmetoder fornumber
ogtype
. - a
Person_PhoneType
klasse med statiske felt for hver verdi iPerson.PhoneType
enum.
du kan lese mer om detaljene for nøyaktig hva som genereres i Dart Generert Kode guide.
Skrive En Melding
la Oss nå prøve å bruke protokollbufferklassene dine. Det første du vil at adressebokprogrammet skal kunne gjøre, er å skrive personlige opplysninger til adressebokfilen din. For å gjøre dette må du opprette og fylle ut forekomster av protokollbufferklassene dine og deretter skrive dem til en utgangsstrøm.
Her er et program som leser en AddressBook
fra en fil, legger til en ny Person
til den basert på brukerinngang, og skriver den nye AddressBook
tilbake til filen igjen. Delene som direkte ringer eller referansekode generert av protokollkompilatoren er uthevet.
import 'dart:io';import 'dart_tutorial/addressbook.pb.dart';// This function fills in a Person message based on user input.Person promtForAddress() { Person person = Person(); print('Enter person ID: '); String input = stdin.readLineSync(); person.id = int.parse(input); print('Enter name'); person.name = stdin.readLineSync(); print('Enter email address (blank for none) : '); String email = stdin.readLineSync(); if (email.isNotEmpty) { person.email = email; } while (true) { print('Enter a phone number (or leave blank to finish): '); String number = stdin.readLineSync(); if (number.isEmpty) break; Person_PhoneNumber phoneNumber = Person_PhoneNumber(); phoneNumber.number = number; print('Is this a mobile, home, or work phone? '); String type = stdin.readLineSync(); switch (type) { case 'mobile': phoneNumber.type = Person_PhoneType.MOBILE; break; case 'home': phoneNumber.type = Person_PhoneType.HOME; break; case 'work': phoneNumber.type = Person_PhoneType.WORK; break; default: print('Unknown phone type. Using default.'); } person.phones.add(phoneNumber); } return person;}// Reads the entire address book from a file, adds one person based// on user input, then writes it back out to the same file.main(List arguments) { if (arguments.length != 1) { print('Usage: add_person ADDRESS_BOOK_FILE'); exit(-1); } File file = File(arguments.first); AddressBook addressBook; if (!file.existsSync()) { print('File not found. Creating new file.'); addressBook = AddressBook(); } else { addressBook = AddressBook.fromBuffer(file.readAsBytesSync()); } addressBook.people.add(promtForAddress()); file.writeAsBytes(addressBook.writeToBuffer());}
Lese En Melding
selvfølgelig ville en adressebok ikke være mye bruk hvis du ikke kunne få noen informasjon ut av det! Dette eksemplet leser filen opprettet av eksemplet ovenfor og skriver ut all informasjonen i den.
import 'dart:io';import 'dart_tutorial/addressbook.pb.dart';import 'dart_tutorial/addressbook.pbenum.dart';// Iterates though all people in the AddressBook and prints info about them.void printAddressBook(AddressBook addressBook) { for (Person person in addressBook.people) { print('Person ID: ${ person.id}'); print(' Name: ${ person.name}'); if (person.hasEmail()) { print(' E-mail address:${ person.email}'); } for (Person_PhoneNumber phoneNumber in person.phones) { switch (phoneNumber.type) { case Person_PhoneType.MOBILE: print(' Mobile phone #: '); break; case Person_PhoneType.HOME: print(' Home phone #: '); break; case Person_PhoneType.WORK: print(' Work phone #: '); break; default: print(' Unknown phone #: '); break; } print(phoneNumber.number); } }}// Reads the entire address book from a file and prints all// the information inside.main(List arguments) { if (arguments.length != 1) { print('Usage: list_person ADDRESS_BOOK_FILE'); exit(-1); } // Read the existing address book. File file = new File(arguments.first); AddressBook addressBook = new AddressBook.fromBuffer(file.readAsBytesSync()); printAddressBook(addressBook);}
Utvide En Protokollbuffer
Før eller senere etter at du slipper koden som bruker protokollbufferen, vil du utvilsomt ønske å «forbedre» protokollbufferens definisjon. Hvis du vil at dine nye buffere skal være bakoverkompatible – og dine gamle buffere til beforward-kompatible – og du vil nesten sikkert ha dette-så er det noen regler du må følge. I den nye versjonen avprotocol buffer:
- du må ikke endre tagnumrene til eksisterende felt.
- du kan slette felt.
- du kan legge til nye felt, men du må bruke ferske tagnumre(dvs. tagnumre som aldri ble brukt i denne protokollbufferen, ikke engang av slettede felt).
(det ernoen unntak tildisse reglene, men de brukes sjelden.)
hvis du følger disse reglene, vil gammel kode gjerne lese nye meldinger ogbare ignorere eventuelle nye felt. Til den gamle koden, singulære felt som varslettet vil bare ha standardverdien, og slettede gjentatte felt vil være tomme. Ny kode vil også transparent lese gamle meldinger.
husk imidlertid at nye felt ikke vil være til stede i gamle meldinger, så du må gjøre noe rimelig med standardverdien. Atype-specificdefault value brukes: for strenger er standardverdien den tomme strengen. For booleans er standardverdien usann. For numeriske typer er standardverdien null.