Geheimnisse der API von Android-GerÀten. Yandex-Bericht

Eine der grĂ¶ĂŸten Herausforderungen bei der Android-Entwicklung ist die Fragmentierung. Fast jeder Hersteller Ă€ndert Android an seine BedĂŒrfnisse. Entwickler Andrey Makeev listete die Unterschiede zwischen Herstellerimplementierungen und dem ursprĂŒnglichen Android Open Source-Projekt auf. Aus dem Bericht erfahren Sie, wie Sie von den einzelnen Funktionen der Firmware auf verschiedenen GerĂ€ten profitieren können.


- Ich programmiere seit der Schule und entwickle es seit drei Jahren fĂŒr Android. Davon verbrachte ich ein Jahr in Yandex und nahm an Projekten wie Launcher und Phone teil.



Ich möchte darĂŒber sprechen, wie die Fragmentierung der API von Android-GerĂ€ten aussieht - von außen, von Seiten der Anwendungsentwickler und von innen, aus Sicht der Entwickler der Plattform und der Telefone.

Mein Bericht besteht aus zwei Teilen. Lassen Sie uns zunĂ€chst darĂŒber sprechen, wie die API extern fragmentiert ist. Dann werden wir den Code durchgehen - wir werden herausfinden, wie das einzigartige Merkmal des abstrakten Telefons realisiert wird, wie die Entwicklung aufgebaut ist.

Die API-Fragmentierung ist einer der Parameter, mit denen Sie ein GerĂ€t fragmentieren können. Das offensichtlichste und einfachste ist die Fragmentierung gemĂ€ĂŸ dem Android SDK. Wir begegnen ihr jeden Tag, buchstĂ€blich von den ersten Tagen der Entwicklung fĂŒr Android an. Wir wissen, welche und in welcher Version der API es erschien, dass es entfernt wurde, dass es gesichert wurde, aber es ist noch verfĂŒgbar und welche bereits verschwunden ist. In der Regel verwenden wir verschiedene Support-Bibliotheken von Google, um unser Leben zu vereinfachen. Es wurde bereits viel fĂŒr uns getan.





In unserem Code sieht es ungefĂ€hr so ​​aus: Wir aktivieren einige Funktionen, einige deaktivieren - je nachdem, in welcher Version des SDK wir uns gerade befinden. Wenn Sie welche verwenden, tun sie normalerweise dasselbe, aber im Inneren.

Wir werden uns nicht auf diese Art der Fragmentierung konzentrieren. Die Nachteile sind allen bekannt - wir sind gezwungen, eine ganze Flotte von GerĂ€ten mit unterschiedlichen Versionen zu behalten, um zumindest unsere Anwendung zu testen. Außerdem mĂŒssen wir zusĂ€tzlichen Code schreiben. Dies ist besonders unpraktisch, als Sie gerade mit der Entwicklung fĂŒr Android begonnen haben, und es stellt sich plötzlich heraus: Sie mĂŒssen lernen, was vor zwei oder drei Jahren dort war, um einige alte GerĂ€te zu unterstĂŒtzen. Positive Aspekte: API entwickelt sich, Technologie entwickelt sich weiter, Android gewinnt neue Benutzer, es wird sowohl fĂŒr Entwickler als auch fĂŒr Benutzer bequemer.

Wie arbeiten wir damit? Wir verwenden auch Bibliotheken und freuen uns sehr, wenn wir Ă€ltere Versionen nicht unterstĂŒtzen. Ich denke, im Leben aller, die dies seit mehr als einem Jahr tun, gab es einen solchen Moment. Das ist nur GlĂŒck. Dies ist eine offensichtliche und einfache Fragmentierungsoption. Als nĂ€chstes folgt die Fragmentierung vom Typ Android. Es gibt mehrere von ihnen. Android TV spricht fĂŒr sich selbst, Android Auto ist hauptsĂ€chlich fĂŒr Autoradios, Android Things - fĂŒr IoT und Ă€hnliche eingebettete GerĂ€te gedacht. W is Wear OS, die frĂŒhere Android Watch, achten Sie auf Android. Wir werden versuchen zu ĂŒberlegen, wie dies von Seiten des Entwicklers aussieht. Und am interessantesten ist, dass wir versuchen zu ĂŒberlegen, wie es von innen aussieht.



Nehmen Sie zwei Beispiele von developer.android.com. Das erste ist Wear OS. Was brauchen wir, um einen Antrag zu stellen? Wir fĂŒgen eine compileOnly-AbhĂ€ngigkeit zu build.gradle hinzu und schreiben zwei zusĂ€tzliche Zeilen in das Manifest: Verwendungsfunktion android.hardware.type.watch und Verwendungsbibliothek, die demselben Paketnamen entspricht wie die Bibliothek, die wir verbunden haben.



Wie setzen wir etwas um? Wir erstellen eine AktivitĂ€t, nur in diesem Fall geben wir keine StandardaktivitĂ€t aus, mit der wir gewohnt sind, und nicht einmal eine zusammengesetzte, sondern WearableActivity, und rufen dafĂŒr spezifische Methoden auf, in diesem Fall setAmbientEnabled (). Wir haben also eine compileOnly-AbhĂ€ngigkeit, das heißt, sie gelangt nicht in den Verlauf unserer Anwendung. Uses-Bibliothek, die das Betriebssystem anscheinend dazu zwingt, diese Klassen und diesen Code zur Laufzeit auf dem GerĂ€t und den neuen Klassen, die wir verwenden, mit uns zu verbinden.



Android Things API ist praktisch nicht anders. Wir schreiben keine Verwendungsfunktion vor, sondern nur Verwendungsbibliothek, compileOnly-AbhÀngigkeit.



Wir erstellen AktivitĂ€ten. In diesem Fall gilt dies nur fĂŒr die Android Things-API, die PeripheralManager-Klasse. Wir befragen den GPIO und versuchen zu versprechen.



Wie verhÀlt sich eine solche App auf Ihrem Handy? Es gibt zwei Möglichkeiten.



Wenn wir angegeben haben, dass die Verwendungsbibliothek android: required = "true" ist, haben wir die obligatorischen Anforderungen des PackageManager fĂŒr die Installation der Anwendung nicht erfĂŒllt und sie wird grundsĂ€tzlich nicht installiert. Wenn wir android: required = ”false” angegeben haben, wird die Anwendung installiert. Wenn wir jedoch versuchen, auf die PeripheralManager-Klasse zuzugreifen, erhalten wir NoClassDefFoundError, da es in Standard-Android keine solche Klasse gibt.

Was sind die Schlussfolgerungen? Wir verbinden die compileOnly-AbhĂ€ngigkeit nur, um wĂ€hrend der Assembly mit ihr in Kontakt zu treten, und die von uns verwendeten Klassen warten auf dem GerĂ€t auf uns. Sie werden ĂŒber bestimmte Zeilen im Manifest verbunden. Manchmal schreiben wir eine Funktion vor, die hĂ€ufiger benötigt wird, um bei Google Play ein GerĂ€t zu unterscheiden, auf das diese Anwendung verteilt werden kann oder nicht.

Ich konnte die negativen Seiten dieser Art der Fragmentierung nicht herausgreifen. Nur diejenigen, die sich entwickelt haben, werden viele Geschichten darĂŒber erzĂ€hlen, wie sie auf völlig unverstĂ€ndliche, unbekannte Fehler gestoßen sind, denen sie am Telefon nicht begegnet sind. Die positive Seite ist, dass dies zusĂ€tzliche MĂ€rkte, zusĂ€tzliche Benutzer, zusĂ€tzliche Erfahrung sind, es ist immer gut.

Wie arbeite ich damit? Schreiben Sie weitere Bewerbungen. Die allgemeine Empfehlung lautet, mehr als eine Version der Anwendung fĂŒr alle Arten von Android zu schreiben, aber immer noch andere. Es sollte weniger fĂŒr eine Uhr geben, fĂŒr Android-Dinge ist praktisch nichts von dem, was auf dem Telefon geschrieben ist, geeignet und so weiter. Und nutzen Sie die Bibliotheken, die uns Android-Entwickler und manchmal auch GerĂ€teentwickler zur VerfĂŒgung stellen.

Die am wenigsten untersuchte Art der Fragmentierung ist die Produzentenfragmentierung. Jeder Hersteller, der den Quellcode erhalten hat - in seltenen FĂ€llen handelt es sich um AOSP, hĂ€ufiger wird er von den Hardwareentwicklern irgendwie geĂ€ndert -, nimmt Änderungen daran vor. In der Regel erfahren wir ĂŒber die negativen Auswirkungen dieser Art der Fragmentierung von nicht den besten KanĂ€len - von negativen Bewertungen bei Google Play, weil jemand etwas kaputt gemacht hat. Oder wir lernen dies aus der Crash-Analyse, wenn plötzlich etwas auf unverstĂ€ndliche Weise abstĂŒrzt, nur auf bestimmten GerĂ€ten. Im besten Fall lernen wir dies aus unserer QualitĂ€tssicherung, wenn beim Testen auf einem bestimmten GerĂ€t etwas kaputt gegangen ist.



Zu diesem Thema habe ich eine wundervolle Geschichte aus unserem Entwicklungs-Launcher. Wir haben einen Fehlerbericht erhalten, in dem sich die AktivitĂ€t nicht auf den gesamten Bildschirm erstreckte und unser bevorzugtes Standard-Hintergrundbild ĂŒberhaupt nicht angezeigt wurde. Es wurde nicht entschlĂŒsselt, ein leeres Fenster zeigte sich. Wir hatten nicht einmal GerĂ€te, um es zu reproduzieren. Durch das Strecken auf den Bildschirm konnten wir immer noch ein Low-End-GerĂ€t finden, auf dem es nicht funktionierte, und alles mit Android ganz einfach reparieren: resizeableActivity = "true" im Manifest. Mit der Tapete wurde alles viel komplizierter. UngefĂ€hr zwei Tage lang haben wir versucht, detailliertere Informationen zu erhalten. Am Ende fanden sie heraus, dass auf einer Reihe von GerĂ€ten entweder der Codec zum Decodieren von progressivem JPEG mit Fehlern implementiert wurde, wenn mehrere Komprimierungsalgorithmen fĂŒr die Ergebnisse des jeweils anderen verwendet wurden. In diesem Fall haben wir gerade einen Flusen-Check geschrieben, der beim Erstellen der Anwendung fehlschlĂ€gt, wenn wir das in der apk selbst progressiv codierte Hintergrundbild in die apk selbst einfĂŒgen. Alle Hintergrundbilder neu codiert, die Situation im Backend wiederholt, das den Rest der Hintergrundbilder verteilt, und alles funktioniert hervorragend. Aber es hat uns ungefĂ€hr zwei Tage gekostet.



Im Code sieht es ungefĂ€hr so ​​aus. Unangenehm, aber leider so. Normalerweise erscheinen diese Zeilen nach einem langen Debugging.



Welche Garantien gibt uns Google, um sicherzustellen, dass die API nicht so stark beschĂ€digt wird, dass Anwendungen im Prinzip nicht funktionieren? ZunĂ€chst gibt es eine CDD, die beschreibt, was möglich ist und was nicht geĂ€ndert werden kann, was abwĂ€rtskompatibel ist und was nicht. Dies ist ein Dokument von mehreren Dutzend Seiten mit allgemeinen Empfehlungen, die natĂŒrlich nicht alle FĂ€lle abdecken. Um weitere FĂ€lle abzudecken, gibt es CTS, das ausgefĂŒllt werden muss, damit das Telefon eine Zertifizierung von Google erhĂ€lt und Google-Dienste von Google verwendet werden können. Dies ist ein Satz von ungefĂ€hr 350.000 automatisierten Tests. Es gibt auch einen CTS-Verifizierer, eine regulĂ€re APK, die Sie auf Ihr Telefon setzen können, um eine Reihe von ÜberprĂŒfungen durchzufĂŒhren. Übrigens, wenn Sie ein Telefon mit Ihren HĂ€nden kaufen, können Sie es so ĂŒberprĂŒfen.

Mit dem Aufkommen von Treble erschien das VTS-Projekt, es ist wahrscheinlicher fĂŒr Entwickler niedrigerer Ebenen. Es ĂŒberprĂŒft die Treiber-APIs, die ab Project Treble versioniert sind und Ă€hnliche Tests durchlaufen. DarĂŒber hinaus sind die Telefonentwickler selbst gesunde Menschen, die möchten, dass Android-Anwendungen fĂŒr sie gut funktionieren, aber das ist eine mittelmĂ€ĂŸige Hoffnung. Die negative Seite ist, dass wir auf unvorhergesehene Fehler stoßen, die erst vorhergesagt werden können, wenn die Anwendung auf dem GerĂ€t gestartet wurde. Auch hier sind wir gezwungen zu kaufen, zusĂ€tzlich zu der Tatsache, dass verschiedene Versionen der API, gibt es auch zusĂ€tzliche GerĂ€te von verschiedenen Herstellern, um nach ihnen zu suchen.

Aber es gibt positive Aspekte. Zumindest fallen die von Herstellern am hÀufigsten implementierten Funktionen in Android selbst. Jemand kann sich daran erinnern, dass die Standard-Fingerabdruck-API spÀter angezeigt wurde als GerÀte, die den Bildschirm mit einem Fingerabdruck entsperren konnten. Laut XDA-Entwicklern möchte die Android-API nun auch mit der Kamera im Gesicht entsperren, dies ist jedoch noch nicht korrekt. Wir werden dies wahrscheinlich mit Ihnen herausfinden.

DarĂŒber hinaus können GerĂ€teentwickler selbst, wenn sie nicht standardmĂ€ĂŸige APIs erstellen, und viele Bibliotheken veröffentlichen, um mit ihrer API fĂŒr normale Entwickler zu arbeiten. Und wenn Sie dies noch nie zuvor getan haben, empfehle ich Ihnen, die Statistiken zur Verwendung Ihrer Anwendung durchzugehen, die beliebtesten Hersteller zu ermitteln und sich die Entwicklerportale ihrer Websites anzusehen. Ich denke, Sie werden angenehm ĂŒberrascht sein, dass viele APIs mit interessanten Hardwarefunktionen, Sicherheitsfunktionen, Cloud-Diensten oder etwas anderem Interessantem haben. Und auf den ersten Blick scheint es wild, separate Funktionen fĂŒr einzelne GerĂ€te zu schreiben, aber neben GerĂ€ten gibt es auch Hersteller von noch kleineren Prozessoren, die auch ihre APIs implementieren. Zum Beispiel hat Qualcomm eine wunderbare Hardwarebeschleunigung zum Erkennen von Bildern von der Kamera, die Sie durchaus verwenden können, sie haben sogar eine gute Beschreibung von ihnen.

Somit kann jeder von Ihnen auch von dieser Art der Fragmentierung profitieren. Was machen wir damit? In keinem Fall können Sie Fehler melden und Fehlerberichte an GerÀteentwickler und sogar an Android-Entwickler senden. Denn wenn APIs, die es wert waren, den CTS-Test zu schreiben, beschÀdigt wurden, wird er geschrieben - und es gab solche PrÀzedenzfÀlle - und danach wurde die API zuverlÀssiger.

Lernen Sie Android, lernen Sie, was Hersteller anbieten, schwören Sie nicht mit ihnen - arbeiten Sie mit ihnen.

Wie sieht es von innen aus? Wie kann ich eine Funktion implementieren, die fĂŒr unser Telefon einzigartig ist, und diese API aus einer regulĂ€ren Android-Anwendung verwenden?



Ein bisschen Theorie. Wie beschreiben Android-Entwickler selbst das interne AOSP-GerĂ€t? Die oberste Ebene ist eine Anwendung, die entweder von Ihnen oder von den Entwicklern des Telefons selbst geschrieben wurde. Sie hat keine hohen Rechte und verwendet lediglich Standard-APIs. Dies ist ein Framework. Hierbei handelt es sich um Klassen, die nicht Teil Ihrer Anwendung sind, z. B. AktivitĂ€t, Paket, Bundle. Sie sind Teil des Systems und werden als Framework bezeichnet. Die Klassen, die Ihnen auf dem GerĂ€t zur VerfĂŒgung stehen. Als nĂ€chstes folgen die Systemdienste. Dies verbindet Sie mit dem System: ActivityManagerService, WindowManagerService, PackageManagerService, die die interne Seite der Interaktion mit dem System implementieren.

Als nĂ€chstes folgt die Hardware-Abstraktionsschicht. Dies ist die oberste Treiberschicht, die die gesamte Logik fĂŒr die Anzeige auf dem Bildschirm, fĂŒr die Interaktion mit Bluetooth und dergleichen enthĂ€lt. Der Kernel ist die unterste Ebene der Treiber, die Systemverwaltung. Diejenigen, die wissen, was der Kern ist und vor was sie stehen, mĂŒssen es nicht sagen, aber Sie können lange sprechen.



Wie sieht es auf dem GerĂ€t aus? Unsere Anwendung interagiert nicht nur mit dem Standard-Framework, sondern kann auch mit dem benutzerdefinierten Framework des Herstellers interagieren. Über dieses Framework kann es auch mit benutzerdefinierten Diensten kommunizieren. Wenn es sich um festverdrahtete oder Low-Level-Funktionen handelt, wird HAL fĂŒr sie und gegebenenfalls sogar fĂŒr Treiber auf Kernel-Ebene geschrieben.



Wie schreiben wir unser Feature? Der Plan ist einfach: Sie mĂŒssen ein Framework schreiben, das sich nicht sehr von den Bibliotheken unterscheidet, die die meisten Android-Entwickler geschrieben haben. Ich denke, Sie alle wissen das. Sie mĂŒssen einen Systemdienst schreiben, bei dem es sich um eine normale Anwendung handelt, und zwar nur mit nicht ganz normalen Rechten im System. Und wenn nötig, können Sie HAL schreiben, aber wir lassen dies weg. Sie können Ihre eigenen Treiber auf Kernel-Ebene schreiben, aber wir werden dies jetzt auch nicht berĂŒcksichtigen. Und schreiben Sie eine Client-Anwendung, die all dies nutzt.



Damit wir mit dem System interagieren können, mĂŒssen wir einen Vertrag abschließen, und dafĂŒr gibt es bereits einen guten Mechanismus fĂŒr AIDL-Schnittstellen. Es ist nur eine Art Schnittstelle, auf deren Grundlage das System eine zusĂ€tzliche Klasse generiert, die wir erweitern können, ĂŒber die die Interprozesskommunikation zwischen Ihrer Anwendung und den Systemdiensten ausgefĂŒhrt wird.



Als nÀchstes schreiben wir ein Framework, unsere Bibliothek, die die Implementierung dieser Schnittstelle enthÀlt, und geben alle Aufrufe an diese weiter. Wenn Sie interessiert sind, funktionieren ActivityManager, PackageManager und WindowManager Àhnlich. Es gibt etwas mehr Logik als wir hier implementiert haben, aber das Wesentliche ist genau das.



Wir haben das Framework implementiert. Wir mĂŒssen einen Systemdienst schreiben, der unsere Daten von der Systemseite empfĂ€ngt. In diesem Fall senden und lesen wir Ganzzahlen. Wir erstellen eine Klasse, die auch die Schnittstelle erweitert, die auf der vorherigen Folie aus AIDL generiert wurde. Wir erstellen ein Feld, in das wir Werte schreiben, lesen, einen Setter schreiben, einen Getter. Das einzige ist, dass es nicht genug Schlösser gibt, aber sie passten nicht sehr gut auf den Schlitten, sie sollten gemacht werden.



Damit dieser Systemdienst verfĂŒgbar ist, mĂŒssen wir ihn im Systemdienstmanager registrieren. In diesem Fall handelt es sich um dieselbe Klasse, die fĂŒr normale Anwendungen nicht verfĂŒgbar ist. Es ist genau fĂŒr diejenigen verfĂŒgbar, die sich in der Plattform in den Systempartitionen befinden. Wir registrieren den Dienst einfach in Application.onCreate () und stellen ihn unter dem Namen der von uns erstellten Klasse zur VerfĂŒgung.

Was benötigen wir, damit onCreate () grundsĂ€tzlich gestartet und unser Dienst in den Speicher geladen werden kann? Wir schreiben in das Manifest in Anwendung Android: persistent = "true". Dies bedeutet, dass dies ein dauerhafter Prozess ist, der stĂ€ndig im Speicher sein muss, da er Systemfunktionen ausfĂŒhrt.



Auch im Manifest selbst können wir android: sharedUserId angeben, in diesem Fall System, aber es kann eine Vielzahl unterschiedlicher IDs sein. Sie ermöglichen es der Anwendung, umfassendere Rechte im System zu erhalten und mit verschiedenen APIs und Diensten zu interagieren, die fĂŒr normale Anwendungen nicht verfĂŒgbar sind.

In diesem Fall haben wir zum Beispiel so etwas nicht verwendet.



Wir haben ein Framework geschrieben, einen Systemdienst. Wir werden die Mechanismen im Inneren weglassen, dies ist ein etwas kompliziertes Thema, es verdient einen separaten Bericht.

Wie kann das Framework fĂŒr Anwendungsentwickler bereitgestellt werden? Zwei Formate. Wir können vollwertige Klassen ausgeben und eine vollwertige Bibliothek erstellen, die Sie in Ihre Anwendung kompilieren, und die gesamte Logik wird Teil Ihrer Dexes.

Sie können das Framework auch in Form von Stub-Klassen verteilen, auf die Sie nur zur Kompilierungszeit verlinken können, und erwarten, dass diese Klassen Ă€hnlich wie in frĂŒheren Beispielen aus verschiedenen Android-Versionen auf dem GerĂ€t selbst auf Sie warten. Sie können es entweder ĂŒber ein regulĂ€res Maven-Repository, das jeder kennt, oder ĂŒber Android Studio sdkmanager verteilen, Ă€hnlich wie Sie neue Versionen des SDK installieren. Es ist schwer zu sagen, welche Methode bequemer ist. FĂŒr mich persönlich ist es bequemer, Maven zu verbinden.



Wir schreiben eine einfache Bewerbung. Auf vertraute Weise verbinden wir die compileOnly-AbhĂ€ngigkeit, nur jetzt ist es unsere Bibliothek. Wir schreiben eine Verwendungsbibliothek vor, die wir geschrieben und auf dem GerĂ€t installiert haben. Wir schreiben AktivitĂ€t, erhalten Zugriff auf diese Klassen und interagieren mit dem System. Es wĂ€re also möglich, absolut alle Funktionen zu implementieren: DatenĂŒbertragung auf einige zusĂ€tzliche GerĂ€te, zusĂ€tzliche Hardwarefunktionen usw.

Somit stellen alle Entwickler den Entwicklern einzigartige Funktionen des GerĂ€ts zur VerfĂŒgung. Manchmal sind dies private APIs, die nur Partnern zur VerfĂŒgung stehen. Manchmal sind sie öffentlich und solche, die Sie auf Entwicklerportalen finden können. Es gibt andere Möglichkeiten, solche Dinge zu implementieren, aber ich habe eine Methode beschrieben, die in Google und unter Android-Entwicklern als die wichtigste angesehen wird.

Sie sollten GerÀteentwickler nicht als Personen behandeln, die Ihre Anwendungen beschÀdigen. Dies sind die gleichen Entwickler, sie schreiben den gleichen Code auf ungefÀhr der gleichen Ebene. Schreibe Fehlerberichte, sie helfen wirklich, ich analysiere sie oft. Schreiben Sie mehr Anwendungen und nutzen Sie die Möglichkeiten, die sowohl Android als auch das GerÀt selbst bieten. Ich habe alles

Source: https://habr.com/ru/post/de448126/


All Articles