今天下了個軟件,可以記錄手機解鎖的次數(shù)和使用時間,當然啦,App 必須在后臺運行著。當時比較納悶的是有什么 API 可以接收設(shè)備解鎖事件或通知的,Google 了下,還真有哎——我是鏈接。
設(shè)備鎖定的狀態(tài)
由上面的回答可以知道,設(shè)備在鎖定、解鎖的時候,SpringBoard 都會發(fā)出通知,iPhoneDevWiki 這里能找到更多有趣的通知(注意:黃色標注的通知是有狀態(tài)變量與之關(guān)聯(lián)的,后面會用到)。貼訂閱通知的代碼:
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), // 獲取通知中心
NULL, // 設(shè)置觀察者
deviceLockStatusChanged, // 接收到通知時的回調(diào)函數(shù)
CFSTR("com.apple.springboard.lockstate"), // 通知名
NULL, // 要觀察的對象
CFNotificationSuspensionBehaviorDeliverImmediately // 決定應用在后臺如何處理通知的標記
);
這里所用的通知中心并不是我們常用的 [NSNotificationCenter defalutCenter]
,而是 CFNotificationCenterRef
對象。提一下,即便前者的底層確實是由 CFNotificationCenter
實現(xiàn)的,但它們兩者不是 Toll-Free Bridged?;氐?CFNotificationCenterRef
,有下面三個函數(shù)獲得不同的通知中心:
- CFNotificationCenterGetLocalCenter(void);
- CFNotificationCenterGetDistributedCenter(void);
- CFNotificationCenterGetDarwinNotifyCenter(void);
第一個是我們熟悉的 Local Center,可以理解為通知的行為完全由本進程維護,作用域也僅在本進程;
第二個是 Distributed Center,如果有看到這個函數(shù)聲明上的編譯條件,你就會發(fā)現(xiàn)僅在桌面系統(tǒng)上才有 Distributed Center 可用。它可以實現(xiàn)兩個進程之間的通信,感興趣可以看看 Communicating With the Target Application ,似乎是通過 BundleID 實現(xiàn)對特定應用發(fā)送通知;
第三個是本文的重點,Darwin Center 的服務(wù)由系統(tǒng)的一個守護進程維護,相比 Local Center,通知的作用域擴大到了所有進程。這就為什么我們的應用能夠接收到 SpringBoard.app 發(fā)送的通知。本文用的是用 CoreFoundation 的函數(shù)實現(xiàn)接收通知,除此之外,文檔里還提到了利用 Mach Port, File Descriptors, Signal 等方法。查看 Darwin Notification Concepts 了解更多。
接下來要講講回調(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()
可以生成一個 token 值用來關(guān)聯(lián)某一個通知,接著用 notify_get_state()
就可以獲得響應狀態(tài)值。最后是 notify_cancel()
,它用來取消跟 token 相關(guān)聯(lián)的通知和釋放相應的資源,按 manual pages 的描述好像只針對利用 Mach Port 和 File Descriptors 接收消息時創(chuàng)建的資源。具體到這個回調(diào)函數(shù),不太清楚底層做了什么,但我們能看到的是 token 被清零了。
后臺運行
獲得設(shè)備鎖定、解鎖的方法有了,接著是要讓應用保存生命力,不能讓它被掛起,否則就統(tǒng)計不了次數(shù)了。比較常見的方法就是循環(huán)播放一段空白的聲音,然后在 Info.plist 里面添加相應的字段(KEY: UIBackgroundModes, VALUE: audio),或者直接在 Capabilities 里面的 Background Modes 中 Audio 的復選框中打個勾。
完
??