iOS UIBackgroundMode 远程通知在4G网络下无法工作

10

我正在测试带有content-available=1的推送通知,但除非在Wi-Fi上,否则似乎无法将其发送到应用程序后台。

在推送通知处理程序的开头,我有一个简单的日志语句:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
                                                    fetchCompletionHandler:(void (^)   (UIBackgroundFetchResult))completionHandler {

    NSLog(@"Notification received: %@", userInfo);
    completionHandler(UIBackgroundFetchResultNewData);
}

这是我的测试:

  1. 运行应用程序,然后按下主页按钮将应用程序放在后台。
  2. 发送一条内容为content-available=1的推送通知。
  3. 观察控制台日志。

在Wi-Fi网络下,控制台日志会显示通知。如果我进入“设置”并关闭Wi-Fi,切换到4G网络,则通知不再出现在日志中(尽管它们会从屏幕顶部滑动进入,所以我知道它们已经被传递了)。

没有崩溃日志,如果我手动点击通知,则通知会被记录。此外,如果我在Xcode中调试应用程序,则不会出现此问题。(即,如果我在Xcode中调试,则应用程序将在4G背景下接收通知)。是否有其他人遇到了这种情况?或者是我做错了什么?

编辑:具体来说,根据我的测试,如果以下条件成立,则上述远程通知委托方法不会被调用:

  1. 应用程序在后台运行
  2. 手机连接到LTE网络,未连接Wi-Fi网络
  3. 应用程序未在Xcode调试器中运行
  4. 收到内容为content-available=1的通知

但是,如果去掉条件2(即手机连接到Wi-Fi),则处理程序将被调用。


在设置中关闭wifi后,您可以尝试重新进入应用程序并再次按下主页按钮。然后发送您的远程通知。会发生什么? - Paulw11
发生了相同的结果 - 我没有收到通知 :( 我尝试了各种情况,但似乎永远无法在LTE后台接收通知。我总是可以在Wi-Fi上接收它。 - user1032657
你在 didReceiveRemoteNotification: fetchCompletionHandler: 方法中做了什么?你在这个方法中花费的时间超过30秒了吗?你是否向完成处理程序返回了正确的值?也许这是你的LTE运营商网络的问题?我已经在后台成功测试过,尽管我需要重新启动手机才能可靠地工作。 - Paulw11
我编辑了问题,以更清楚地说明回调方法中发生了什么以及测试条件是什么。当您成功测试时,应用程序是否在Xcode调试器中运行?我已在多个位置的多部手机上进行了测试。 - user1032657
2
有了这些信息,我现在可以确认你所看到的情况 - 我正在调试器下运行并且它是工作的。我更改了我的代码,调用了我的 Web 服务器上的 URL,创建了一个日志文件条目,并在未连接到调试器时运行。启用 WiFi 后,通知会显示并创建一个日志条目。禁用 WiFi 后,通知会显示但不会创建日志条目。看起来你应该向 Apple 记录一个 bug。 - Paulw11
3个回答

4

试一下以下代码:

// AppDelegate.h

@class ViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>
{

    NSString *DeviceToken;
    NSMutableDictionary *App_Messages;

    NSString *Longitude,*Latitude;
    NSMutableDictionary * badge;
}

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) ViewController *viewcontrollervc;

@property (strong, nonatomic) UINavigationController *navcontroller;

@property (nonatomic,retain)NSMutableDictionary *badge;

@property (nonatomic,retain)NSString *DeviceToken;

   

// AppDelegate.m

#import "ViewController.h"

@implementation AppDelegate

@synthesize badge,DeviceToken;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
    self.viewcontrollervc = [[ViewController alloc]initWithNibName:@"ViewController" bundle:nil];
    self.navcontroller = [[UINavigationController alloc]initWithRootViewController:self.viewcontrollervc];
    self.window.rootViewController = self.navcontroller;
    self.navcontroller.navigationBarHidden = YES;


    //Notification
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
    NSDictionary * remoteNotificationObj = [launchOptions objectForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"];
    if (remoteNotificationObj)
    {
        [self performSelector:@selector(handleRemoteNotificationWithUserInfo:) withObject:remoteNotificationObj afterDelay:3.0];
    }

    [self.window makeKeyAndVisible];
    return YES;

}


- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{

    [self handleRemoteNotificationWithUserInfo:userInfo];

}

-(void)handleRemoteNotificationWithUserInfo:(NSDictionary *)userInfo
{

    NSLog(@"userInfo - %@",userInfo);
    NSDictionary *alertData = [userInfo objectForKey:@"aps"];
    NSDictionary *returnDatalert=[alertData objectForKey:@"alert"];
    NSString *alertmsg=[returnDatalert objectForKey:@"body"];

    NSLog(@"alertmsg %@",alertmsg);
    self.badge = [NSMutableDictionary dictionaryWithDictionary:[alertData objectForKey:@"badge"]];
    NSString *notificationtype=[badge objectForKey:@"fnct"];

    NSLog(@"%@",notificationtype);

}


- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{

   NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken: %@", deviceToken);
    NSString *dt = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    dt = [dt stringByReplacingOccurrencesOfString:@" " withString:@""];
    self.DeviceToken=dt;
    NSLog(@"~~~~devToken(dv)=%@",deviceToken);

}

- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{

    NSLog(@"Failed to get token, error: %@", error);

}

我的APNS设置没有问题,我可以正常接收通知。唯一出现意外情况的时候是在4G后台接收通知的时候。 - user1032657
请简要解释此过程中发生了什么。 - bhavik

1

根据这里的评论者反馈和在多个设备上进行的重复测试,这似乎是iOS上的一个错误(或预期行为)。


0

对我来说,它在Wi-Fi和4G上工作(在移动设置中强制关闭LTE),但在LTE上不起作用。

更新:经过大量调试,我发现当使用LTE时,对我而言与两个因素有关。一个是电源。我发现如果iPhone插入了电源,应用程序就会按预期被唤醒。如果没有插入电源,则应用程序不会响应content-available = 1而被唤醒。第二个是设备设置。尽管每个相关的设置都正确设置了,但对于我来说,“重置所有设置”修复了这个问题。

假设这不是Apple的错误,我的猜测是,随着iOS为给定应用程序标识符开发能源配置文件,它选择在某些情况下(网络状态、电池状态等)不唤醒使用过多后台周期的应用程序。例如,不正确地使用beginBackgroundTaskWithExpirationHandler,导致应用程序在后台保持活动状态并迫使iOS将其过期。即使修复了过多的后台使用也可能无法纠正该问题,因为iOS已确定您的应用程序是后台程序。这解释了为什么“重置所有设置”可以为我解决问题。

不幸的是,这一切只是基于2-3天调试此问题的猜测,由于推送通知中存在如此多的变量,更不用提模糊和不同的文档,我们可能永远无法确定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接