Ez a bemutató egy alapvető Dart programozó bevezetés dolgozó protokoll pufferek, a proto3 változata a protocol buffers nyelvet. Egy egyszerű Példaalkalmazás létrehozásával megmutatja, hogyan kell
- meghatározni az üzenetformátumokat egy
.proto
fájlban. - használja a protokoll puffer fordítót.
- használja a dart protocol buffer API-t üzenetek írásához és olvasásához.
Ez nem egy átfogó útmutató a protokoll pufferek használatához a Dartban . További részletes referenciainformációkért lásd a protokoll puffer nyelvi útmutatóját, a Dart nyelvi túrát, a Dart API hivatkozást, a Dart által generált kód útmutatót és a kódolási hivatkozást.
miért használjon protokoll puffereket?
a példa, amelyet használni fogunk, egy nagyon egyszerű” címjegyzék ” alkalmazás, amely képes olvasni és írni az emberek elérhetőségeit egy fájlba és onnan. A címjegyzékben szereplő minden személynek van neve, azonosítója, e-mail címe és kapcsolattartó telefonszáma.
hogyan szerializáljuk és szerezzük vissza a strukturált adatokat? A probléma megoldásának néhány módja van:
- kitalálhat egy ad-hoc módot az adatelemek egyetlen karakterláncba történő kódolására – például 4 ints kódolása “12:3:-23:67”. Ez egy egyszerű és rugalmas megközelítés, bár egyszeri kódolást és elemzési kódot igényel, és az elemzés kis futási költséget jelent. Ez nagyon egyszerű ADATOK kódolásához működik a legjobban.
- Sorosítsa az adatokat XML-be. Ez a megközelítés nagyon vonzó lehet, mivel az XML (egyfajta) ember által olvasható, és sok nyelven vannak kötelező könyvtárak. Ez jó választás lehet, ha adatokat szeretne megosztani más alkalmazásokkal/projektekkel. Az XML azonban közismerten helyigényes, és a kódolás/dekódolás hatalmas teljesítménybüntetést szabhat ki az alkalmazásokra. Ezenkívül az XML DOM fában való navigálás lényegesen bonyolultabb, mint egy osztály egyszerű mezőinek navigálása.
protokoll pufferek a rugalmas, hatékony, automatizált megoldás, hogy megoldja pontosan ezt a problémát. A protokoll pufferekkel .proto
leírást ír a tárolni kívánt adatstruktúráról. Ebből a protokoll puffer fordító létrehoz egy osztályt, amely megvalósítja a protokoll puffer adatok automatikus kódolását és elemzését hatékony bináris formátumban. A generált osztály gettereket és beállítókat biztosít a protokoll puffert alkotó mezők számára, és gondoskodik a protokoll puffer egységként történő olvasásának és írásának részleteiről. Fontos, hogy a protokoll pufferformátum támogatja a formátum időbeli kiterjesztésének gondolatát oly módon, hogy a kód továbbra is olvassa a régi formátummal kódolt adatokat.
hol található a példakód
példánk egy parancssori alkalmazás egy címjegyzék kezelésére, protokoll pufferekkel kódolva.A dart add_person.dart
parancs új bejegyzést ad az adatfájlhoz. A dart list_people.dart
parancs elemzi az adatfájltés kinyomtatja az adatokat a konzolra.
a teljes példát a GitHub repository examples könyvtárában találhatja meg.
A protokoll formátumának meghatározása
a címjegyzék alkalmazás létrehozásához egy.proto
fájllal kell kezdenie. A .proto
fájl definíciói egyszerűekegyszerű: minden egyes sorosítani kívánt adatstruktúrához hozzáad egy üzenetet, majd megad egy nevet és egy típust az üzenet minden mezőjéhez. Példánkban a.proto
az üzeneteket meghatározó fájladdressbook.proto
.
a .proto
fájl egy csomag deklarációval kezdődik, amely segít megelőzni a különböző projektek közötti névkonfliktusokat.
syntax = "proto3";package tutorial;import "google/protobuf/timestamp.proto";
ezután meg kell adnia az üzenet definícióit. Az üzenet csak egy aggregátum, amely egy sor gépelt mezőt tartalmaz. Számos szabványos egyszerű adattípus érhető el mezőtípusként, beleértve a bool
int32
float
double
és string
. Az üzenetekhez további struktúrát is hozzáadhat, ha más üzenettípusokat használ mezőtípusként.
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;}
a fenti példában a Person
az üzenet tartalmazza aPhoneNumber
üzeneteket, míg a AddressBook
messagecontains Person
üzenetek. Akár más üzenetekbe ágyazott üzenettípusokat is megadhat – amint láthatja, aPhoneNumber
típus belül van meghatározva Person
. Megadhatjaa enum
típusokat is, ha azt szeretné, hogy az egyik mezőnek legyen egy előre meghatározott értéklistája – itt meg szeretné adni, hogy egy telefonszám a következő lehet: MOBILE
HOME
, vagyWORK
.
az egyes elemek “= 1”, “= 2” jelölői azonosítják azt az egyedi “címkét”, amelyet a mező a bináris kódolásban használ. Az 1-15-ös címkeszámok kódolásához eggyel kevesebb bájt szükséges, mint a magasabb számok, így optimalizálásként dönthet úgy, hogy ezeket a címkéket használja a gyakran használt vagy ismétlődő elemekhez, a 16-os vagy annál magasabb címkéket hagyva a kevésbé gyakran használt opcionális elemekhez. Az ismétlődő mező minden eleméhez újra kell kódolni a címke számát, így az ismételt mezők különösen jó jelöltek erre az optimalizálásra.
ha nincs beállítva mezőérték, akkor az adefault értéket használjuk: nullanumerikus típusoknál az üres karakterlánc a karakterláncoknál, hamis a bool-oknál. Embeddedmessages esetén az alapértelmezett érték mindig az üzenet” alapértelmezett példánya “vagy” prototípusa”, amelynek egyik mezője sincs beállítva. Az accessor meghívása egy olyan mező értékének megszerzéséhez, amely nincs kifejezetten beállítva, mindig visszaadja a mező alapértelmezett értékét.
Ha egy mező repeated
, a mező tetszőleges számú alkalommal megismételhető (beleértve a nullát is). Az ismételt értékek sorrendje megmarad a protokoll pufferben. Gondolj az ismétlődő mezőkre dinamikusan méretezett tömbökként.
a .proto
fájlok írásának teljes útmutatóját – beleértve az összes lehetséges mezőtípust – a Protocol Buffer Language Guide-ban találja. Ne keressen olyan létesítményeket, amelyek hasonlóak az osztály örökléséhez – a protokoll pufferek ezt nem teszik meg.
A protokoll pufferek összeállítása
most, hogy van egy .proto
, a következő dolog, amit meg kell tennie, hogy létrehozza az olvasandó és írandó osztályokat AddressBook
(és ezért Person
és PhoneNumber
) üzenetek. Ehhez futtatnia kell a protoc
protokoll puffer fordítót a .proto
:
- ha még nem telepítette a fordítót, töltse le a csomagot, és kövesse a README utasításait.
- telepítse a Dart protokoll puffer plugint a README-ben leírtak szerint. A futtatható
bin/protoc-gen-dart
kell lennie aPATH
a protokoll pufferprotoc
megtalálni. - most futtassa a fordítót, megadva a forráskönyvtárat (ahol az alkalmazás forráskódja él – az aktuális könyvtárat használja, ha nem ad meg értéket), a célkönyvtárat (ahová a generált kódot szeretné menni; gyakran ugyanaz, mint a
$SRC_DIR
), valamint a.proto
elérési útját. Ebben az esetben a következőt kell meghívnia:protoc -I=$SRC_DIR --dart_out=$DST_DIR $SRC_DIR/addressbook.proto
mivel Dart kódot szeretne, a
--dart_out
opciót használja – hasonló opciók állnak rendelkezésre más támogatott nyelvekhez is.
ezzel addressbook.pb.dart
generálódik a megadott célkönyvtárban.
A protokoll puffer API
generálóaddressbook.pb.dart
adja meg a következő hasznos típusok:
- egy
AddressBook
osztály egyList<Person> get people
getter. - a
Person
osztály hozzáférési módszerekkel aname
id
email
andphones
. - a
Person_PhoneNumber
osztály, hozzáférési módszerekkel anumber
éstype
számára. - a
Person_PhoneType
osztály statikus mezőkkel minden értékhez aPerson.PhoneType
enum.
a dart által generált kód útmutatóban többet olvashat arról, hogy pontosan mi keletkezik.
Üzenet írása
most próbáljuk meg használni a protokoll puffer osztályokat. Az első dolog, amit szeretne, hogy a címjegyzék-alkalmazás képes legyen megtenni, az, hogy személyes adatokat ír a címjegyzék-fájlba. Ehhez létre kell hoznia és fel kell töltenie a protokoll pufferosztályok példányait, majd be kell írnia őket egy kimeneti adatfolyamba.
itt van egy program, amely beolvassa aAddressBook
fájlt egy fájlból, hozzáad egy újPerson
hozzá a felhasználói bevitel alapján, és kiírja az újAddressBook
vissza a fájlba. Azok a részek vannak kiemelve, amelyek közvetlenül hívják vagy hivatkoznak a protokoll-fordító által generált kódra.
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());}
üzenet olvasása
természetesen egy címjegyzék nem lenne sok haszna, ha nem tudna információt kihozni belőle! Ez a példa beolvassa a fenti példa által létrehozott fájlt, és kinyomtatja az összes információt.
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);}
Protokollpuffer kiterjesztése
előbb vagy utóbb,miután kiadta a protokollpuffert használó kódot, kétségtelenül “javítani” akarja a protokollpuffer meghatározását. Ha azt szeretné, hogy az új pufferei visszafelé kompatibilisek legyenek, a régi pufferei pedig előre kompatibilisek legyenek-és ezt szinte biztosan szeretné -, akkor van néhány szabály, amelyet be kell tartania. Aprotokoll puffer új verziójában:
- nem szabad megváltoztatni a meglévő mezők címkeszámait.
- mezőket törölhet.
- hozzáadhat új mezőket, de friss címkeszámokat kell használnia (azaz olyan címkeszámokat, amelyeket soha nem használtak ebben a protokoll pufferben, még a törölt mezők sem).
( vannak kivételek ezektől a szabályoktól, de ritkán használják őket.)
Ha követi ezeket a Szabályokat, a régi kód boldogan olvassa el az új üzeneteket ésegyszerűen figyelmen kívül hagyja az új mezőket. A régi kódhoz a törölt egyes mezők egyszerűen alapértelmezett értéket kapnak, a törölt ismétlődő mezők pedig üresek lesznek. Az új kód átláthatóan olvassa el a régi üzeneteket is.
ne feledje azonban, hogy az új mezők nem lesznek jelen a régi üzenetekben,ezért valami ésszerű dolgot kell tennie az alapértelmezett értékkel. Atype-specificdefault valueis used: karakterláncok esetén az alapértelmezett érték az üres karakterlánc. Logikai értékek esetén az alapértelmezett érték hamis. Numerikus típusok esetén az alapértelmezett érték nulla.