App耗電方式主要分為:
1,CPU計(jì)算
通過(guò)方法查看當(dāng)前CPU使用率,然后獲取當(dāng)前方法堆棧,就可以定位耗電原因。
首先獲取線程信息
thread_act_array_t threads;
mach_msg_type_number_t threadCount = 0;
const task_t thisTask = mach_task_self();
kern_return_t kr = task_threads(thisTask, &threads, &threadCount);
threads 結(jié)構(gòu)體如下
struct thread_basic_info {
time_value_t user_time; /* user 運(yùn)行的時(shí)間 */
time_value_t system_time; /* system 運(yùn)行的時(shí)間 */
integer_t cpu_usage; /* CPU 使用百分比 */
policy_t policy; /* 有效的計(jì)劃策略 */
integer_t run_state; /* run state (see below) */
integer_t flags; /* various flags (see below) */
integer_t suspend_count; /* suspend count for thread */
integer_t sleep_time; /* 休眠時(shí)間 */
};
判斷cpu_usage就可以知道當(dāng)前CPU的使用百分比,較高的情況即是耗電較多的情況
// 輪詢檢查多個(gè)線程 CPU 情況
+ (void)updateCPU {
thread_act_array_t threads;
mach_msg_type_number_t threadCount = 0;
const task_t thisTask = mach_task_self();
kern_return_t kr = task_threads(thisTask, &threads, &threadCount);
if (kr != KERN_SUCCESS) {
return;
}
for (int i = 0; i < threadCount; i++) {
thread_info_data_t threadInfo;
thread_basic_info_t threadBaseInfo;
mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) {
threadBaseInfo = (thread_basic_info_t)threadInfo;
if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) {
integer_t cpuUsage = threadBaseInfo->cpu_usage / 10;
if (cpuUsage > 90) {
//cup 消耗大于 90 時(shí)打印和記錄堆棧
NSString *reStr = smStackOfThread(threads[i]);
//記錄數(shù)據(jù)庫(kù)中
[[[SMLagDB shareInstance] increaseWithStackString:reStr] subscribeNext:^(id x) {}];
NSLog(@"CPU useage overload thread stack:\n%@",reStr);
}
}
}
}
}
GCD 的 dispatch_block_create_with_qos_class 方法指定隊(duì)列的 Qos 為 QOS_CLASS_UTILITY,將計(jì)算工作放到這個(gè)隊(duì)列的 block 里。在 QOS_CLASS_UTILITY 這種 Qos 模式下,系統(tǒng)針對(duì)大量數(shù)據(jù)的計(jì)算,以及復(fù)雜數(shù)據(jù)處理專門(mén)做了電量?jī)?yōu)化。
2,I/O 操作
I/O 操作會(huì)破壞低功耗狀態(tài),所以要減少操作次數(shù)??梢韵仁褂肗SCache來(lái)存儲(chǔ),等數(shù)據(jù)存儲(chǔ)達(dá)到閾值之后再將數(shù)據(jù)進(jìn)行I/O操作。比如我們常見(jiàn)的SDWebImage 就是這樣實(shí)現(xiàn)的
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
return [self.memCache objectForKey:key];
}
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key {
// 檢查 NSCache 里是否有
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
return image;
}
// 從磁盤(pán)里讀
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
return diskImage;
}
NSCache 是線程安全的,NSCache 會(huì)在到達(dá)預(yù)設(shè)緩存空間值時(shí)清理緩存,這時(shí)會(huì)觸發(fā) cache:willEvictObject: 方法的回調(diào),在這個(gè)回調(diào)里就可以對(duì)數(shù)據(jù)進(jìn)行 I/O 操作,達(dá)到將聚合的數(shù)據(jù) I/O 延后的目的。I/O 操作的次數(shù)減少了,對(duì)電量的消耗也就減少了。