Getting state for system wide notifications in iOS and OS X

后端 未结 1 837
粉色の甜心
粉色の甜心 2020-12-14 05:17

I am trying to write a code which will handle turning on/off screen on iOS (You can take a look at this question where similar problem is discussed). I included OSX tag for

相关标签:
1条回答
  • 2020-12-14 05:47

    So, I built a very simple experiment. I ran this on a jailbroken iOS 6.1 iPhone 5, outside the debugger.

    Code

    I built a consumer app, with the following code:

    #define EVENT "com.mycompany.bs"
    
    - (void)registerForNotifications {
        int result = notify_register_dispatch(EVENT,
                                              &notifyToken,
                                              dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0l),
                                              ^(int info) {
                                                  uint64_t state;
                                                  notify_get_state(notifyToken, &state);
                                                  NSLog(@"notify_register_dispatch() : %d", (int)state);
                                              });
        if (result != NOTIFY_STATUS_OK) {
            NSLog(@"register failure = %d", result);
        }
        CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                        NULL, // observer
                                        notifyCallback, // callback
                                        CFSTR(EVENT), // event name
                                        NULL, // object
                                        CFNotificationSuspensionBehaviorDeliverImmediately);
    }
    
    static void notifyCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
        uint64_t state;
        notify_get_state(notifyToken, &state);
        NSLog(@"notifyCallback(): %d", (int)state);
    }
    

    So, as you see, it uses two different methods to register for the same custom event. I startup this app, let it register for the event, then put it into the background (home button press).

    Then, the producer app, which lets me generate the event with a button press:

    double delayInSeconds = 0.001;
    
    dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l);
    dispatch_async(q, ^(void) {
        notify_set_state(notifyToken, 2);
        notify_post(EVENT);        
    });
    
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, q, ^(void){
        notify_set_state(notifyToken, 3);
        notify_post(EVENT);
    }); 
    

    Results

    I then ran the producer app, manually generating a pair of events about every two seconds. As you can see, the producer quickly posts the event with a state of 2, and then immediately posts another event with a state of 3. So, the consumer should print out 2 then 3, for both callback methods, if this is working perfectly. It does not (as you feared):

    Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    
    Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    
    Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    
    Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 2
    Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    
    Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    
    Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    
    Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 2
    Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
    

    I tried changing one consumer registration method to use CFNotificationSuspensionBehaviorCoalesce (instead of delivering immediately). Results:

    Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    
    Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
    Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    
    Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
    Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    
    Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
    Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    
    Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
    Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    
    Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
    Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    
    Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
    Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    
    Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
    Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
    

    I then tried changing the queue priority of the notify_register_dispatch() consumer to high, instead of background priority. Results:

    Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    
    Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
    Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    
    Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
    Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    
    Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
    Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    
    Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
    Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    
    Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
    Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    
    Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
    Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    
    Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
    Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
    Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    
    Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
    Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
    Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
    

    Conclusions (?)

    • There is a problem as you suspected, and it's not just with the SBGetScreenLockStatus call. Sometimes, the consumer never saw the state set to 2.
    • If I increased the producer time delay to 5 msec, I never saw the problem. So, this may only be an issue for events really close in time. Screen lock / unlock probably isn't a big deal. Obviously, slower phones (iPhone < 5) will respond differently.
    • The static notifyCallback() method seemed to get called back first, unless the GCD callback block was put on the high priority queue. Even then, usually the static callback function was called first. Many times, the first method called back got the correct state (2), while the second one did not. So, if you have to live with the problem, you might choose the callback mechanism that seemed to perform best (or at least, prototype this yourself, inside your app).
    • I can't say that the suspensionBehavior parameter made much difference. That said, depending on how iOS is posting the events, they may be using a call like CFNotificationCenterPostNotification that may ignore the consumers' behavior request.
    • If you look at this Apple document, you'll see two things.

      1. First, notify_set_state was not part of the very original API
      2. Second, the very first paragraph in that document says

    Darwin Notification API Reference

    These routines allow processes to exchange stateless notification events.

    So, maybe we're just trying to do something that's not consistent with the original design :(

    • If you also look at Apple's NotificationPoster example, you see that they don't use notify_get_state and notify_set_state to convey state. They pass it with the notification as a user info dictionary. Obviously, if you're observing Apple's iOS events, you have no control over how the events are posted. But, in apps where you get to create the producer and consumer, I'd stay away from notify_set_state.
    0 讨论(0)
提交回复
热议问题