我如何通过VK API共享位置

我最近决定尝试实现如何通过VK API与朋友以近实时模式共享位置的想法。 输出是用于iOS / Android的跨平台Qt应用程序,用于VKontakte的Web应用程序以及对VK API的几个请求请求。 在本文中,我想分享一些可能让人感兴趣的非显而易见的实现点。 所以,有兴趣的请猫。

为什么我完全需要它


孩子迟早会长大。 朴实的真理。 因此,我十岁的女儿有一天说:“爸爸,不要再开车带我了,我想独自上学!” 好吧,然后,我发现索赔要求合理,要求为期两周的宽限期,并开始进行准备。

由于我有编写应用程序的经验,并且我的女儿经常带着iPhone SE的口袋做准备,因此决定快速编写一个应用程序,以显示女儿在这个特定时刻的位置。 是的,我知道现在有很多这样的应用程序(即使最近在Google Maps中也出现了类似的功能),并且可以使用一些现成的解决方案,但是我对编写自己的东西很感兴趣。

为什么选择VKontakte?


由于我不希望将我的设备上其他人(在可能的情况下)的个人数据的处理和存储与由此产生的所有“魅力”相关联,因此我考虑了如何在没有自己的服务器部件的情况下进行操作。 然后它突然降临在我身上-毕竟,有一个像VKontakte这样的怪物! 它时尚,功能强大且具有已开发的API,最重要的是,我们所有的孩子都已经坐在其中很长时间了,而且非常紧张(我不能说我喜欢它,但这是现实)。 但是,福尔摩斯,我该如何将位置数据填充到其中,这样,首先,它不会出现在不必要的地方;其次,您可以控制对这些数据的访问,从而使不良的人对他们有帮助没到那里?

救援人员来了。 是的,这些维基百科指出,曾经(根据传闻)非常流行,现在已经流行,大多数时候他们停止在录音带中闪烁,并移至网站的另一部分,您不会遇到a脚的山羊。 它们可以包含任意文本数据,但是最主要的是,可以为它们分配访问列表,该访问列表列出了可以查看和评论此注释的人员和组。

在我眼中,该应用程序的总体方案开始看起来像这样:

  • 创建要与之共享位置数据的朋友列表(我称其为“受信任的朋友”);
  • 我们会创建一个具有特定名称的注释,并赋予查看该注释的权限,并在上面的列表中写下位置数据并定期进行更新;
  • 我们会定期浏览受信任的朋友列表,检查他们是否也有一个带有此特定名称的便笺(如果有),然后尝试从其内容中提取有关朋友位置的数据,如果成功,则将其显示在地图上;
  • ???
  • 赢利!

因此,有一个想法,要实现它取决于小事情。

的iOS


由于她的女儿使用iPhone,因此从iOS版本开始实施是合乎逻辑的。 出于跨平台的考虑,选择Qt作为框架是因为我已经很长时间了,并且选择了Qt Location中有一个插件的旧版Open Street Map作为地图的引擎。 由于该应用程序最初是作为开源创建的,因此Qt的许可限制并没有吓到我。

GUI用QML编写,可以与VK一起使用,我连接并使用了常规的VK iOS SDK ,它是用Objective-C编写的,因此它的集成没有引起任何问题。 iOS的后台工作是通过重要更改位置服务实现的。 为了减少能源消耗,该应用程序监视运动的活动,如果它了解一个人长时间坐了大约一个地方(例如,他去学校或办公室),那么他会降低确定地理位置所需的精度,从而迫使操作系统切换到能耗较低的方式来确定地理位置(通常,在手机信号塔上)。 如果应用程序了解到该人已开始主动移动,则准确性会再次提高。

可以在GitHub上获得iOS版本的完整源代码。 这是我在实现过程中遇到的一些非显而易见的观点:

在Qt应用程序中覆盖NSApplicationDelegate方法

VKontakte iOS SDK需要在其应用程序中的某些功能中添加调用:didFinishLaunchingWithOptions:和应用程序:openURL:options:方法。 在某些版本的Qt之前(我认为在5.11之前),足以为QIOSApplicationDelegate创建一个类别,如下所示:

@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 

但是在最新版本的Qt中,QIOSApplicationDelegate已经具有应用程序的实现:openURL:options:,因此带有类别的选项不再适用。 我必须从QIOSApplicationDelegate继承,并通过setDelegate分配一个委托:

 @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(); [...] } 

来自VKRequest的错误处理

面对发生错误的VKRequest返回一个空(空)NSError。 我制作了一个修补程序,用于修补某人以前的修补程序,并请求该修补程序,但它仍挂在未审查的修补程序中。

安卓系统


以下是Android的版本。 GUI代码几乎完全从iOS重复使用,以与VK交互,再次使用常规的VK Android SDK ,通过JNI进行交互,并根据Google针对此类应用程序的约定实施后台工作-即通过Foreground Service。 当然,类似于iOS中使用的减少能耗的逻辑也已实现。

可以在GitHub上再次获得Android版本的完整源代码,这里是在实现此版本的过程中遇到的一些非显而易见的地方:

如何在Qt上创建Android服务

为了在Qt 5.10中创建服务,出现了QAndroidService类,应该使用它代替QGuiApplication。 您可以为活动和服务编译单独的.so,也可以为所有内容使用一个.so,为了使代码了解其工作方式,可以通过命令行键指定此模式,如下所示:

 <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; } } 

onDestroy()中奇怪的“悬空”活动

在实施该服务的过程中,一个有趣的问题变得很明显-QtActivity挂在其onDestroy()肠内的某个位置,取决于正在运行的前台服务的可用性。 显然,Qt并不期望活动完成后,应用程序中可能还会保留其他内容。 通过在清单中使用android:进程,通过在不同进程之间分配活动和服务来解决该问题:

 <service android:name=".VKGeoService" android:process=":VKGeoService"> [...] </service> 

并在重写的onDestroy()中固定活动运行的过程:

 @Override public void onDestroy() { [...] /* * This call hangs when foreground service is running, * so we just kill activity process instead (service * is running in a different process). * * super.onDestroy(); */ Process.killProcess(Process.myPid()); } 

是的,皮棉不断发誓,但是对我来说,如何以不同的方式处理它还不是很明显,但是在这个主题上与PoC接触QTBUG尚未实现。

更新 :在Qt 5.12.3中(可能更早一些-我没有检查过),当服务启动时,带有QtActivity.onDestroy()挂起的错误已得到修复,不再需要这种解决方法。

取消VKBatchRequest时缺少errorBlock调用

我广泛使用批处理请求来减轻VKontakte服务器上的负载,并且在Android(在iOS下一切正常)下,如果VKBatchRequest被取消并且errorBlocks未被取消,我会遇到问题。 我在库的本地版本中解决了此问题,进行了适当的修补,并请求该修补程序发出请求 ,但仍然将其挂在未审查的程序中。

结论


Apple的iOS版本很容易放在App Store中,并且仍然可以在App Store上找到; Android版本在Google Play上生存了一段时间,直到Google Play政策收紧(它规定应明确跟踪地理位置的应用程序(供家庭或公司使用),之后我的申请被安全地阻止了。 为了向我提出上诉,一位负责任的Google员工(或者也许是机器人,现在还不很清楚,谁能准确回答您的问题)坚决表示:“嗯,此应用程序不能仅用于家庭或公司跟踪” ,我对此无可辩驳-的确,用锤子不仅可以锤打钉子,还可以使自己的头punch……我将Android版本放置在Yandex.Store和Amazon Appstore中,并作为APK放置在项目网站上。

如果此应用程序对某人有用,可以作为一种视觉辅助工具来澄清在编写适用于iOS / Android的Qt应用程序时出现的一些不明显的时刻,特别是那些与在Qt上实施Android服务有关的时刻,我会很高兴(此功能是相对较新的,实现示例,多少我不太了解)。 我也很乐意在评论中回答问题(如果有)。

Source: https://habr.com/ru/post/zh-CN434996/


All Articles