安卓電量優化之WakeLock鎖機制全面解析

版權聲明:本文出自汪磊的博客,轉載請務必註明出處。html

1、WakeLock概述java

wakelock是一種鎖的機制,只要有應用拿着這個鎖,CPU就沒法進入休眠狀態,一直處於工做狀態。好比,手機屏幕在屏幕關閉的時候,有些應用依然能夠喚醒屏幕提示用戶消息,這裏就是用到了wakelock鎖機制,雖然手機屏幕關閉了,可是這些應用依然在運行着。手機耗電的問題,大部分是開發人員沒有正確使用這個鎖,成爲"待機殺手"。android

Android手機有兩個處理器,一個叫Application Processor(AP),一個叫Baseband Processor(BP)。AP是ARM架構的處理器,用於運行Linux+Android系統;BP用於運行實時操做系統(RTOS),通信協議棧運行於BP的RTOS之上。非通話時間,BP的能耗基本上在5mA左右,而AP只要處於非休眠狀態,能耗至少在50mA以上,執行圖形運算時會更高。另外LCD工做時功耗在100mA左右,WIFI也在100mA左右。通常手機待機時,AP、LCD、WIFI均進入休眠狀態,這時Android中應用程序的代碼也會中止執行。微信

Android爲了確保應用程序中關鍵代碼的正確執行,提供了Wake Lock的API,使得應用程序有權限經過代碼阻止AP進入休眠狀態。但若是不領會Android設計者的意圖而濫用Wake Lock API,爲了自身程序在後臺的正常工做而長時間阻止AP進入休眠狀態,就會成爲待機電池殺手。網絡

那麼Wake Lock API具體有啥用呢?心跳包從請求到應答,斷線重連從新登錄等關鍵邏輯的執行過程,就須要Wake Lock來保護。而一旦一個關鍵邏輯執行成功,就應該當即釋放掉Wake Lock了。兩次心跳請求間隔5到10分鐘,基本不會怎麼耗電。架構

2、WakeLock使用app

獲取WakeLock實例代碼以下:ide

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);  
WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag");

newWakeLock(int levelAndFlags, String tag)中PowerManager.PARTIIAL_WAKE_LOCK是一個標誌位,標誌位是用來控制獲取的WakeLock對象的類型,主要控制CPU工做時屏幕是否須要亮着以及鍵盤燈須要亮着,標誌位說明以下:佈局

levelAndFlags CPU是否運行 屏幕是否亮着 鍵盤燈是否亮着
PARTIAL_WAKE_LOCK
SCREEN_DIM_WAKE_LOCK 低亮度
SCREEN_BRIGHT_WAKE_LOCK 高亮度
FULL_WAKE_LOCK

特殊說明:自API等級17開始,FULL_WAKE_LOCK將被棄用。應用應使用FLAG_KEEP_SCREEN_ONpost

WakeLock類能夠用來控制設備的工做狀態。使用該類中的acquire可使CPU一直處於工做的狀態,若是不須要使CPU處於工做狀態就調用release來關閉。

(1)、自動release

若是咱們調用的是acquire(long timeout)那麼就無需咱們本身手動調用release()來釋放鎖,系統會幫助咱們在timeout時間後釋放。

(2)、手動release

若是咱們調用的是acquire()那麼就須要咱們本身手動調用release()來釋放鎖。

最後使用WakeLock類記得加上以下權限:

1 <uses-permission android:name="android.permission.WAKE_LOCK" />   

注意:在使用該類的時候,必須保證acquirerelease是成對出現的。

3、保持屏幕常亮

最好的方式是在Activity中使用FLAG_KEEP_SCREEN_ON的Flag。

1 public class MainActivity extends Activity { 2  @Override 3     protected void onCreate(Bundle savedInstanceState) { 4         super.onCreate(savedInstanceState); 5  setContentView(R.layout.activity_main); 6  getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 7  } 8 }

這個方法的好處是不像喚醒鎖(wake locks),須要一些特定的權限(permission)。而且能正確管理不一樣app之間的切換,不用擔憂無用資源的釋放問題。 
另外一個方式是在佈局文件中使用android:keepScreenOn屬性:

1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 
3     android:layout_width="match_parent"
4     android:layout_height="match_parent"
5     android:keepScreenOn="true">
6  ... 7 </RelativeLayout>

android:keepScreenOn = 」true「的做用和FLAG_KEEP_SCREEN_ON同樣。使用代碼的好處是你容許你在須要的地方關閉屏幕。

注意:通常不須要人爲的去掉FLAG_KEEP_SCREEN_ON的flag,windowManager會管理好程序進入後臺回到前臺的的操做。若是確實須要手動清掉常亮的flag,使用getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

4、WakefulBroadcastReceiver + IntentService實例

IntentService使用請參照我以前博客Android IntentService使用介紹以及源碼解析

WakefulBroadcastReceiver是BroadcastReceiver的一種特例。它會爲你的APP建立和管理一個PARTIAL_WAKE_LOCK類型的WakeLock。WakefulBroadcastReceiver把工做交接給service(一般是IntentService),並保證交接過程當中設備不會進入休眠狀態。若是不持有WakeLock,設備很容易在任務未執行完前休眠。最終結果是你的應用不知道會在何時能把工做完成,相信這不是你想要的。

使用startWakefulService()方法來啓動服務,與startService()相比,在啓動服務的同時,並啓用了喚醒鎖。

當後臺服務的任務完成,要調用WLWakefulReceiver.completeWakefulIntent()來釋放喚醒鎖。

WLWakefulReceiver類以下:

 1 public class WLWakefulReceiver extends WakefulBroadcastReceiver {  2 
 3     private static final String TAG = "myTag";  4 
 5  @Override  6     public void onReceive(Context context, Intent intent) {  7         //  8         String extra = intent.getStringExtra("msg");  9         Log.i(TAG, "onReceive:"+extra); 10         Intent serviceIntent = new Intent(context, MyIntentService.class); 11         serviceIntent.putExtra("msg", extra); 12  startWakefulService(context, serviceIntent); 13  } 14 }

很簡單,就是打印一下信息以及調用startWakefulService方法來啓動服務。

MyIntentService類以下:

 1 public class MyIntentService extends IntentService {  2 
 3     private static final String TAG = "myTag";  4     
 5     public MyIntentService() {  6         super("MyIntentService");  7  }  8 
 9  @Override 10     protected void onHandleIntent(Intent intent) { 11         //子線程中執行
12         Log.i(TAG, "onHandleIntent"); 13         for (int i = 0; i < 10; i++) { 14             try { 15                 Thread.sleep(3000); 16                 String extra = intent.getStringExtra("msg"); 17                 Log.i(TAG, "onHandleIntent:"+extra); 18             } catch (InterruptedException e) { 19  e.printStackTrace(); 20  } 21  } 22         //調用completeWakefulIntent來釋放喚醒鎖。
23  WLWakefulReceiver.completeWakefulIntent(intent); 24  } 25 }

一樣很簡單,也是打印信息進行耗時操做,可是執行完本身業務邏輯後一點記得調用completeWakefulIntent來釋放喚醒鎖。

最後就是啓動廣播接收者以及加入權限和聲明瞭:

Intent intent = new Intent("WANG_LEI"); intent.putExtra("msg", "學習WAKE_LOCK。。。"); sendBroadcast(intent); <uses-permission android:name="android.permission.WAKE_LOCK" />

<receiver android:name=".WLWakefulReceiver" >
            <intent-filter>
                <action android:name="WANG_LEI" />
            </intent-filter>
</receiver>
<service android:name=".MyIntentService"></service>

好了,編寫好程序運行發現及時按電源鍵屏幕關閉依然有LOG打印出。

本篇到此結束,wakelock鎖主要是相對系統的休眠而言的,意思就是個人程序給CPU加了這個鎖那系統就不會休眠了,這樣作的目的是爲了全力配合咱們程序的運行。有的狀況若是不這麼作就會出現一些問題,好比微信等及時通信的心跳包會在熄屏不久後中止網絡訪問等問題。因此微信裏面是有大量使用到了wake_lock鎖。但願通過上述共同窗習你能正確使用WakeLock,不要作電池殺手。

聲明:文章將會陸續搬遷到我的公衆號,之後文章也會第一時間發佈到我的公衆號,及時獲取文章內容請關注公衆號

相關文章
相關標籤/搜索