草庐IT

ios - QT iOS 谷歌云消息崩溃

coder 2024-01-13 原文

我正在尝试在我用 Qt 编写的跨平台项目中实现远程推送通知。

我发现 google 为 Android 和 iOS 提供了 Google Cloud Messaging。 ( https://developers.google.com/cloud-messaging/ ) 我需要使用一些谷歌代码并为每个平台编写一些 native 代码(Android 的 Java 和 iOS 的 Objective C)

我阅读了 Android 部分并在我的项目中实现了它,它运行良好。

现在我尝试实现 iOS 部分 ( https://developers.google.com/cloud-messaging/ios/start )

有一些问题。

首先,我不能在 Qt 项目中使用 CocoaPods,所以我必须手动链接必要的库。

这是我的 Qt 项目文件:

ios {

ios_google_plist.files = $$PWD/ios/GoogleService-Info.plist

QMAKE_BUNDLE_DATA += ios_google_plist
QMAKE_INFO_PLIST = $$PWD/ios/Info.plist

LIBS += \
    ./ios/libs/libGGLInstanceIDLib.a \
    ./ios/libs/libGGLCloudMessaging.a \
    ./ios/libs/libGGLCore.a \
    ./ios/libs/libGcmLib.a \
    ./ios/libs/libProtocolBuffers.a \
    ./ios/libs/libGTMSessionFetcher_core.a \
    ./ios/libs/libGTMSessionFetcher_full.a \
    ./ios/libs/libGSDK_Overload.a \
    ./ios/libs/libGTM_AddressBook.a \
    ./ios/libs/libGTM_core.a \
    ./ios/libs/libGTM_DebugUtils.a \
    ./ios/libs/libGTM_GTMURLBuilder.a \
    ./ios/libs/libGTM_iPhone.a \
    ./ios/libs/libGTM_KVO.a \
    ./ios/libs/libGTM_NSDictionary+URLArguments.a \
    ./ios/libs/libGTM_NSScannerJSON.a \
    ./ios/libs/libGTM_NSStringHTML.a \
    ./ios/libs/libGTM_NSStringXML.a \
    ./ios/libs/libGTM_Regex.a \
    ./ios/libs/libGTM_RoundedRectPath.a \
    ./ios/libs/libGTM_StringEncoding.a \
    ./ios/libs/libGTM_SystemVersion.a \
    ./ios/libs/libGTM_UIFont+LineHeight.a \
    ./ios/libs/libGTMStackTrace.a

  }

iOS 应用程序构建成功,但在接收 token 时崩溃了。

这是日志:

2015-08-21 16:59:50.735 MyCustomApp[475:96862] Attempted to configure [Identity, Analytics, AdMob, SignIn, AppInvite, CloudMessaging].
2015-08-21 16:59:50.735 MyCustomApp[475:96862] Successfully configured [].
2015-08-21 16:59:50.736 MyCustomApp[475:96862] Failed to configure [].
2015-08-21 16:59:50.736 MyCustomApp[475:96862] Subspecs not present, so not configured [Identity, Analytics, AdMob, SignIn, AppInvite, CloudMessaging].
2015-08-21 16:59:50.762 MyCustomApp[475:96862] didRegisterForRemoteNotificationsWithDeviceToken begin
2015-08-21 16:59:50.767 MyCustomApp[475:96862] didRegisterForRemoteNotificationsWithDeviceToken end
2015-08-21 16:59:50.787 MyCustomApp[475:96862] -[GGLInstanceIDTokenManager fetchTokenWithAuthorizedEntity:scope:keyPair:options:handler:]: unrecognized selector sent to instance 0x170623c20
2015-08-21 16:59:50.788 MyCustomApp[475:96862] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[GGLInstanceIDTokenManager fetchTokenWithAuthorizedEntity:scope:keyPair:options:handler:]: unrecognized selector sent to instance 0x170623c20'
*** First throw call stack:
()
libc++abi.dylib: terminating with uncaught exception of type  NSException
program received signal 6,thread:17a5e;qaddr:199071490;00:0000000000000000;01:0000000000000000;02:0000000000000000;03:37364e7001000000;04:fddd569401000000;05:60d2ca6f01000000;06:6e00000000000000;07:800f000000000000;08:0000000800000000;09:0000000400000000;0a:0002000000000000;0b:0000000000000000;0c:0000000000000000;0d:0000000000000000;0e:0200000000000000;0f:0000000000000000;10:4801000000000000;11:0000000000000000;12:0000000000000000;13:0600000000000000;14:1013079901000000;15:e89c059901000000;16:b0c7217001000000;17:a09ae60101000000;18:0000000000000000;19:7a54608901000000;1a:0000000000000000;1b:0000000000000000;1c:c0b7049901000000;1d:c0d1ca6f01000000;1e:28d2589501000000;1f:a0d1ca6f01000000;20:70f24e9501000000;21:00000000;metype:5;mecount:2;medata:10003;medata:6;

如您所见,未调用 registrationHandler 回调并在 google lib 的某处发生崩溃(在调用 registrationHandler 回调之前)

我采用了谷歌示例代码并做了一些更改,例如我将 AppDelegate 接口(interface)重命名为 QIOSApplicationDelegate(否则不会调用 Objective-C 函数)

这是 Objective-C 代码:

AppDelegateGoogle.h

#include <QtCore>
void registerDeviceForNotification_iOS_CPP(void);

AppDelegateGoogle.mm

#import "Google/CloudMessaging.h"
#import <UIKit/UIKit.h>

#import "AppDelegateGoogle.h"


@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate, GGLInstanceIDDelegate>    
@property(nonatomic, strong) UIWindow *window;
@property(nonatomic, readonly, strong) NSString *registrationKey;
@property(nonatomic, readonly, strong) NSString *messageKey;
@property(nonatomic, readonly, strong) NSString *gcmSenderID;
@property(nonatomic, readonly, strong) NSDictionary *registrationOptions;

@property(nonatomic, strong) void (^registrationHandler)
    (NSString *registrationToken, NSError *error);
@property(nonatomic, assign) BOOL connectedToGCM;
@property(nonatomic, strong) NSString* registrationToken;
@property(nonatomic, assign) BOOL subscribedToTopic;   
@end

QIOSApplicationDelegate* pApp;

NSString *const SubscriptionTopic = @"/topics/global";

void registerDeviceForNotification_iOS_CPP(void)
{
    [pApp registerDeviceForNotification_iOS];
}

@implementation QIOSApplicationDelegate

// [START register_for_remote_notifications]
- (void)registerDeviceForNotification_iOS {
    // [START_EXCLUDE]
    _registrationKey = @"onRegistrationCompleted";
    _messageKey = @"onMessageReceived";
    // Configure the Google context: parses the GoogleService-Info.plist, and initializes
    // the services that have entries in the file
    NSError* configureError;
    [[GGLContext sharedInstance] configureWithError:&configureError];
    if (configureError != nil) {
      NSLog(@"Error configuring the Google context: %@", configureError);
    }
    _gcmSenderID = [[[GGLContext sharedInstance] configuration] gcmSenderID];
    // [END_EXCLUDE]
    // Register for remote notifications
    UIUserNotificationType allNotificationTypes =
        (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
    UIUserNotificationSettings *settings =
        [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    [[UIApplication sharedApplication] registerForRemoteNotifications];
    // [END register_for_remote_notifications]
    // [START start_gcm_service]
    [[GCMService sharedInstance] startWithConfig:[GCMConfig defaultConfig]];
    // [END start_gcm_service]
    __weak QIOSApplicationDelegate* weakSelf = self;
    // Handler for registration token request
    _registrationHandler = ^(NSString *registrationToken, NSError *error){
            NSLog(@"_registrationHandler called");
            if (registrationToken != nil) {
              NSLog(@"Registration Token: %@", registrationToken);
              std::string strToken([registrationToken UTF8String]);
              Device::sendRegistrationToServer(strToken);
            } else {
              NSLog(@"Registration to GCM failed with error: %@", error.localizedDescription);
            }
    };
}

// [START register_for_remote_notifications]
- (BOOL)application:(UIApplication *)application
      didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  pApp = self;
  return YES;
}

- (void)subscribeToTopic {
  // If the app has a registration token and is connected to GCM, proceed to subscribe to the
  // topic
  if (_registrationToken && _connectedToGCM) {
    [[GCMPubSub sharedInstance] subscribeWithToken:_registrationToken
                                             topic:SubscriptionTopic
                                           options:nil
                                           handler:^(NSError *error) {
                                             if (error) {
                                               // Treat the "already subscribed" error more gently
                                               if (error.code == 3001) {
                                                 NSLog(@"Already subscribed to %@",
                                                       SubscriptionTopic);
                                               } else {
                                                 NSLog(@"Subscription failed: %@",
                                                       error.localizedDescription);
                                               }
                                             } else {
                                               self.subscribedToTopic = true;
                                               NSLog(@"Subscribed to %@", SubscriptionTopic);
                                             }
                                           }];
  }
}

// [START connect_gcm_service]
- (void)applicationDidBecomeActive:(UIApplication *)application {
  // Connect to the GCM server to receive non-APNS notifications
  [[GCMService sharedInstance] connectWithHandler:^(NSError *error) {
    if (error) {
      NSLog(@"Could not connect to GCM: %@", error.localizedDescription);
    } else {
      _connectedToGCM = true;
      NSLog(@"Connected to GCM");
      // [START_EXCLUDE]
      [self subscribeToTopic];
      // [END_EXCLUDE]
    }
  }];
}
// [END connect_gcm_service]

// [START disconnect_gcm_service]
- (void)applicationDidEnterBackground:(UIApplication *)application {
  [[GCMService sharedInstance] disconnect];
  // [START_EXCLUDE]
  _connectedToGCM = NO;
  // [END_EXCLUDE]
}
// [END disconnect_gcm_service]

// [START receive_apns_token]
- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// [END receive_apns_token]
                NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken begin");
  // [START get_gcm_reg_token]
  // Start the GGLInstanceID shared instance with the default config and request a registration
  // token to enable reception of notifications
  [[GGLInstanceID sharedInstance] startWithConfig:[GGLInstanceIDConfig defaultConfig]];
  _registrationOptions = @{kGGLInstanceIDRegisterAPNSOption:deviceToken,
                           kGGLInstanceIDAPNSServerTypeSandboxOption:@YES};
  [[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:_gcmSenderID
                                                      scope:kGGLInstanceIDScopeGCM
                                                    options:_registrationOptions
                                                    handler:_registrationHandler];
  // [END get_gcm_reg_token]
                NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken end");
}

// [START receive_apns_token_error]
- (void)application:(UIApplication *)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  NSLog(@"Registration for remote notification failed with error: %@", error.localizedDescription);
// [END receive_apns_token_error]
  NSDictionary *userInfo = @{@"error" :error.localizedDescription};
  [[NSNotificationCenter defaultCenter] postNotificationName:_registrationKey
                                                      object:nil
                                                    userInfo:userInfo];
}

// [START ack_message_reception]
- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo {
  NSLog(@"Notification received: %@", userInfo);
  // This works only if the app started the GCM service
  [[GCMService sharedInstance] appDidReceiveMessage:userInfo];
  // Handle the received message
  // [START_EXCLUDE]
  [[NSNotificationCenter defaultCenter] postNotificationName:_messageKey
                                                      object:nil
                                                    userInfo:userInfo];
  // [END_EXCLUDE]
}

- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
  NSLog(@"Notification received: %@", userInfo);
  // This works only if the app started the GCM service
  [[GCMService sharedInstance] appDidReceiveMessage:userInfo];
  // Handle the received message
  // Invoke the completion handler passing the appropriate UIBackgroundFetchResult value
  // [START_EXCLUDE]
  [[NSNotificationCenter defaultCenter] postNotificationName:_messageKey
                                                      object:nil
                                                    userInfo:userInfo];
  handler(UIBackgroundFetchResultNoData);
  // [END_EXCLUDE]
}
// [END ack_message_reception]

// [START on_token_refresh]
- (void)onTokenRefresh {
  // A rotation of the registration tokens is happening, so the app needs to request a new token.
  NSLog(@"The GCM registration token needs to be changed.");
  [[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:_gcmSenderID
                                                      scope:kGGLInstanceIDScopeGCM
                                                    options:_registrationOptions
                                                    handler:_registrationHandler];
}
// [END on_token_refresh]

@end

有人可以帮忙吗?

谢谢,叶夫根

======EDIT1 开始======

当我尝试使用 -ObjC 链接器选项时,出现链接器错误:

duplicate symbol _OBJC_METACLASS_$_QIOSApplicationDelegate in: /Users/sha/build-MyCustomApp-iphoneos_clang_Qt_5_5_0_for_iOS-Release/MyCustomApp.build/Debug-iphoneos/MyCustomApp.build/Objects-normal/arm64/AppDelegateGoogle.o /Users/sha/Qt/5.5/ios/plugins/platforms/libqios_debug.a(qiosapplicationdelegate.o) duplicate symbol _OBJC_CLASS_$_QIOSApplicationDelegate in: /Users/sha/build-MyCustomApp-iphoneos_clang_Qt_5_5_0_for_iOS-Release/MyCustomApp.build/Debug-iphoneos/MyCustomApp.build/Objects-normal/arm64/AppDelegateGoogle.o /Users/sha/Qt/5.5/ios/plugins/platforms/libqios_debug.a(qiosapplicationdelegate.o) ld: 2 duplicate symbols for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

据我了解,有两个Application Delegate:

  • 一个是Qt框架提供和
  • 其次是 Google Lib 提供的。

所以链接器有 2 个重复的符号。

可能我需要将这两个 Application Delegate 合并为一个,但我不知道该怎么做...

======EDIT1 结束======

最佳答案

该方法在您的应用程序中似乎未链接的类别中定义。您应该将 -ObjC 链接器标志添加到 XCode 选项,这将强制它将静态库中的类别添加到您的应用程序。

这里有更多信息 https://developer.apple.com/library/mac/qa/qa1490/_index.html

正如上面的链接所说

This flag causes the linker to load every object file in the library that defines an Objective-C class or category. While this option will typically result in a larger executable (due to additional object code loaded into the application), it will allow the successful creation of effective Objective-C static libraries that contain categories on existing classes.

关于ios - QT iOS 谷歌云消息崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32143488/

有关ios - QT iOS 谷歌云消息崩溃的更多相关文章

  1. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  2. Ruby Readline 在向上箭头上使控制台崩溃 - 2

    当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby​​安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少

  3. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  4. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  5. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  6. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

  7. Ruby - 如何将消息长度表示为 2 个二进制字节 - 2

    我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi

  8. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  9. ruby-on-rails - 在 Flash 警报 Rails 3 中显示错误消息 - 2

    如果我在模型中设置验证消息validates:name,:presence=>{:message=>'Thenamecantbeblank.'}我如何让该消息显示在闪光警报中,这是我迄今为止尝试过的方法defcreate@message=Message.new(params[:message])if@message.valid?ContactMailer.send_mail(@message).deliverredirect_to(root_path,:notice=>"Thanksforyourmessage,Iwillbeintouchsoon")elseflash[:error]

  10. ruby-on-rails - 在 RSpec 中,如何以任意顺序期望具有不同参数的多条消息? - 2

    RSpec似乎按顺序匹配方法接收的消息。我不确定如何使以下代码工作:allow(a).toreceive(:f)expect(a).toreceive(:f).with(2)a.f(1)a.f(2)a.f(3)我问的原因是a.f的一些调用是由我的代码的上层控制的,所以我不能对这些方法调用添加期望。 最佳答案 RSpecspy是测试这种情况的一种方式。要监视一个方法,用allowstub,除了方法名称之外没有任何约束,调用该方法,然后expect确切的方法调用。例如:allow(a).toreceive(:f)a.f(2)a.f(1)

随机推荐