三.能耗

3.1CPU

消耗取決於不一樣的因素:html

  • 對數據的處理(例如,對文本進行格式化)
  • 待處理的數據大小
  • 處理數據的算法和數據結構 執行更新的次數,尤爲是在數據更新後,觸發應用的狀態或UI進行更新。

最佳實踐:git

  • 針對不一樣的狀況選擇優化的算法:(實例小於43時)插入排序,歸併排序,(實例大於286時)雙樞軸快速排序,單樞軸快速排序。
  • 若是應用從服務器接受數據,儘可能減小須要在客戶端進行的處理。
  • 優化靜態編譯(ahead-of-time,AOT,預編譯)處理:動態編譯(just-in-time,JIT)處理的缺點在於它會強制用戶等待操做完成。可是激進的AOT處理則會致使計算資源的浪費。
  • 分析電量消耗:測量目標用戶的全部設備上的電量消耗。

3.2網絡

智能的網絡訪問管理可讓應用響應的更快,並有助於延長電池的壽命。應避免在沒有鏈接WiFi的狀況下進行高帶寬消耗的操做,好比視頻流。蜂窩無線系統(LTE,4G,3G等)對電量的消耗都遠大於WiFi信號。根源在於LTE設備基於多輸入,多輸出技術,使用多個併發信號以維護兩端的LTE連接。相似的,全部的蜂窩數據連接都會按期掃描以尋找更強的信號。github

  • 在進行任何網絡操做以前,先檢查合適的網絡鏈接是否可用;
  • 持續監視網絡的可用性,並在鏈接狀態發生變化時給予適當的反饋。

官方Reachability示例代碼:https://developer.apple.com/library/content/samplecode/Reachability/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007324-Intro-DontLinkElementID_2 Reachabilitypod:https://github.com/tonymillion/Reachability算法

檢查網絡狀態:api

-(BOOL)isAPIServerReachable{
	Reachability *r = [Reachability reachabilityWithHostName:@"api.yourdomain.com"];//1.檢查服務器域名是否可達
	
	return r.isReachable;//2.對網絡狀態NetworkStatus進行優化
}

-(void)performNetWorkOperation:(NSDictionary *) params completion:(void(^)(NSError *,id)) completion{//3.提供id類型的結果或NSError類型錯誤
	if (![self isAPIServerReachable]){
//		[self enqueueRequest:params completion:completion];4.對操做進行排隊
		NSError *err = [[NSError alloc]initWithDomain:@"network" code:1 userInfo:nil];//5.code需自定義常量
	}else{
//		[self doNetworkOperation:params completion:completion];//6.網絡可用觸發請求
	}
}

監控網絡並執行隊列:服務器

[@interface](https://my.oschina.net/u/996807) HPNewworkOps ()
[@property](https://my.oschina.net/property) (nonatomic , readonly) BOOL isAPISeverReachable;//檢測網絡是否可用標識
[@property](https://my.oschina.net/property) (nonatomic , strong) Reachability *reachability;//監聽狀態 本例暫監視WiFi網絡的變化
@property (nonatomic , strong) NSOperationQueue *networkOperationQueue;//保留隊列的操做,該隊列一次只容許執行一個操做
@property (nonatomic , strong) NSBlockOperation *operation;
@end

@implementation HPNewworkOps
-(id)init{
	if (self == [super init]){
		self.reachability = [Reachability reachabilityWithHostName:@"www.baidu.com"];
		self.reachability.reachableOnWWAN = NO;
		
		self.networkOperationQueue = [[NSOperationQueue alloc]init];
		self.networkOperationQueue.maxConcurrentOperationCount = 1;
		
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStatusChanged:) name:kReachabilityChangedNotification object:nil];
	}
	return self;
}

-(void)networkStatusChanged:(Reachability *)reachability{//根據網絡的可用狀況,通知的接受者掛起或恢復隊列
	if (reachability.isReachable != ReachableViaWiFi){
		self.networkOperationQueue.suspended = YES;
	}else{
		self.networkOperationQueue.suspended = NO;
	}
}
-(BOOL)isAPISeverReachable{
	return self.reachability.isReachable;
}

-(void)performNetWorkOperation:(NSDictionary *)param completion:(void(^)(NSError *,id)) completion{//老是將網絡操做送入隊列中
	
	self.operation = [NSBlockOperation blockOperationWithBlock:^{
		[self enqueueRequest:param completion:completion];//對網絡操做進行排隊
	}];
	[self.networkOperationQueue addOperation:self.operation];
}
-(void)enqueueRequest:(NSDictionary *)params completion:(void(^)(NSError *,id)) completion{

	AFHTTPSessionManager *op = [AFHTTPSessionManager manager];
	op.requestSerializer.timeoutInterval = 10;
	[op POST:@"www.baidu.com" parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
		
	} success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
		completion(nil,responseObject);
	} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
		completion(error,nil);
	}];
}
@end

一個掛起的隊列僅僅意味着後續操做在其恢復以前不會被執行,操做只有完成後纔會從隊列中移除。然而,爲了完成執行,必須先啓動操做。由於掛起的隊列不會啓動任何新的操做,因此它也不會移除任何正在排隊且未被執行的操做網絡

3.3定位管理器和GPS

使用GPS計算座標須要肯定兩點信息:數據結構

  • 時間鎖:每一個GPS衛星每毫秒廣播惟一一個1023位隨機數。由於數據傳播速率是1.024Mbit/s。GPS的接受芯片必須正確地與衛星的時間鎖槽對齊。
  • 頻率鎖:GPS接收器必須計算由接收器與衛星的相對運動致使的多普勒偏移帶來的信號偏差。
- (void)viewDidLoad {
	[super viewDidLoad];
	self.manager = [[CLLocationManager alloc]init];
	self.manager.delegate = self;
	self.manager.distanceFilter = kCLDistanceFilterNone;//觀察全部距離的變化
	self.manager.desiredAccuracy = kCLLocationAccuracyBest;//按照最大精度初始化管理器
	if (IOS8_OR_LATER_NEW){
		[self.manager requestAlwaysAuthorization];	//iOS8 特定API用於應用活動時申請使用定位服務
	}
	[self.manager stopUpdatingLocation];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{
	CLLocation *loc = [locations lastObject];
	//使用定位信息
}

3.3.1最佳的初始化

  • distanceFilter:只要設備的移動超過了最小距離,距離過濾器就會致使管理器對委託對象的locationManager: didUpdateLocations:事件通知發生變化。該距離使用公制單位(米)。
  • desiredAccuracy:精度參數的使用直接影響了使用天線的個數,進而影響了對電池的消耗。
    ◆ kCLLocationAccuracyBestForNavigation:用於導航的最佳精度級別
    ◆ kCLLocationAccuracyBest:設備可能達到的最佳精度級別
    ◆ kCLLocationAccuracyNearestTenMeters :精度接近10米
    ◆ kCLLocationAccuracyHundredMeters:精度接近100米
    ◆ kCLLocationAccuracyKilometer:精度在公里範圍
    ◆ kCLLocationAccuracyThreeKilometers:精度在三公里範圍
    距離過濾器只是軟件層面的過濾器,而精度級別會影響物理天線的使用

3.3.2關閉可有可無的特性

無需跟蹤位置變化時調用stopUpdatingLocation,向終端用戶提供關閉非必要功能的選項是個不錯的設計。併發

3.3.3只在必要時使用網絡

按期集中短暫的使用網絡,而不是持續的保持活動的數據流,才能更好的節省消耗。app

3.3.4後臺定位服務

應用進入後臺調用監聽(startUpdatingLocation也會喚起回調,startMonitoringSignificantLocationChanges在程序被殺掉時調用更好)

- (void)applicationDidEnterBackground:(UIApplication *)application {
	[self.manager startMonitoringSignificantLocationChanges];
//	[self.manager startUpdatingLocation];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
	[self.manager stopMonitoringSignificantLocationChanges];
}

後臺定位:http://adad184.com/2015/07/22/how-to-deal-with-background-location-update/

3.3.5NSTimer,NSThread和定位服務

當應用位於後臺時,任何定時器或線程都會掛起。但若是你在應用位於後臺狀態時申請了定位,那麼應用會在每次收到更新後被短暫喚醒。在此期間,線程和定時器都會被喚醒。

3.3.6在應用關閉後重啓

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
	if(launchOptions[UIApplicationLaunchOptionsLocationKey]){//被殺掉的APP 在後臺被系統喚醒時 launchOptions會包含UIApplicationLaunchOptionsLocationKey字段來進行標識
		[self.manager startMonitoringSignificantLocationChanges];//從新監聽
	}
}

3.4屏幕

3.4.1動畫

經過監聽UIApplicationWillResignActiveNotification或UIApplicationDidEnterBackgroundNotification的通知事件來暫停或中止動畫,也能夠經過監聽UIApplicationDidBecomeActiveNotification的通知事件來恢復動畫。

3.4.2視頻播放

[[UIApplication sharedApplication]setIdleTimerDisabled:YES];//保持屏幕常量

3.4.3多屏幕

官方使用外部屏幕demo:https://apple.co/1jauUnu

@interface HPMultiScreenViewController ()
@property (nonatomic , strong) UIWindow *secondWindow;
@end

@implementation HPMultiScreenViewController

- (void)viewDidLoad {
    [super viewDidLoad];
	[self registerNotifications];
}

-(void)viewDidAppear:(BOOL)animated{
	[super viewDidAppear:animated];
	[self updateScreens];
}

-(void)viewDidDisappear:(BOOL)animated{
	[super viewDidDisappear:animated];
	[self disconnectFromScreen];
}

-(void)disconnectFromScreen{
	if (self.secondWindow != nil){
		//斷開連接並準備釋放內存
		self.secondWindow.rootViewController = nil;
		self.secondWindow.hidden = YES;
		self.secondWindow = nil;
	}
}

-(void)updateScreens{
	NSArray *screens = [UIScreen screens];
	if (screens.count > 1){
		UIScreen *secondScreen = (UIScreen *)[screens objectAtIndex:1];
		CGRect rect = secondScreen.bounds;
		if (self.secondWindow == nil){
			self.secondWindow = [[UIWindow alloc]initWithFrame:rect];
			self.secondWindow.screen = secondScreen;
			
			HPScreen2ViewController *svc = [[HPScreen2ViewController alloc]init];
			//設置svc的其餘屬性以完整的對它初始化
//			svc.parent = self;
			
			self.secondWindow.rootViewController = svc;
		}
		self.secondWindow.hidden = NO;
	}else{
		[self disconnectFromScreen];
	}
	
}
-(void)dealloc{
	[self unregisterNotifications];
}

-(void)registerNotifications{
	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
	[nc addObserver:self selector:@selector(screenChanged:) name:UIScreenDidConnectNotification object:nil];
	[nc addObserver:self selector:@selector(screenChanged:) name:UIScreenDidDisconnectNotification object:nil];
}
-(void)screenChanged:(NSNotification *) notification{
	[self updateScreens];
}
-(void)unregisterNotifications{
	[[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end
  • viewDidLoad註冊UIScreenDidConnectNotification(屏幕鏈接)和UIScreenDidDisconnectNotification(屏幕斷開)通知
  • 每當有新的屏幕加入或有屏幕移除時,都會調用screenChanged:方法,在這裏更新UI
  • viewDidAppear在用戶進入視圖或離開視圖時,更新UI
  • viewDidDisappear當用離開時所在視圖控制器時,可能想在另外一個屏幕上更新UI。
  • 調用disconnectFromScreen方法將secondWindow從屏幕上移除
  • updateScreens檢查屏幕數量,若是大於1,將新的窗口與第二屏鏈接。

在屏幕之間交換UI

-(void)swapScreens:(UIWindow *)currentWindow newWindow:(UIWindow *)newWindow{
	NSArray *screens = [UIScreen screens];
	
	UIScreen *deviceScreen = [screens objectAtIndex:0];
	UIScreen *extScreen = [screens objectAtIndex:1];
	
	currentWindow.screen = extScreen;
	newWindow.screen = deviceScreen;
}

3.5電池電量與代碼感知

一個智能的應用會考慮電池的電量和自身的狀態,從而決定是否要真正的執行資源密集消耗性操做。

//使用電量級別和充電狀態進行條件處理
-(BOOL)shouldProceedWithMinLevel:(NSInteger)minLevel{
	UIDevice *device = [UIDevice currentDevice];
	device.batteryMonitoringEnabled = YES;
	
	UIDeviceBatteryState state = device.batteryState;
	if (state == UIDeviceBatteryStateCharging || state == UIDeviceBatteryStateFull){//在充電或電池已經充滿的狀態能夠進行操做
		return YES;
	}
	
	NSUInteger batteryLevel = (NSUInteger) (device.batteryLevel * 100);//按本身設定的電池範圍操做
	if (batteryLevel >= minLevel){
		return YES;
	}
	return NO;
}

對CPU的利用率:

-(float)appCPUUsage{

	kern_return_t kr;
	task_info_data_t info;
	mach_msg_type_number_t infoCount = TASK_INFO_MAX;
	
	kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)info, &infoCount);
	
	if (kr != KERN_SUCCESS){
		return -1;
	}
	
	thread_array_t thread_list;
	mach_msg_type_number_t thread_count;
	thread_info_data_t thinfo;
	mach_msg_type_number_t thread_info_count;
	thread_basic_info_t basic_info_th;
	
	kr = task_threads(mach_task_self(), &thread_list, &thread_count);
	if (kr != KERN_SUCCESS){
		return -1;
	}
	
	float tot_cpu = 0;
	int j;
	
	for (j = 0 ;j< thread_count ; j++){
		thread_info_count = THREAD_INFO_MAX;
		kr = thread_info(thread_list[j], THREAD_BASIC_INFO, (thread_info_t)thinfo,&thread_info_count);
		if (kr != KERN_SUCCESS){
			return -1;
		}
	
		basic_info_th =(thread_basic_info_t)thinfo;
		if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
			tot_cpu += basic_info_th -> cpu_usage / (float)TH_USAGE_SCALE * 100.0;
		}
	}
	
	vm_deallocate(mach_thread_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
	return tot_cpu;
}

3.6最佳實踐

確保對電量的謹慎使用:

  • 最小化硬件使用。
  • 在進行密集型任務前,檢查電池電量和充電狀態。
  • 在電量低時,提示用戶是否肯定要執行任務。
  • 或提供設置的選項,容許用戶定義電量的閾值,以便在執行密集型操做前提示用戶。
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	BOOL prompt = [defaults boolForKey:@"promptForBattery"];
	NSInteger minLevel = [defaults integerForKey:@"minBatterKevel"];
	
	BOOL canAutoProceed = [self shouldProceedWithMinLevel:minLevel];
	if (canAutoProceed){
		//執行密集操做
	}else{
		if (prompt){//低電量進行提示
		
		}else{
			
		}
	}
相關文章
相關標籤/搜索