iOS:一段时间后在后台终止app

时间:2022-05-06 01:24:52

I'm trying to implement a passcode lock feature in my app that lets the user choose how much time must go by before the passcode is required for reentry (similar to the passcode functionality of the OS). So for example the user may be able to select that they want the passcode to be required 5, 10, 20 minutes after exiting the app into the background.

我正在尝试在我的应用程序中实现一个passcode锁定功能,让用户可以选择需要多少时间才能重新输入密码(类似于操作系统的passcode功能)。例如,用户可以选择他们想要的密码在退出应用程序后的5、10、20分钟。

I've tried to deal with presenting a passcode view in different ways, but it is often difficult to figure out the best way to present it, and so I had the idea that perhaps it is best to terminate the app after the time is up, and therefore I would only have to present the passcode screen when the app is launched.

我试图以不同的方式处理提交密码视图,但通常很难找出目前的最好方法,所以我的想法,也许最好终止应用程序在时间到了之后,因此我将只需要应用程序启动时的密码屏幕。

Is this possible to do? I had two thoughts about ways to approach this.

这可能吗?对此我有两个想法。

1) Have an NSTimer within the app delegate, start it when the app goes into the background, and then when/if the timer reaches the set number of minutes, then terminate the app? I could see a number of things going wrong with this, for example if the OS terminated the app to free up memory sooner than the timer finished. Although that wouldn't be a huge issue.

1)在app委托中设置一个NSTimer,当app进入后台时启动,当/如果计时器达到设定的分钟数时终止app?我可以看到这有很多问题,例如,如果操作系统终止了应用程序,以便在计时器完成之前释放内存。虽然这不是一个大问题。

2) Set an instance of NSDate when the app goes into the background. Then when the app is being launched, see if this date is more than x minutes ago, and present the passcode entry screen depending on that.

2)在app进入后台时设置NSDate实例。然后,当应用程序启动时,看看这个日期是否在x分钟之前,并根据这个日期显示密码输入屏幕。

I feel like both of these are a little off. I'm inexperienced with Timers, RunLoops, etc, so any advice is appreciated.

我觉得这两个都有点不太对劲。我对定时器、runloop等等都没有经验,所以我很感激你的建议。

3 个解决方案

#1


2  

Option 2 seems to be a good solution that we have used with success.

选项2似乎是我们成功使用的一个很好的解决方案。

#2


2  

Option 2. Use the ApplicationDelegate Lifecycle methods to drive it.

第二个选项。使用ApplicationDelegate的生命周期方法来驱动它。

  • application:didFinishLaunchingWithOptions:
  • 应用:didFinishLaunchingWithOptions:
  • applicationDidBecomeActive:
  • applicationDidBecomeActive:
  • applicationWillResignActive:
  • applicationWillResignActive:
  • applicationDidEnterBackground:
  • applicationDidEnterBackground:
  • applicationWillEnterForeground:
  • applicationWillEnterForeground:
  • applicationWillTerminate:
  • applicationWillTerminate:
  • applicationDidFinishLaunching:
  • applicationDidFinishLaunching:

In the applicationWillResignActive method persist the current timestamp to your UserDefaults, and in the applicationWillEnterForeground check this against the current time and if the passcode interval has passed, present your passcode. (probably best to clear the timestamp when you are active to minimise the chance of false triggering on receiving calls and SMS etc)

在applicationWillResignActive方法中,将当前的时间戳持久化到您的UserDefaults中,在applicationwillenter前台对当前时间进行检查,如果passcode interval已经通过,请给出您的密码。(可能最好在您处于活动状态时清除时间戳,以尽量减少在接收电话和SMS等时发生误触发的机会)

Depending on sensitivity you may want to prepare your views before entering foreground to obscure sensitive data, so they do not return in the unlocked state.

根据敏感性的不同,您可能希望在进入前台以模糊敏感数据之前准备视图,因此它们不会以解锁状态返回。

#3


1  

you can follow both for better result. for example use option 2 when app active from didFinishLaunchingWithOptions: and option 1 when application enable from - (void)applicationDidBecomeActive:(UIApplication *)application or - (void)applicationWillEnterForeground:(UIApplication *)application option 1-Easiest way is to schedule a NSTimer on the background run-loop. I suggest that the following code is implemented on your application delegate, and that you call setupTimer from applicationWillResignActive:.

你可以遵循这两者来获得更好的结果。例如,当程序从didFinishLaunchingWithOptions激活时使用选项2;当应用程序从- (void)applicationDidBecomeActive:(UIApplication *)应用程序或- (void) applicationwillenter前台:(UIApplication *)应用程序选项1-最简单的方法是在后台运行循环中安排一个NSTimer。我建议在您的应用程序委托上实现以下代码,并调用applicationWillResignActive:。

- (void)applicationWillResignActive:(UIApplication *)application
{
[self performSelectorInBackground:@selector(setupTimerThread) withObject:nil];
}
-(void)setupTimerThread;
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  NSTimer* timer = [NSTimer timerWithTimeInterval:10 * 60 target:self selector:@selector(triggerTimer:) userInfo:nil repeats:YES];
  NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
  [runLoop addTimer:timer forModes:NSRunLoopCommonModes];
  [runLoop run];
  [pool release];
}

-(void)triggerTimer:(NSTimer*)timer;
{
  // Do your stuff
}

in appDelegate .h

在appDelegate . h

 UIBackgroundTaskIdentifier bgTask;

in appDelegate .m

在appDelegate打烊

- (void)applicationDidEnterBackground:(UIApplication *)application
 {

UIApplication*    app = [UIApplication sharedApplication];

// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    // Synchronize the cleanup call on the main thread in case
    // the task actually finishes at around the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;

        }
   });
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

    // Synchronize the cleanup call on the main thread in case
    // the expiration handler is fired at the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;

        }
    });
});
NSLog(@"app entering background");
/*
 Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
 If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
 */

}
OR you could run the NSTimer on a background thread by with something like this (I am intentionally leaking the thread object):

或者您可以在后台线程上运行NSTimer,就像这样(我故意泄漏线程对象):

-(void)startTimerThread;
{
  NSThread* thread = [[NSThread alloc] initWithTarget:self selector:@selector(setupTimerThread) withObject:nil];
  [thread start];
}

try with this above code. we use both options its works fine for us. good luck

试试上面的代码。我们同时使用这两个选项,它对我们很有效。祝你好运

#1


2  

Option 2 seems to be a good solution that we have used with success.

选项2似乎是我们成功使用的一个很好的解决方案。

#2


2  

Option 2. Use the ApplicationDelegate Lifecycle methods to drive it.

第二个选项。使用ApplicationDelegate的生命周期方法来驱动它。

  • application:didFinishLaunchingWithOptions:
  • 应用:didFinishLaunchingWithOptions:
  • applicationDidBecomeActive:
  • applicationDidBecomeActive:
  • applicationWillResignActive:
  • applicationWillResignActive:
  • applicationDidEnterBackground:
  • applicationDidEnterBackground:
  • applicationWillEnterForeground:
  • applicationWillEnterForeground:
  • applicationWillTerminate:
  • applicationWillTerminate:
  • applicationDidFinishLaunching:
  • applicationDidFinishLaunching:

In the applicationWillResignActive method persist the current timestamp to your UserDefaults, and in the applicationWillEnterForeground check this against the current time and if the passcode interval has passed, present your passcode. (probably best to clear the timestamp when you are active to minimise the chance of false triggering on receiving calls and SMS etc)

在applicationWillResignActive方法中,将当前的时间戳持久化到您的UserDefaults中,在applicationwillenter前台对当前时间进行检查,如果passcode interval已经通过,请给出您的密码。(可能最好在您处于活动状态时清除时间戳,以尽量减少在接收电话和SMS等时发生误触发的机会)

Depending on sensitivity you may want to prepare your views before entering foreground to obscure sensitive data, so they do not return in the unlocked state.

根据敏感性的不同,您可能希望在进入前台以模糊敏感数据之前准备视图,因此它们不会以解锁状态返回。

#3


1  

you can follow both for better result. for example use option 2 when app active from didFinishLaunchingWithOptions: and option 1 when application enable from - (void)applicationDidBecomeActive:(UIApplication *)application or - (void)applicationWillEnterForeground:(UIApplication *)application option 1-Easiest way is to schedule a NSTimer on the background run-loop. I suggest that the following code is implemented on your application delegate, and that you call setupTimer from applicationWillResignActive:.

你可以遵循这两者来获得更好的结果。例如,当程序从didFinishLaunchingWithOptions激活时使用选项2;当应用程序从- (void)applicationDidBecomeActive:(UIApplication *)应用程序或- (void) applicationwillenter前台:(UIApplication *)应用程序选项1-最简单的方法是在后台运行循环中安排一个NSTimer。我建议在您的应用程序委托上实现以下代码,并调用applicationWillResignActive:。

- (void)applicationWillResignActive:(UIApplication *)application
{
[self performSelectorInBackground:@selector(setupTimerThread) withObject:nil];
}
-(void)setupTimerThread;
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  NSTimer* timer = [NSTimer timerWithTimeInterval:10 * 60 target:self selector:@selector(triggerTimer:) userInfo:nil repeats:YES];
  NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
  [runLoop addTimer:timer forModes:NSRunLoopCommonModes];
  [runLoop run];
  [pool release];
}

-(void)triggerTimer:(NSTimer*)timer;
{
  // Do your stuff
}

in appDelegate .h

在appDelegate . h

 UIBackgroundTaskIdentifier bgTask;

in appDelegate .m

在appDelegate打烊

- (void)applicationDidEnterBackground:(UIApplication *)application
 {

UIApplication*    app = [UIApplication sharedApplication];

// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    // Synchronize the cleanup call on the main thread in case
    // the task actually finishes at around the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;

        }
   });
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

    // Synchronize the cleanup call on the main thread in case
    // the expiration handler is fired at the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;

        }
    });
});
NSLog(@"app entering background");
/*
 Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
 If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
 */

}
OR you could run the NSTimer on a background thread by with something like this (I am intentionally leaking the thread object):

或者您可以在后台线程上运行NSTimer,就像这样(我故意泄漏线程对象):

-(void)startTimerThread;
{
  NSThread* thread = [[NSThread alloc] initWithTarget:self selector:@selector(setupTimerThread) withObject:nil];
  [thread start];
}

try with this above code. we use both options its works fine for us. good luck

试试上面的代码。我们同时使用这两个选项,它对我们很有效。祝你好运