最小化 IOS 应用程序时,后台处理在几秒钟后被终止

Wil*_*ior 5 xcode objective-c ios react-native react-native-ios

介绍

\n

我用 制作了一个VOIP 拨号器应用程序react-native。我正在使用react-native-callkeep在接到电话时向用户显示默认的IOS呼叫屏幕(在后台和前台),并且我正在使用react-native-push-notification弹出本地通知当接到电话时。

\n

问题

\n

当我最小化应用程序(使用主页按钮)并且应用程序收到呼叫时,它不会显示本地通知或呼叫屏幕。\n但是,如果在最小化操作之后立即接到呼叫,则屏幕和通知出现。

\n

对所发生情况的技术解释

\n

阅读日志,我发现 IOS 在打印以下行时终止了我的应用程序后台处理:

\n

15:08:23.085311-0300 symptomsd com.vmaxtelecom.vmaxfone: Foreground: false

\n

此后,前景或背景都不起作用

\n

代码解释

\n

也许问题不在于react-native-callkeep或react-native-push-notification本身(因为它们按预期工作)。我认为这是 IOS 本身的问题。

\n

我发现这个线程准确地定义了我身上发生的事情。

\n

我启用的功能

\n

在此输入图像描述

\n

我正在使用什么

\n

react-native version 0.61.5

\n

Xcode version 11.5 (11E608c) on a MacOS 10.15.5 Catalina

\n

IOS 13.4.1 on a Iphone 8

\n

I\'m using a release version of my project (The debug version does the same thing too)

\n

我的AppDelegate.m(只有后面的部分didFinishLaunchingWithOptions最重要)

\n
#import "AppDelegate.h"\n\n#import <React/RCTBridge.h>\n#import <React/RCTBundleURLProvider.h>\n#import <React/RCTRootView.h>\n#import <UserNotifications/UserNotifications.h>\n#import <RNCPushNotificationIOS.h>\n#import "RNCallKeep.h"\n\n#if DEBUG\n#import <FlipperKit/FlipperClient.h>\n#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>\n#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>\n#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>\n#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>\n#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>\n\nstatic void InitializeFlipper(UIApplication *application) {\n  FlipperClient *client = [FlipperClient sharedClient];\n  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];\n  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];\n  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];\n  [client addPlugin:[FlipperKitReactPlugin new]];\n  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];\n  [client start];\n}\n#endif\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{\n\nif ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){\n    [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];\n}\n\n#if DEBUG\n  InitializeFlipper(application);\n#endif\n\n  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];\n  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge\n                                                   moduleName:@"VMAXFone"\n                                            initialProperties:nil];\n\n  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];\n\n  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];\n  UIViewController *rootViewController = [UIViewController new];\n  rootViewController.view = rootView;\n  self.window.rootViewController = rootViewController;\n  [self.window makeKeyAndVisible];\n\n  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];\n  center.delegate = self;\n\n  return YES;\n}\n\n-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler\n{\n  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);\n}\n\n- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge\n{\n#if DEBUG\n  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];\n#else\n  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];\n#endif\n}\n\n- (BOOL)application:(UIApplication *)application\ncontinueUserActivity:(NSUserActivity *)userActivity\n  restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler\n{\n  return [RNCallKeep application:application\n           continueUserActivity:userActivity\n             restorationHandler:restorationHandler];\n}\n\n- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings\n{\n [RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];\n}\n\n- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken\n{\n [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];\n}\n\n- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo\nfetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler\n{\n  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];\n}\n\n- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error\n{\n [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];\n}\n\n- (void)userNotificationCenter:(UNUserNotificationCenter *)center\ndidReceiveNotificationResponse:(UNNotificationResponse *)response\n         withCompletionHandler:(void (^)(void))completionHandler\n{\n  [RNCPushNotificationIOS didReceiveNotificationResponse:response];\n  completionHandler();\n}\n\n- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification\n{\n [RNCPushNotificationIOS didReceiveLocalNotification:notification];\n}\n\n@end\n
Run Code Online (Sandbox Code Playgroud)\n

我认为这不是必要的,但每个信息都很重要,所以这是我如何设置react-native-callkeep(该函数在应用程序启动时调用)

\n
function initCallKitIntegration() {\n  return async function (dispatch, getState) {\n    let activeCall = null\n    const uuids = {}\n    const options = {\n      ios: {\n        appName: \'VMAX Fone\',\n        imageName: \'phone_account_icon\',\n        includesCallsInRecents: true,\n        supportsVideo: false\n      }\n    }\n\n    RNCallKeep.setup(options).then(accepted => {})\n\n    let incomingCall = null\n\n    const {endpoint} = getState().pjsip\n    const {appState} = getState().pjsip\n\n    endpoint.on("call_received", (call) => {\n\n      RNCallKeep.addEventListener(\'answerCall\', () => {\n        //accept call here\n      })\n\n      RNCallKeep.addEventListener(\'endCall\', () => {\n        //decline call here\n      })\n\n      RNCallKeep.addEventListener(\'didPerformDTMFAction\', ({ digits, callUUID }) => {\n        //send dtmf digits here\n      })\n\n      RNCallKeep.addEventListener(\'didPerformSetMutedCallAction\', ({ muted, callUUID }) => {\n        //mute and unmute call here\n      })\n\n      RNCallKeep.addEventListener(\'didToggleHoldCallAction\', ({ hold, callUUID }) => {\n        //hold and unhold call here\n      })\n\n      endpoint.on("call_changed", (call) => {\n        //Change Call state here\n      })\n\n      endpoint.on("call_terminated", (call) => {\n        \n        //endCall here\n\n        RNCallKeep.endCall(uuids[call.getCallId()])\n\n        delete uuids[call.getCallId()]\n      })\n\n      if (appState != \'active\'){\n        RNCallKeep.displayIncomingCall(uuids[call.getCallId()], call.getRemoteNumber(), call.getRemoteNumber(), \'number\', false)\n      }\n    })\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

日志

\n
15:08:22 VMAXFone   Snapshotting a view (0x107269800, UIKeyboardImpl) that has not been rendered at least once requires afterScreenUpdates:YES.\n15:08:22 powerd Process runningboardd.28 Created SystemIsActive "application<com.vmaxtelecom.vmaxfone>28-56-1455:FBSceneSnapshotAction:sceneID:com.vmaxtelecom.vmaxfone-default" age:00:00:00  id:51539643472 [System: PrevIdle SysAct]\n15:08:22 SpringBoard    [sceneID:com.vmaxtelecom.vmaxfone-default] Sending scene action [SceneLifecycleEventOnly][0xa901] through WorkspaceServer: 0x2819e4300\n15:08:22 backboardd Lcurrent=258.9457 Lr=2.8323 DR=91.4263 factor=0.084515:08:23 SpringBoard    Application process state changed for com.vmaxtelecom.vmaxfone: <SBApplicationProcessState: 0x280321380; pid: 476; taskState: Running; visibility: Background>\n15:08:23 SpringBoard    [com.vmaxtelecom.vmaxfone] Generating image data for snapshot: <XBApplicationSnapshot: 0x10033e300; identifier: 5EA00F96-F22F-4CDA-907D-4E24FCC9119C; contentType: SceneContent>\n15:08:23 backboardd Lcurrent=258.9457 Lr=2.8347 DR=91.3501 factor=0.0846\n15:08:23 backboardd Lcurrent=258.9457 Lr=2.8370 DR=91.2738 factor=0.0847\n15:08:23 SpringBoard    [com.vmaxtelecom.vmaxfone] Generating image data for snapshot: <XBApplicationSnapshot: 0x107662e60; identifier: DAA57ADA-4C38-4904-9139-C0782D20A8CA; variantID: downscaled; \n15:08:23 SpringBoard    [com.vmaxtelecom.vmaxfone] Snapshot data for <XBApplicationSnapshot: 0x10033e300; \xe2\x80\xa64E24FCC9119C> written to file: /private/var/mobile/Containers/Data/Application/E4F918EF-7431-4A05-9845-344EB8B058DD/Library/SplashBoard/Snapshots/sceneID:com.vmaxtelecom.vmaxfone-default/5EA00F96-F22F-4CDA-907D-4E24FCC9119C@2x.ktx\n15:08:23 backboardd Lcurrent=258.9457 Lr=2.8394 DR=91.1976 factor=0.0848\n15:08:23 powerlogHelperd    {"msg":"CLCopyAppsUsingLocation", "event":"activity"}\n15:08:23 SpringBoard    [com.vmaxtelecom.vmaxfone] Snapshot data for <XBApplicationSnapshot: 0x107662e60; \xe2\x80\xa6C0782D20A8CA> written to file: /private/var/mobile/Containers/Data/Application/E4F918EF-7431-4A05-9845-344EB8B058DD/Library/SplashBoard/Snapshots/sceneID:com.vmaxtelecom.vmaxfone-default/downscaled/DAA57ADA-4C38-4904-9139-C0782D20A8CA@2x.ktx\n15:08:23 backboardd Lcurrent=258.9457 Lr=2.8418 DR=91.1215 factor=0.0849\n15:08:23 backboardd Lcurrent=258.9457 Lr=2.8441 DR=91.0455 factor=0.0850\n15:08:23 kernel memorystatus: set assertion priority(3) target VMAXFone:476\n15:08:23 runningboardd  [application<com.vmaxtelecom.vmaxfone>:476] Set jetsam priority to 3 [0] flag[1]\n15:08:23 runningboardd  [daemon<com.apple.SpringBoard>:56] Ignoring jetsam update because this process is not memory-managed\n15:08:23 runningboardd  Calculated state for application<com.vmaxtelecom.vmaxfone>: running-active (role: NonUserInteractive)\n15:08:23 runningboardd  [application<com.vmaxtelecom.vmaxfone>:476] Set darwin role to: NonUserInteractive\n15:08:23 runningboardd  [daemon<com.apple.SpringBoard>:56] Ignoring GPU update because this process is not GPU managed\n15:08:23 runningboardd  Calculated state for daemon<com.apple.SpringBoard>: running-active (role: UserInteractiveNonFocal)\n15:08:23 backboardd Lcurrent=258.9457 Lr=2.8465 DR=90.9694 factor=0.0851\n15:08:23 mediaserverd   -CMSessionMgr- CMSessionMgrHandleApplicationStateChange: CMSession: Client com.vmaxtelecom.vmaxfone with pid \'476\' is now Background Running. Background entitlement: YES ActiveLongFormVideoSession: NO WhitelistedLongFormVideoApp NO\n15:08:23 mediaserverd   AudioSessionServerImp.cpp:3517:HandleAudioSessionApplicationStateChangeListener: { "action":"application_state_changed", "session":{"ID":"0x1f672","PID":476,"name":"VMAXFone"}, "details":{"new_state":"Background Running"} }\n15:08:23 mediaserverd   -CMSessionMgr- CMSessionMgrHandleApplicationStateChange: CMSession: Client (null) with pid \'477\' is now Background Running. Background entitlement: NO ActiveLongFormVideoSession: NO WhitelistedLongFormVideoApp NO\n15:08:23 mediaserverd   -CMSessionMgr- CMSessionMgrHandleApplicationStateChange: CMSession: Sending stop command to (null) with pid \'477\' because client is not allowed to play in the background AND does not continue AirPlaying video when device locks\n15:08:23 mediaserverd   -CMSessionMgr- CMSessionMgrHandleApplicationStateChange: CMSession: Client (null) with pid \'405\' is now Background Running. Background entitlement: YES ActiveLongFormVideoSession: NO WhitelistedLongFormVideoApp NO\n15:08:23 mediaserverd   SSServerImp.cpp:1179:SystemSoundServerKillSoundsForPID: pid 477(ContactViewViewS)\n15:08:23 SpringBoard    <private> setEnabledTopics <private> ignoredTopics <private> opportunisticTopics <private> sendToDaemon: YES\n15:08:23 apsd   Looking up connection on peer: 1b848900   found <private>\n15:08:23 apsd   <private>: connection set enabled topics from <private> to <private>\n15:08:23 apsd   <private>: connection set opportunistic topics from <private> to <private>\n15:08:23 apsd   <private> setEnabledTopics:<private> ignoredTopics:<private> opportunisticTopics:<private> forCategory UsesALS pretend NO change Downgraded\n15:08:23 apsd   <private> cancelling any pending filter updates\n15:08:23 apsd   <private> received topic update for Server pretend YES but there is no change.\n15:08:23 apsd   <private> no change detected between the new and old server filter - cancelling any pending updates\n15:08:23 apsd   <private> flush any pending work that ALS told us to queue for its managed topics <private>, processMode systemToken\n15:08:23 apsd   <private> flush any pending work that ALS told us to queue for its managed topics <private>, processMode userToken\n15:08:23 apsd   <private> received topic update for Normal pretend NO but there is no change.\n15:08:23 SpringBoard    <private> setEnabledTopics <private> ignoredTopics <private> opportunisticTopics <private> sendToDaemon: NO\n15:08:23 locationd  {"msg":"#CLIUA Marking change", "clientKey":"com.vmaxtelecom.vmaxfone", "reason":"Decaying in-use status from process state", "assertionLevel":3, "coming":1}\n15:08:23 locationd  {"msg":"#CLIUA Marking change", "clientKey":"com.vmaxtelecom.vmaxfone", "reason":"Process state from RunningBoard", "assertionLevel":4, "coming":0}\n15:08:23 backboardd Lcurrent=258.9457 Lr=2.8489 DR=90.8935 factor=0.0852\n15:08:23 CommCenter #I BundleID: <private> is no longer a foreground app\n15:08:23 backboardd Lcurrent=258.9457 Lr=2.8513 DR=90.8176 factor=0.0853\n15:08:23 symptomsd  NBSM Current state: idle, changed: systemForeground to 0 for net type 0\n15:08:23 symptomsd  NBSM Eligible to go to active\n15:08:23 symptomsd  NBSM Current state: idle, changed: systemForeground to 0 for net type 0, eligible for active but constraints unsatisfied (0,0,0)\n15:08:23 symptomsd  NBSM Current state: idle, changed: systemForeground to 0 for net type 0, ineligible for positive as nil pred, wifi (0x0) cell (0x0)\n15:08:23 symptomsd  NBSM Current state: idle, changed: systemForeground to 0 for net type 0, ineligible for broken as nil pred, wifi (0x0) cell (0x0)\n15:08:23 symptomsd  com.vmaxtelecom.vmaxfone: Foreground: false\n
Run Code Online (Sandbox Code Playgroud)\n

如果有什么概念或东西可以防止我的应用程序被 IOS 本身杀死,我将不胜感激您的帮助。

\n

[编辑]

\n

所以,我在寻找并发现了一些对我有帮助的东西:

\n
    \n
  • My app, even with the background modes, needs to USE them to be a valid app to run on background and foreground.

    \n
  • \n
  • The OS needs to recognize the app as a valid app to run the background mode (In my case, be a valid VOIP dialer).

    \n
  • \n
\n

What exactly was happening

\n

My app processes wasn\'t recognized as any of the background modes i was using, so it was snapshotted, not killed. As viewed on the below image, taken from this article on the Apple website, there was no event to be handled, so it was snapshotted.

\n

在此输入图像描述

\n

So now, my problem is how can i make my app be recognized as a valid VOIP dialer app by the Apple store itself.

\n

This question is now ended.

\n