統(tǒng)計(jì) iOS 設(shè)備鎖定、解鎖次數(shù)

今天下了個(gè)軟件,可以記錄手機(jī)解鎖的次數(shù)和使用時(shí)間,當(dāng)然啦,App 必須在后臺(tái)運(yùn)行著。當(dāng)時(shí)比較納悶的是有什么 API 可以接收設(shè)備解鎖事件或通知的,Google 了下,還真有哎——我是鏈接。

設(shè)備鎖定的狀態(tài)

由上面的回答可以知道,設(shè)備在鎖定、解鎖的時(shí)候,SpringBoard 都會(huì)發(fā)出通知,iPhoneDevWiki 這里能找到更多有趣的通知(注意:黃色標(biāo)注的通知是有狀態(tài)變量與之關(guān)聯(lián)的,后面會(huì)用到)。貼訂閱通知的代碼:

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), // 獲取通知中心
                                NULL,  // 設(shè)置觀察者
                                deviceLockStatusChanged,  // 接收到通知時(shí)的回調(diào)函數(shù)
                                CFSTR("com.apple.springboard.lockstate"),  // 通知名
                                NULL,  // 要觀察的對(duì)象
                                CFNotificationSuspensionBehaviorDeliverImmediately  // 決定應(yīng)用在后臺(tái)如何處理通知的標(biāo)記
                                );

這里所用的通知中心并不是我們常用的 [NSNotificationCenter defalutCenter],而是 CFNotificationCenterRef 對(duì)象。提一下,即便前者的底層確實(shí)是由 CFNotificationCenter 實(shí)現(xiàn)的,但它們兩者不是 Toll-Free Bridged?;氐?CFNotificationCenterRef,有下面三個(gè)函數(shù)獲得不同的通知中心:

  1. CFNotificationCenterGetLocalCenter(void);
  2. CFNotificationCenterGetDistributedCenter(void);
  3. CFNotificationCenterGetDarwinNotifyCenter(void);

第一個(gè)是我們熟悉的 Local Center,可以理解為通知的行為完全由本進(jìn)程維護(hù),作用域也僅在本進(jìn)程;
第二個(gè)是 Distributed Center,如果有看到這個(gè)函數(shù)聲明上的編譯條件,你就會(huì)發(fā)現(xiàn)僅在桌面系統(tǒng)上才有 Distributed Center 可用。它可以實(shí)現(xiàn)兩個(gè)進(jìn)程之間的通信,感興趣可以看看 Communicating With the Target Application ,似乎是通過 BundleID 實(shí)現(xiàn)對(duì)特定應(yīng)用發(fā)送通知;
第三個(gè)是本文的重點(diǎn),Darwin Center 的服務(wù)由系統(tǒng)的一個(gè)守護(hù)進(jìn)程維護(hù),相比 Local Center,通知的作用域擴(kuò)大到了所有進(jìn)程。這就為什么我們的應(yīng)用能夠接收到 SpringBoard.app 發(fā)送的通知。本文用的是用 CoreFoundation 的函數(shù)實(shí)現(xiàn)接收通知,除此之外,文檔里還提到了利用 Mach Port, File Descriptors, Signal 等方法。查看 Darwin Notification Concepts 了解更多。

接下來(lái)要講講回調(diào)函數(shù)了:

static void deviceLockStatusChanged(CFNotificationCenterRef center,
                                void *observer,
                                CFStringRef name,
                                const void *object,
                                CFDictionaryRef userInfo){
NSString *nameString = (__bridge NSString*)name;
int token;
uint64_t state;
notify_register_check(nameString.UTF8String, &token);
notify_get_state(token, &state);
NSLog(@"%@: token: %d, state: %llu", nameString, token, state);
if (state == 0) {
    counter++;
}
notify_cancel(token);

}

函數(shù)的原型是直接抄文檔的,notify_register_check() 可以生成一個(gè) token 值用來(lái)關(guān)聯(lián)某一個(gè)通知,接著用 notify_get_state() 就可以獲得響應(yīng)狀態(tài)值。最后是 notify_cancel(),它用來(lái)取消跟 token 相關(guān)聯(lián)的通知和釋放相應(yīng)的資源,按 manual pages 的描述好像只針對(duì)利用 Mach Port 和 File Descriptors 接收消息時(shí)創(chuàng)建的資源。具體到這個(gè)回調(diào)函數(shù),不太清楚底層做了什么,但我們能看到的是 token 被清零了。

后臺(tái)運(yùn)行

獲得設(shè)備鎖定、解鎖的方法有了,接著是要讓應(yīng)用保存生命力,不能讓它被掛起,否則就統(tǒng)計(jì)不了次數(shù)了。比較常見的方法就是循環(huán)播放一段空白的聲音,然后在 Info.plist 里面添加相應(yīng)的字段(KEY: UIBackgroundModes, VALUE: audio),或者直接在 Capabilities 里面的 Background Modes 中 Audio 的復(fù)選框中打個(gè)勾。

??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,624評(píng)論 30 472
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,063評(píng)論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評(píng)論 19 139
  • 1、OC中創(chuàng)建線程的方法是什么?如果指定在主線程中執(zhí)行代碼?如何延時(shí)執(zhí)行代碼。【難度系數(shù)★★】 1)創(chuàng)建線程的方法...
    木旁_G閱讀 2,085評(píng)論 2 16
  • 紐約比加州快3小時(shí), 但并不意味加州慢了。 有些人22歲畢業(yè), 但熬了5年才獲得一份好工作。 有些人25歲就當(dāng)上C...
    暴有料閱讀 708評(píng)論 0 1

友情鏈接更多精彩內(nèi)容