版權聲明:本文出自汪磊的博客,轉載請務必註明出處。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_ON。post
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" />
最好的方式是在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)
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,不要作電池殺手。
聲明:文章將會陸續搬遷到我的公衆號,之後文章也會第一時間發佈到我的公衆號,及時獲取文章內容請關注公衆號