Kürzlich habe ich mich entschlossen, die Idee zu implementieren, wie ein Standort über die VK-API mit Freunden im Echtzeitmodus geteilt werden kann. Die Ausgabe war eine plattformübergreifende Qt-Anwendung für iOS / Android, eine Webanwendung für VKontakte und einige Pull-Anfragen für die VK-API. In diesem Artikel möchte ich einige nicht offensichtliche Implementierungspunkte mitteilen, die für jemanden von Interesse sein können. Also, interessiert bitte nach Katze fragen.
Warum brauchte ich es überhaupt?
Früher oder später werden Kinder erwachsen. Banale Wahrheit. Also sagte meine zehnjährige Tochter eines Tages: "Papa, nimm mich nicht mehr mit dem Auto, ich möchte alleine zur Schule gehen!" Nun, dann fand ich die Forderung fair, bat um eine zweiwöchige Nachfrist und begann mit den Vorbereitungen.
Da ich Erfahrung im Schreiben von Anwendungen habe und meine Tochter als Vorbereitung ständig in der Tasche des iPhone SE herumträgt, wurde beschlossen, schnell eine Anwendung zu schreiben, die zeigt, wo sich die Tochter in diesem bestimmten Moment befindet. Ja, ich weiß, dass es jetzt viele solcher Anwendungen gibt (sogar ähnliche Funktionen wurden kürzlich in Google Maps veröffentlicht), und es war möglich, eine vorgefertigte Lösung zu verwenden, aber ich war daran interessiert, etwas Eigenes zu schreiben.
Warum VKontakte?
Da ich nicht mit der Verarbeitung und Speicherung der (möglicherweise möglichen) personenbezogenen Daten anderer Personen auf meinen Geräten in Verbindung treten möchte, habe ich mir überlegt, wie ich auf meinen eigenen Serverteil verzichten soll. Und dann dämmerte es mir - schließlich gibt es so ein Monster wie VKontakte! Es ist modisch, leistungsstark und mit seiner entwickelten API, und vor allem haben alle unsere Kinder lange darin gesessen und waren eng (ich kann nicht sagen, dass es mir gefallen hat, aber es ist eine Realität). Aber zum Teufel, Holmes, wie kann ich Standortdaten einfügen, damit sie erstens nicht dort angezeigt werden, wo sie nicht erforderlich sind, und zweitens, damit Sie den Zugriff auf diese Daten steuern können, damit die schlechten Pedobirs zu ihnen gelangen nicht dort angekommen?
Notizen kamen zur Rettung. Ja, die gleichen Wikipedia-Notizen, die einst (Gerüchten zufolge) sehr beliebt waren und jetzt im Stift sind, haben größtenteils aufgehört, in den Bändern zu blinken, und sind in einen separaten Bereich der Website verschoben, den Sie bei einer lahmen Ziege nicht erreichen können. Sie können beliebige Textdaten in sie einfügen, aber die Hauptsache ist, dass ihnen Zugriffslisten zugewiesen werden können, in denen Personen und Gruppen aufgelistet sind, die diesen Hinweis sehen und kommentieren können.
Das allgemeine Schema der Anwendung in meinen Augen sah folgendermaßen aus:
- Erstellen Sie eine Liste von Freunden, mit denen Sie Standortdaten teilen möchten (ich habe sie "Vertrauenswürdige Freunde" genannt).
- Wir erstellen eine Notiz mit einem bestimmten Namen, erteilen das Recht, sie in der obigen Liste anzuzeigen, notieren die Standortdaten dort und aktualisieren sie regelmäßig.
- Wir durchsuchen regelmäßig die Liste der vertrauenswürdigen Freunde und prüfen, ob sie auch eine Notiz mit diesem bestimmten Namen haben. Wenn dies der Fall ist, versuchen wir, Daten über den Standort des Freundes aus dessen Inhalten zu extrahieren. Wenn dies erfolgreich ist, zeigen wir sie auf der Karte an.
- ???
- GEWINN!
Es gibt also eine Idee, es liegt an der kleinen Sache, sie zu realisieren.
iOS
Da ihre Tochter das iPhone verwendet, war es logisch, mit der Implementierung mit der iOS-Version zu beginnen. Im Hinblick auf die plattformübergreifende Nutzung wurde Qt als Framework ausgewählt, da ich es schon lange kenne, und die gute alte Open Street Map, für die es ein Plugin in Qt Location gibt, wurde als Engine für die Karte ausgewählt. Da die Anwendung ursprünglich als Open Source erstellt wurde, haben mich die Lizenzbeschränkungen von Qt nicht erschreckt.
Die GUI wurde in QML geschrieben, um mit VK zu arbeiten. Ich habe das reguläre
VK iOS SDK verbunden und verwendet. Es ist in Objective-C geschrieben, sodass die Integration keine Probleme verursachte. Die Arbeit im Hintergrund unter iOS wird über den Significant Change Location Service implementiert. Um den Energieverbrauch zu senken, überwacht die Anwendung die Aktivität von Bewegungen. Wenn sie versteht, dass eine Person lange Zeit an ungefähr einem Ort sitzt (z. B. zur Schule oder ins Büro gekommen ist), verringert sie die erforderliche Genauigkeit bei der Bestimmung der Geolokalisierung und zwingt das Betriebssystem, auf weniger energieintensive Methoden zur Bestimmung zu wechseln (wie) in der Regel auf Zelltürmen). Wenn die Anwendung versteht, dass sich die Person aktiv bewegt, steigt die Genauigkeit wieder an.
Der vollständige Quellcode für die iOS-Version ist auf
GitHub verfügbar. Hier sind einige nicht offensichtliche Punkte, auf die ich im Implementierungsprozess gestoßen bin:
Überschreiben von NSApplicationDelegate-Methoden in einer Qt-AnwendungFür das VKontakte iOS SDK müssen einige seiner Funktionen in der Anwendung aufgerufen werden: didFinishLaunchingWithOptions: und application: openURL: options: Methoden. Vor einer Version von Qt (meiner Meinung nach vor 5.11) war es ausreichend, eine Kategorie für QIOSApplicationDelegate wie folgt zu erstellen:
@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate> @end @interface QIOSApplicationDelegate (QIOSApplicationDelegateVKGeoCategory) @end @implementation QIOSApplicationDelegate (QIOSApplicationDelegateVKGeoCategory) - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [...] } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { [...] } @end
In neueren Versionen von Qt verfügt QIOSApplicationDelegate jedoch bereits über eine Implementierung der Anwendung: openURL: options :, sodass die Option mit Kategorien nicht mehr ausgeführt wird. Ich musste einen Erben von QIOSApplicationDelegate erstellen und einen Delegaten über setDelegate zuweisen:
@interface VKGeoApplicationDelegate : QIOSApplicationDelegate @end @implementation VKGeoApplicationDelegate - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { [...] } @end void InitializeVKGeoApplicationDelegate() { [[UIApplication sharedApplication] setDelegate:[[VKGeoApplicationDelegate alloc] init]]; } int main(int argc, char *argv[]) { [...] InitializeVKGeoApplicationDelegate(); [...] }
Fehlerbehandlung von VKRequestAngesichts der Tatsache, dass VKRequest bei einem Fehler einen leeren (leeren) NSError zurückgegeben hat. Ich habe einen Patch erstellt, der den vorherigen Patch von jemandem patcht und eine
Anfrage für diesen Patch abruft, aber er hängt immer noch in nicht überprüften Patches.
Android
Das Folgende war die Version für Android. Der GUI-Code wurde von iOS fast vollständig wiederverwendet, um mit dem VK zu interagieren. Das reguläre
VK-Android-SDK wurde erneut verwendet. Die Interaktion erfolgt über das JNI. Die Arbeit im Hintergrund wird gemäß den Google-Covenants für solche Anwendungen implementiert - nämlich über den Foreground Service. Natürlich ist auch die Logik zur Reduzierung des Energieverbrauchs implementiert, die der in iOS verwendeten ähnelt.
Der vollständige Satz des Quellcodes für die Android-Version ist wieder auf
GitHub verfügbar. Hier sind einige nicht offensichtliche Punkte, auf die ich bei der Implementierung dieser Version gestoßen bin:
So erstellen Sie einen Android-Dienst auf QtUm einen Dienst in Qt 5.10 zu erstellen, wurde die QAndroidService-Klasse angezeigt, die anstelle von QGuiApplication verwendet werden sollte. Sie können separate .so für Aktivität und Service kompilieren oder eine .so für alles verwenden. Damit der Code versteht, in welchem Modus er funktioniert, können Sie diesen Modus über die Befehlszeilentaste angeben.
<service android:name=".VKGeoService"> <meta-data android:name="android.app.arguments" android:value="-service"/> </service>
int main(int argc, char *argv[]) { if (argc == 1) { QGuiApplication app(argc, argv); [...] } else if (argc == 2 && QString(argv[1]) == "-service") { QAndroidService app(argc, argv); [...] } else { return 0; } }
Seltsame "baumelnde" Aktivität in onDestroy ()Bei der Implementierung des Dienstes wurde ein lustiges Problem deutlich: QtActivity „hing“ irgendwo im Darm seines onDestroy (), abhängig von der Verfügbarkeit eines funktionierenden Vordergrunddienstes. Anscheinend erwartet Qt nicht, dass nach Abschluss der Aktivität noch etwas von der Anwendung übrig bleibt. Das Problem wurde gelöst, indem Aktivität und Service über verschiedene Prozesse verteilt wurden, indem android: process im Manifest verwendet wurde:
<service android:name=".VKGeoService" android:process=":VKGeoService"> [...] </service>
und Festnageln des Prozesses, in dem die Aktivität ausgeführt wird, in einem überschriebenen onDestroy ():
@Override public void onDestroy() { [...] Process.killProcess(Process.myPid()); }
Ja, Lint schwört ständig darauf, aber wie ich anders damit umgehen soll, ist mir noch nicht klar, aber QTBUG mit PoC zu diesem Thema in die Hände zu bekommen, bringt noch nichts.
UPDATE : In Qt 5.12.3 (möglicherweise etwas früher - ich habe es nicht überprüft) wurde der Fehler beim Aufhängen von QtActivity.onDestroy () behoben, als der Dienst gestartet wurde. Diese Problemumgehung wird nicht mehr benötigt.
Fehlender errorBlock-Aufruf beim Abbrechen von VKBatchRequestIch verwende häufig Batch-Anforderungen, um das Laden von VKontakte-Servern zu erleichtern. Unter Android (unter iOS funktioniert alles einwandfrei) ist ein Problem aufgetreten, wenn VKBatchRequest abgebrochen wurde und errorBlocks nicht für stornierte Anforderungen aufgerufen wurden. Ich habe dieses Problem in der lokalen Version der Bibliothek behoben, den entsprechenden Patch und die entsprechende
Pull-Anforderung für diesen Patch gestellt, aber es hängt immer noch in den nicht überprüften.
Fazit
Die iOS-Version von Apple konnte problemlos in den App Store gestellt werden und ist dort immer noch verfügbar. Die Android-Version lebte einige Zeit bei Google Play, bis die Google Play-Richtlinie verschärft wurde (es wurde vorgeschrieben, dass Anwendungen, die die Geolokalisierung verfolgen, explizit sein sollten für Familien- oder Firmenzwecke bestimmt), wonach meine Bewerbung dort sicher blockiert wurde. Zu meinem Berufungsversuch erklärte ein verantwortungsbewusster Google-Mitarbeiter (oder vielleicht war es ein Bot, jetzt ist es nicht so offensichtlich, wer Ihre Fragen genau beantwortet) unerbittlich: "Nun, diese Anwendung darf NICHT NUR für die Verfolgung von Familien oder Unternehmen verwendet werden." , zu dem ich nichts zu streiten finden konnte - tatsächlich kann man mit einem Hammer nicht nur Nägel hämmern, sondern auch die Köpfe schlagen ... Ich habe die Android-Version in Yandex.Store und Amazon Appstore sowie als APK auf der Projektwebsite platziert.
Ich würde mich freuen, wenn diese Anwendung für jemanden als visuelle Hilfe nützlich wäre, um einige nicht offensichtliche Momente beim Schreiben einer Qt-Anwendung für iOS / Android zu verdeutlichen, insbesondere solche, die sich auf die Implementierung des Android-Dienstes auf Qt beziehen (diese Funktionalität ist relativ neu, Beispiele für die Implementierung, wie viel Ich weiß nicht so viel). Gerne beantworte ich auch Fragen in den Kommentaren, falls vorhanden.