App killed by OS in ios7 after some seconds -edit

2019-05-18 21:30发布

问题:

I am creating navigation based app for iOS 7, for it I am taking users location data, Using CoreLocation framework,

App requirement is to start getting users location in background at particular time, For that I have implemented Silent Pushnotification with didReceiveRemoteNotification fetchCompletionHandler: method,

I have successfully implement this Using Silent Pushnotification & it Call startUpdatingLocation and I am able to get location data in delegate method :

Using This Payload:

{"aps" : {"content-available" : 1},"SilentPush" : "4"}

I have enabled location & remote notification for Background mode:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
     __block UIBackgroundTaskIdentifier bgTask =0;
    UIApplication  *app = [UIApplication sharedApplication];
     bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [self.locationManager startUpdatingLocation];

 }];

didUpdateLocations

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
        lastLoc=[locations lastObject];
        [logFile addLocObject:[NSString stringWithFormat:@"Loc: %@",lastLoc]];
}

But Problem is :

After Some Seconds delegate method of location class is stops and it will not send any data if device is moving, And when i talking app to foreground it will called 'didFinishLaunhing' method , So i guess os will kill app even Location is updating, in Device Diagnostics & usage i am getting following crash report:

Application Specific Information:
MockUpApp2[390] has active assertions beyond permitted time: 
{(
    <BKProcessAssertion: 0x145ac790> identifier: Called by MockUpApp2, from -[AppDelegate application:didReceiveRemoteNotification:fetchCompletionHandler:] process: MockUpApp2[390] permittedBackgroundDuration: 40.000000 reason: finishTaskAfterBackgroundContentFetching owner pid:390 preventSuspend  preventIdleSleep  preventSuspendOnSleep 
)}

Yesterday I have asked this question now I am able to startlocation manager in background through push notification,

So please anybody can give solution of this problem.

Thank you.

Note: If I am running app in debugging mode, means I run app through XCode & not stoping it or not disconnecting , app will run.. In this case app will not stop by OS.

EDIT 1

As per @Stephen Darlington Answer if i remove all backgroundfatcher like,

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
        [self.locationManager startUpdatingLocation];

}

Now app will not call didUpdateLocations even once, :(

What should i write in this method?

Edit 2

As per Apple Doc say:

If your app is suspended or not running, the system wakes up or launches your app and puts it into the background running state before calling the method.

SO , is app should run in background for long as i enabled location backgound mode ,?

回答1:

I don't think background processing on iOS works in the way that you're expecting. It's not like on the Mac or Windows where you can run indefinitely in the background (even with the changes in iOS 7).

So, to directly answer your question: you kick off a task that can continue to run in the background. There are two "buts."

  1. The code block is the expiration handler. It is not the code that you want to run in the background. This code will get execute just before the background task is out of time (so in your case it gets executed shortly before your app is killed). Apple doesn't really document how long that is but it looks as though you're getting about 40 seconds. The code you want to run in the background are the lines after beginBackgroundTaskWithExpirationHandler. When you're done you say endBackgroundTask:
  2. Secondly, location updates work in the background without all the beginBackgroundTaskWithExpirationHandler: stuff. The delegate methods will get called even if the app is in the background

To summarise: remove the beginBackgroundTaskWithExpirationHandler: statement. You only need to add the startUpdatingLocation:.



回答2:

I did quite a bit of work with CLLocation some time ago and I have some comments and suggestion, while maybe not complete solution.

I see you are putting your reaction to the silent notification in beginBackgroundTaskWithExpirationHandler block. I do not think that is correct. I do believe you should use that backgroundTask pattern on the CLLocationDelegate methods, however.

More importantly, there is a good chance Apple will deny you the use of the locations flag for background processes. Take from my experience. I spent a lot of time working out a decent use of location services improving the accuracy of my own process. I even enabled many protections against inordinate battery use and they rejected it without discussion. I made it so it only used background services when the device was plugged into the charger. I appealed and made a good case for the responsible use, to no avail.

You should look up the terms and make sure your use case is included precisely as stated or it will not float.

I still say that you can get what you want without background services. Using significant location changes and region monitoring your app will not need anything but to save latest known location and respond to notifications.

I would think of a scheme something like this:

  • significant location change event comes in. Check speed/activity. You can do this without background services enabled.
  • if driving just save location. You will get frequent changes when driving so you will only be minutes behind.
  • not driving or idle, create a region of reasonable size depending on your accuracy requirements. As long as device is in that region, you can use the currently saved recent location. This will be most of the time throughout a 24 hour period so you are impacting the battery very little overall.

It seems to me you are going at this with the wrong perspective. It is logical to think that you will send message then respond by making CL do something. That is not the way the service is intended. Essentially, you set up the Location Manager and let it go.

You may benefit from looking at some of the patterns used in a repository I have on GitHub.

TTLocationHandler

It hasn't been updated in a year, and I don't remember all of what I learned working on it but it has served me solid as I haven't had to fiddle with it.

Following that example, you would stop and start, or switch to and from region or significant location changes monitoring all based on application state. No interaction needed.

Your location events are handled as they come, background or no.

In your remote notification response you would only read the latest location info or whatever info you require and act as per your intention. You do not call startUpdatingLocation here. If you are using location services in the background you should never have stopped it in the first place.

Edit- Proper use of Expiration Handler Block

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    __block UIBackgroundTaskIdentifier bgTask =0;
    UIApplication  *app = [UIApplication sharedApplication];
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
          // Do something here. Perhaps you'll log the error or respond with some fall back
          // This block will only be run if the handler expires without having been ended
          // In that case, you need to end it now or you will crash
          If (bgTask != UIBackgroundTaskInvalid) {
              [app endBackgroundTask:bgTask];
              [bgTask = UIBackgroundTaskInvalid];
          }

      }];

      // This is where the code you intend to run in the background starts

     [self.locationManager startUpdatingLocation];

     // Now tell the system you are finished, ending background task normally before exit

     If (bgTask != UIBackgroundTaskInvalid) {
         [app endBackgroundTask:bgTask];
         [bgTask = UIBackgroundTaskInvalid;
     }
}


回答3:

The method beginBackgroundTaskWithExpirationHandler: give the app time to to its stuffs, but it doesn't meant that the task can run forever.
You're app is killed because you are clearly running out time, as stated in the crash log.
You should get the same result just asking for location update without wrapping into the expiration handler.
I remember that in older systems I've managed something similar, by triggering location updates in background using region monitoring.
[EDIT]
To have best result while playing with location I suggest you to use real devices and maybe you are.