Alles über Proto DataStore. In diesem Beitrag werden wir etwas über Proto lernen… | von Simona Stojanovic Android-Entwickler Januar 2022

Das erfahren wir in diesem Beitrag Proto-DataStoreeiner von zwei DataStore-Implementierungen. Wir werden darüber sprechen, wie erstellen, Daten lesen und schreiben und Ausnahmen behandelnum die Szenarien besser zu verstehen, die Proto zu einer guten Wahl machen.
Proto DataStore verwendet typisierte Objekte, die von unterstützt werden Protokollpufferkleinere Datensätze beim Bereitstellen zu speichern Typ Sicherheit. Dadurch entfällt die Notwendigkeit, Schlüssel/Wert-Paare zu verwenden strukturell anders von seinem SharedPreferences
Vorgänger und seine zugehörige Implementierung, DataStore-Einstellungen. Doch das ist noch nicht alles – berichtet DataStore viele weitere Verbesserungen fertig SharedPreferences
. Melden Sie sich schnell bei uns zurück erster Beitrag in Folge und schauen Sie sich den detaillierten Vergleich an, den wir dort gemacht haben. Wir werden uns das Folgende ansehen Proto DataStore
wie nur Proto
wenn nicht anders angegeben.
zusammenfassen:
- Bietet ein vollständig asynchrone API zum Herunterladen und Speichern von Daten mit der Kraft von Kotlin Korutina
- Es bietet keine gebrauchsfertige synchrone Unterstützung – vermeidet direkt jegliche Arbeit, die den UI-Thread blockiert
- Es stützt sich auf den internen Fehlersignalisierungsmechanismus von Flow, sodass Sie sicher arbeiten können Ausnahmen abfangen und verarbeiten beim Lesen oder Schreiben von Daten
- Verarbeitet sicher Datenaktualisierungen in Atomare Read-Modify-Write-Operationstark zur Verfügung stellen SÄURE Garantien
- Erlaubt einfache und einfache Datenmigration
- Müssen vollständige Sicherheit und Ihre Daten erfordern die Arbeit mit mehr komplexe Klassen, wie Aufzählungen oder Listen? Dies ist mit nicht möglich Einstellungenalso wähle Proto stattdessen
Um Proto DataStore nutzen zu können, müssen Sie sich kennenlernen Protokollpuffer – sprachneutraler, plattformneutraler Mechanismus für Serialisierung strukturierter Daten. Es ist schneller, kleiner, einfacher und eindeutiger als XML und leichter zu lesen als andere ähnliche Datenformate.
Sie definieren ein Schema aus wie Sie Ihre Daten strukturiert haben möchten und geben Sie Optionen an, z. B. welche Sprache zum Generieren des Codes verwendet werden soll. Der Compiler generiert dann Klassen nach Ihren Vorgaben. Dadurch können Sie problemlos strukturierte Daten schreiben und lesen zu und von verschiedenen Datenströmen, Austausch zwischen verschiedenen Plattformen, Verwendung mehrerer verschiedener Sprachen, z. Kotlin.
Beispielschema einiger Daten ua .proto
Datei:
So verwenden Sie den generierten Kotlin-Code zum Erstellen Ihres Datenmodells:
Oder Sie können das neu Angekündigte ausprobieren Kotlin DSL-Unterstützung für Protokollpuffer für eine idiomatischere Art, Ihr Datenmodell zu erstellen:
Etwas mehr Zeit in das Erlernen dieses neuen Serialisierungsmechanismus zu investieren, lohnt sich auf jeden Fall, denn er bringt Typsicherheit, verbesserte Lesbarkeit und allgemeine Einfachheit des Codes.
Schauen wir uns jetzt etwas Code an und lernen, wie Proto funktioniert.
Wir werden verwenden Proto-DataStore Codelab-Muster. Wenn Sie an einem praktischeren Ansatz zur Implementierung interessiert sind, empfehlen wir Ihnen wirklich, es durchzugehen Arbeiten Sie mit Proto DataStore Codelab selbstständig.
Diese Beispielanwendung zeigt eine Liste von Aufgaben an, und der Benutzer kann sie nach ihrem abgeschlossenen Status filtern oder nach Priorität und Frist sortieren. Wir möchten ihre Auswahl speichern boolean, um abgeschlossene Aufgaben anzuzeigen ia Aufzählung sortieren Proto.
Wir werden zunächst Proto-Abhängigkeiten und einige der grundlegenden Protobuff-Einstellungen zu Ihrem Modul hinzufügen build.gradle
. Wenn Sie an einer fortgeschritteneren Anpassung der Protobuffs-Kompilation interessiert sind, werfen Sie einen Blick darauf Protobuf-Plugin für Gradle-Notizen:
💡 Kurzer Tipp – Wenn Sie Ihre Konstruktion minimieren möchten, fügen Sie unbedingt eine zusätzliche Regel hinzu
proguard-rules.pro
Datei, um zu verhindern, dass Ihre Felder gelöscht werden:
Unsere Reise mit Proto beginnt mit der Definition Ihrer Struktur persistente Daten u .proto
Datei. Betrachten Sie es als lesbares Schema für dich u.a Entwurf für den Compiler. Wir werden unsere nennen user_prefs.proto
und füge es hinzu app/src/main/proto
Verzeichnis.
Begleitet Protobuf-SprachführerWir werden dieser Datei ein hinzufügen Nachricht für jede Datenstruktur wir wollen serialisieren und dann a spezifizieren Name ia Typ für jedes Feld in der Nachricht. Um dies einfacher zu visualisieren, schauen wir uns sowohl die Kotlin-Datenklasse als auch das entsprechende Protobuff-Schema an.
UserPreferences
– Kotlin-Datenklasse:
UserPreferences
– .proto
planen:
Wenn Sie noch nie Protobuffs verwendet haben, interessieren Sie sich vielleicht für die ersten Zeilen des Schemas. Lassen Sie uns sie aufschlüsseln:
syntax
– zeigt an zu verwendenproto3
Syntaxjava_package
– eine Dateioption, die angibt Paketerklärung für Ihre generierten Klassen, was dazu beiträgt, dies zu verhindern Namenskonflikte zwischen verschiedenen Projektenjava_multiple_files
– eine Dateioption, die angibt, ob nur eine einzelne Datei mit verschachtelten Unterklassen werden hierfür generiert.proto
(wenn auf false gesetzt) oder if getrennte Dateien wird für jeden Nachrichtentyp der obersten Ebene generiert (wenn auf wahr gesetzt); es ist standardmäßig falsch
Das Folgende ist die Definition unserer Botschaft. Eine Nachricht ist ein Aggregat, das enthält eine Reihe von typisierten Feldern. Viele einfache Standarddatentypen sind als Feldtypen verfügbar, einschließlich bool
, int32
, float
doppelt i string
. Sie können Ihre Nachrichten auch mit weiter strukturieren andere Nachrichtentypen als Feldtypenwie wir mit SortOrder
.
Der = 1
, = 2
Die Markierungen auf jedem Element identifizieren das eindeutige “Tag”, das das Feld in der Binärcodierung verwendet – wie z. B. die Sortier-ID. Wenn Ihr Nachrichtentyp verwendet wird, müssen diese Nummern nicht geändert werden.
Wenn Sie den Protokollpuffer-Compiler auf einem ausführen .proto,
Der Compiler generiert Code in der von Ihnen ausgewählten Sprache. In unserem speziellen Fall führt dies beim Start des Compilers zur Generierung UserPreferences
Klasse, die in Ihrer Bewerbung enthalten ist build/generated/source/proto
… Verzeichnis:
💡 Schneller Tipp – Du kannst auch die neu angekündigten ausprobieren Kotlin DSL-Unterstützung für Protokollpuffer um eine idiomatischere Methode zum Erstellen Ihres Datenmodells zu verwenden.
Jetzt, wo wir haben UserPreferences
wir müssen angeben Richtlinien wie Proto sie lesen und schreiben soll. Wir tun dies über den DataStore Serializer
es bestimmt das endgültige Format Ihrer Daten wann es gespeichert wird und wie man richtig darauf zugreift. Dies erfordert das Überschreiben von:
defaultValue
– was zurückgegeben werden soll, wenn keine Daten vorhanden sindwriteTo
– wie man die Speicheranzeige unseres Datenobjekts in ein für die Speicherung geeignetes Format umwandeltreadFrom
– die Umkehrung des Obigen, wie man von einem Speicherformat in eine geeignete Speicheranzeige umwandelt
Um Ihren Code so sicher wie möglich zu machen, zu handhaben CorruptionException
um unangenehme Überraschungen zu vermeiden, wenn die Datei aufgrund einer Formatbeschädigung nicht deserialisiert werden kann.
💡 Kurztipp – Wenn dein AS mal nichts finden kann
UserPreferences
in Verbindung gebracht, reinigen und erneuern Ihr Projekt, um mit der Generierung von Protobuff-Klassen zu beginnen.
Sie interagieren mit Proto über Instanzen DataStore<UserPreferences>
. DataStore
ist eine Schnittstelle, die bietet Zugriff auf dauerhafte Informationenin unserem Fall in der generierten Form UserPreferences
.
Es wird empfohlen, zum Erstellen dieser Instanz einen Delegaten zu verwenden dataStore
und Pass erforderlich fileName
ich serializer
Argumente:
fileName
verwendet, um a zu erstellen File
verwendet, um Daten zu speichern. Deshalb ist es so dataStore
Der Delegierte ist a Kotlin-Eigenschaftserweiterung dessen Empfängertyp eine Instanz sein muss Context
weil es benötigt wird, um eine Datei über zu erstellen applicationContext.filesDir
. Vermeiden Sie die Verwendung dieser Datei außerhalb von Proto, da dies die Konsistenz Ihrer Daten beeinträchtigen würde.
U dataStore
Delegierten können Sie ein weiteres optionales Argument weiterleiten – corruptionHandler
. Dieser Operator heißt, wenn a CorruptionException
wurde vom Serialisierer ausgeworfen, als Daten können nicht deserialisiert werden. corruptionHandler
würde dann Proto anweisen, wie die beschädigten Daten zu ersetzen sind:
Sie sollten nicht mehr als eine DataStore-Instanz für eine bestimmte Datei erstellen, weil es die gesamte Funktionalität des DataStore ruinieren kann. Daher können Sie ein Delegate-Konstrukt einmal auf der obersten Ebene Ihrer Kotlin-Datei hinzufügen und es in der gesamten Anwendung verwenden, um es als zu übergeben Einzelling. Wie das mit einer Suchtspritze geht, sehen wir uns in späteren Beiträgen an.
Zum Auslesen gespeicherter Daten, u UserPreferencesRepository
wir belichten a Flow<UserPreferences>
von userPreferencesStore.data
. Dies ermöglicht ein effizientes Vorgehen der letzte erhaltene Zustand ich Sendungen bei jeder Änderung. Dies ist einer von größte Stärke von Proto – deins Flow
‘s Werte kommen bereits in der generierten Form UserPreferences
. Das heisst Sie müssen keine zusätzlichen Transformationen vornehmen von den gespeicherten Daten zum Kotlin-Datenklassenmodell, so wie Sie es tun würden SharedPreferences
oder Preferences DataStore
:
Flow wird immer entweder einen Wert ausgeben oder verwerfen Sie die Ausnahme beim Versuch, von der Festplatte zu lesen. Wir werden uns in späteren Abschnitten mit der Behandlung von Ausnahmen befassen. Der DataStore stellt außerdem sicher, dass die Arbeit immer erledigt wird Dispatchers.IO
Ihr UI-Thread wird also nicht blockiert.
🚨 Erstellen Sie keine Cache-Repositories um den aktuellen Stand Ihrer Proto-Daten abzubilden. Damit erlischt die DataStore-Garantie für Datenkonsistenz. Wenn Sie eine Aufnahme Ihrer Daten ohne ein Abonnement für weitere Flow-Shows benötigen, verwenden Sie sie stattdessen userPreferencesStore.data.first()
:
Wir werden die Aussetzung verwenden, um Daten zu schreiben DataStore<UserPreferences>.updateData(transform: suspend (t: T) -> T)
Funktion.
Lassen Sie es uns aufschlüsseln:
DataStore<UserPreferences>
Schnittstelle – wird derzeit verwendetuserPreferencesStore
als konkrete Implementierung von Prototransform: suspend (t: T) -> T)
– ein Suspend-Block, der verwendet wird, um bestimmte Änderungen an unseren permanenten T-Typ-Daten anzuwenden
Auch hier können Sie einen Unterschied bemerken Preferences DataStore
die auf den Gebrauch angewiesen ist Preferences
ich MutablePreferences
ähnlich Map
ich MutableMap
als Standarddatenanzeige.
Jetzt können wir dies nutzen, um unsere zu ändern showCompleted
boolesch. Protokollpuffer vereinfachen dies ebenfallsEliminierung der Notwendigkeit einer manuellen Transformation in und aus Datenklassen:
Es gibt mehrere Schritte für die Analyse:
toBuilder()
– er gewannBuilder
Version von unscurrentPreferences
die es für Änderungen „freischaltet“..setShowCompleted(completed)
– setzt einen neuen Wert.build()
– Beendet den Update-Vorgang durch Konvertieren inUserPreferences
Die Datenaktualisierung erfolgt transaktional in einem Atomare Read-Modify-Write-Operation. Das bedeutet, dass es eine bestimmte Abfolge von Datenverarbeitungsvorgängen garantiert, während derer Daten für andere Threads gesperrt sind Konsistenz ich verhindert Rennbedingungen. Nur nach transform
ich updateData
Corutins erfolgreich abgeschlossen, Daten werden dauerhaft auf der Festplatte gespeichert und userPreferencesStore.data
Der Fluss wird die Aktualisierung widerspiegeln.
🚨 Beachten Sie, dass dies die einzige Möglichkeit ist, den Status des DataStore zu ändern. Wartung u UserPreferences
Referenz und manuelle Mutation danach transform
vervollständigt ändert die permanenten Daten nicht in Proto, also sollten Sie nicht versuchen zu modifizieren UserPreferences
außen transform
Block.
Wenn der Schreibvorgang aus irgendeinem Grund fehlschlägt, wird die Transaktion beendet und eine Ausnahme ausgelöst.
Wenn Sie es zuvor verwendet haben SharedPreferences
in Ihrer Anwendung und Sie Ihre Daten sicher an Proto übertragen möchten, können Sie verwenden SharedPreferencesMigration
. Dazu braucht es Kontext, SharedPreferences
Namen und Anweisungen, wie Sie Ihren eigenen umwandeln können SharedPreferences
Schlüssel/Wert-Paare für UserPreferences
Innerhalb migrate
Parameter. Geben Sie dies weiter produceMigrations
ParameterdataStore
Delegierter für einfache Migration:
In diesem Beispiel durchlaufen wir den Konstruktionsprozess UserPreferences
und seine Einstellung sortOrder
zu dem, was zuvor in der entsprechenden gespeichert wurde SharedPreferences
Schlüssel-Wert-Paar oder einfach auf NONE gesetzt.
produceMigrations
wird dafür sorgen migrate()
wurde gestartet vor einem möglichen Zugriff auf Daten im Datenspeicher. Das bedeutet Ihre Migration ihm muss es gelungen sein bevor der DataStore weitere Werte aussendet und bevor er mit neuen Datenänderungen beginnt. Sobald Sie erfolgreich migriert haben, können Sie die Verwendung sicher einstellen SharedPreferences
wie Schlüssel nur einmal migriert und dann ENTFERNT von SharedPreferences
.
Der produceMigrations
akzeptiert die Liste DataMigration
. Wir werden in späteren Episoden sehen, wie wir dies für andere Arten von Datenmigrationen verwenden können. Wenn Sie nicht migrieren müssen, können Sie dies ignorieren, da es eine gibt Ursprünglich listOf()
bereitgestellt schon.
Einer der Hauptvorteile von DataStore gegenüber SharedPreferences
ist seine ein geordneter Mechanismus zum Erfassen und Behandeln von Ausnahmen. Während SharedPreferences
wirft Parsing-Fehler als Laufzeitausnahmen aus und lässt Raum für unerwartete, nicht abgefangene Abstürze, DataStore wirft aus IOException
wenn beim Lesen/Schreiben von Daten ein Fehler auftritt.
Wir können dies sicher lösen, indem wir verwenden catch()
Flow- und Broadcast-Betreiber getDefaultInstance()
:
Oder mit dem einfachen try-catch
Blockschreiben:
Wenn eine andere Art von Ausnahme ausgelöst wird, sollten Sie sie besser erneut auslösen.
Wir deckten ab Protokollpuffer ich Das Proto von DataStore Implementierung – wann und wie man damit Daten liest und schreibt, wie man mit Fehlern umgeht und wie man sie überwindet SharedPreferences
. Im nächsten und letzten Beitrag gehen wir noch einen Schritt weiter und sehen, wie sich der DataStore in Ihren einfügt Anwendungsarchitektur, wie man es mit Hilt injiziert und natürlich, wie man es testet. Seh dich später!