现在的位置: 首页 > 移动开发 > 正文

IOS后台运行机制 与 动作

2019年08月23日 移动开发 ⁄ 共 6683字 ⁄ 字号 评论关闭

当用户按下"Home"键或者系统启动另外一个应用时,前台foreground应用首先切换到Inactive状态,然后切换到Background状态。此转换将会导致先后调用应用代理的applicationWillResignActive:和applicationDidEnterBackground:方法。

  在applicationDidEnterBackground:方法返回后,大部分应用在之后不久转入suspended状态。对于请求特定后台background任务的应用,比如播放音乐应用,或者那些请求需要额外执行时间的应用,可能会继续执行更长一段时间。


  注:应用从froeground切换到background只有在支持多任务并且运行iOS4.0或更新版本系统的设备上才会发生。所有其它的情况,应用不是切向后台,而是直接终止,并且从内存中清除。

  应用切向后台background时应该做什么:

  应用可以在applicationDidEnterBackground:方法中做些切向background状态前需要做的一些准备工作,当切向background状态时,所有的应用需要做以下事情:

  (1)应用界面快照。当applicationDidEnterBackground:方法返回时,系统保存应用界面的快照,并且使用快照图片作为转换动画。如果在你的应用界面中有涉及到敏感信息的视图,则你应该在applicationDidEnterBackground:方法返回前隐藏或者修改这些视图。

  (2)保存用户数据和应用状态信息。所有没有保存的改变都应该在切向background状态前写入磁盘以保存。这一步是必须的,因为你的应用在后台时很有可能因为多种其它原因而被很快kill掉。根据需要你可以在background
thread后台线程中执行这些操作。


  (3)释放尽可能多的内存资源。

  applicationDidEnterBackground:方法允许最多有5秒的时间去完成任何任务然后返回。实际中,此方法应该尽可能快的返回。如果在时间到期之后,此方法没有返回,则应用即被kill掉,并且清除所占用的内存。如果你的应用确实需要更多的时间去执行任务,可以调用beginBackgroundTaskWithExpirationHandler:方法请求后台执行时间,然后启动一个能长期执行任务的线程。无论你是否启动一个执行后台任务的线程,applicationDidEnterBackground:方法都必须在5秒后退出。

  注:UIApplicationDidEnterBackgroundNotification通知也会发送,以让应用对此通知感兴趣的部分知道当前应用正切向background状态。你的应用中的对象可以使用默认的通知中心注册这个通知。

  依据不同的应用场合,应用切向后台时还有很多其它的事情需要做,比如active状态的Bonjour服务应该暂停,应用应该停止调用OpenGL
ES函数。


  因为前台应用在使用系统资源和硬件时一直比后台应用具有更高的优先权。运行在后台的应用应该对此差异有心理准备,并且在后台运行时要调整它们的访问资源行为。特别的,当应用切向background时尤其要遵循以下几点:

  (1)不要在应用代码中调用任何OpenGL ES的东西。当应用在后台运行时不可以创建EAGLContext对象或者发出任何OpenGL
ES绘画命令,使用这些调用将会导致应用立即被kill掉。应用也必须保证先前提交发出的所有命令在应用切向background状态前都已执行完毕。具体细节请参考“OpenGL ES Programming Guide for iOS”中“Implementing a Multitasking-aware OpenGL ES Application”部分。


  (2)在应用挂起suspended之前取消所有Bonjour相关的服务。当应用转向后台,并且在被挂起前,应用应该unregister
Bonjour服务并且关掉任何和网络服务相关的sockets监听。挂起的应用是没法响应这些服务请求的。如果你的应用不关掉这些和Bonjour相关的服务,当应用被挂起的时候,系统会自动帮你关掉这些服务。


  (3)在基于网络sockets的应用中,需要处理连接失败的情况。当你的应用因为某些原因而被挂起时,系统可能会拆除socket连接。只要你的应用对尽可能多的网络错误情况都有很好的处理,像丢掉信号等,此类问题不会导致你的应用出现不正常。当应用从后台退出恢复执行时,如果遇到sockets使用错误,简单的重建socket连接即可。

  (4)在切向background状态前保存应用状态。在低内存告警时,后台应用可能会被清除出内存以释放空间。处于suspended状态的应用被优先清除内存,并且在被清除前不会给出任何通知。因此,当应用切入background状态前一定要保存足够多的应用状态信息以便后面恢复时使用。

  (5)当切向后台时,释放所有不再需要的内存。如果你的应用保持着一个很大的内存缓存对象(比如图像),则切入后台前,释放所有的对这些缓存对象的引用。

  (6)在被挂起前停止使用系统共享资源。使用系统共享资源(比如Address Book或Calendar
Data)的应用,在被挂起前必须停止对这些共享资源的使用。对这些资源的使用,前台应用具有更高的优先使用权,如果发现你的应用在被挂起后还没有停止对这些共享资源的使用,则应该将被kill掉。


  (7)避免更新应用窗口和视图。当应用处在后台时,应用窗口和视图是不可见的,所以不需要更新它他。尽管在后台创建和操纵窗口和视图对象并不会导致应用被kill掉,但是可以考虑将这些工作推迟到应用返回前台时执行。

  (8)响应外部附件连接和失去连接通知。针对和外部附件有通信的应用,当应用切向background状态时,系统会发送一个disconnection通知。应用必须注册此通知并且使用它去关掉当前的附件访问session。当应用返回foreground时,会有一个与之匹配的通知被发送,给应用提供重新建立session的机会。

  (9)切向后台时,清除行为警告相关的资源。为了在应用相互切换之间保存应用上下文,当应用切向后台时,系统并不自动dismiss
action sheets(UIActionSheet)和alert views(UIAlertView)。由应用设计者去提供具本的清除方案。对于运行在iOS4.0版本之前的应用,在退出时action sheets和alerts仍然被dismiss掉,以让应用的取消处理函数有机会去运行。


  (10)切向后台时,移除所有敏感视图信息。因为系统会快照应用界面并且生成应用切换动画,所以带有敏感信息的视图或窗口必须隐藏或移除,具体原因前面已介绍。

  (11)应用在后台运行时执行最少量化的工作。系统给后台运行的应用的执行时间和给前台运行的应用相比,通常非常有限。如果应用在后台播放音频或者监测位置变化,则应用应该仅关注此任务,所有不必要的任务都应该被推迟。在后台执行时间过长的应用会被系统throttled
back或者直接被kill掉。

下来我们操作一下看看:
AppDelegate.h:

 ///

    UIBackgroundTaskIdentifier bgTask;

    NSUInteger counter;


AppDelegate.m:

- (void)backgroundHandler {

    

    NSLog(@"### -->backgroundinghandler");

    

    UIApplication* app = [UIApplicationsharedApplication];

    

    bgTask = [appbeginBackgroundTaskWithExpirationHandler:^{

        

        [app endBackgroundTask:bgTask];

        

        bgTask =UIBackgroundTaskInvalid;

        

    }];

    

    // Start the long-running task

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
^{

        

        while (1) {

            

            NSLog(@"counter:%d",counter++);

            [[UIApplicationsharedApplication]setApplicationIconBadgeNumber:numbers++];

            sleep(1);   

        }

    });

}



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

    /*

     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, called instead of applicationWillTerminate: when the user quits.

     */

    printf("\n applicationDidEnterBackground \n");

    

    

    //////////////////////////////////////

    BOOL backgroundAccepted = [[UIApplicationsharedApplication]setKeepAliveTimeout:600handler:^{

        [selfbackgroundHandler];

    }];

    

    if (backgroundAccepted)

        

    {

        

        NSLog(@"backgrounding accepted");

        

    }

    [selfbackgroundHandler];

}

info.plist:

Required background modes字段:

App provides Voice over IP services;


OK就可以看到后台程序在运行了!

步骤:

1.在info.plist里加入UIBackgroundModes键,其值为数组,数组之一为voip字符串:

<key>UIBackgroundModes</key><array><string>voip</string></array>

2.在程序启动的时候调用- (void)setupBackgroundHandler函数,函数体如下:

#pragma
mark - VoIP
 
-
(
void)setupBackgroundHandler
{   
    if(
UIUDeviceIsBackgroundSupported() )
    {
        if(
           [[UIApplication
sharedApplication] setKeepAliveTimeout:600 handler: ^
            {
                [self requestServerHowManyUnreadMessages];
            }
            ]
           )
        {
            UDLog(@"Set
Background handler successed!"
);
        }
        else
        {//failed
            UDLog(@"Set
Background handler failed!"
);
        }
    }
    else
    {
        UDLog(@"This
Deviece is not Background supported."
);
    }
}
 
-
(
void)requestServerHowManyUnreadMessages
{
    UIApplication*
app = [UIApplication sharedApplication];
     
    if([app
applicationState] == UIApplicationStateBackground)
    {
        NSArray *
oldNotifications = [app scheduledLocalNotifications];
        if ([oldNotifications
count] > 0)
            [app
cancelAllLocalNotifications];
        UILocalNotification*
alarm = [[[UILocalNotification alloc] init] autorelease];
        if (alarm)
        {
            alarm.fireDate
= [
NSDate dateWithTimeIntervalSinceNow:15];
            alarm.timeZone
= [
NSTimeZone defaultTimeZone];
            alarm.repeatInterval
= 0;
            alarm.soundName
= UILocalNotificationDefaultSoundName;
            alarm.alertBody
= @
"Time
to request MOA2 Server!"
;
            [app
scheduleLocalNotification:alarm];
        }
    }
    else if([app
applicationState] == UIApplicationStateActive)
    {
        UIAlertView
*alertView =  [[[UIAlertView alloc] init] autorelease];
        [alertView
setTitle:@
"alert"];
        [alertView
setMessage:@
"Time
to request MOA2 Server!"
];
        [alertView
addButtonWithTitle:
NSLocalizedString(@"cancel",nil)];
        [alertView
setDelegate:
nil];
        [alertView
show];
    }
}

解说:



- (BOOL)setKeepAliveTimeout:(NSTimeInterval)timeout handler:(void (^)(void))keepAliveHandler

函数功能:app每隔timeout唤醒一次。

0.要成功调用该函数,就必须在Info.plist里设UIBackgroundModes键的array值之一voip字符串.

1.timeout必须>=600

2.唤醒app的时间间隔是不精准的。

3.唤醒后只有10秒执行时间。即handler里的代码要在10秒类执行完。10秒后app再次被阻塞。

(可以用-backgroundTimeRemaining属性来返回剩余时间

4.该函数成功调用后,在程序生命周期内有效。

该函数的效果在回到前台的状况下,依然有效。(因此可以把它当timer使.) 

5.clearKeepAliveTimeout函数用来清除handler。

代码下载:http://download.csdn.net/detail/kaitiren/7930749

抱歉!评论已关闭.