標籤:view 進入 執行 好處 擷取 hand schema class 喚醒
著作權聲明:本文出自汪磊的部落格,轉載請務必註明出處。
一、WakeLock概述
wakelock是一種鎖的機制,只要有應用拿著這個鎖,CPU就無法進入休眠狀態,一直處於工作狀態。比如,手機螢幕在螢幕關閉的時候,有些應用依然可以喚醒工具提示使用者訊息,這裡就是用到了wakelock鎖機制,雖然手機螢幕關閉了,但是這些應用依然在運行著。手機耗電的問題,大部分是開發人員沒有正確使用這個鎖,成為"待機殺手"。
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分鐘,基本不會怎麼耗電。
二、WakeLock使用
擷取WakeLock執行個體代碼如下:
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。
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" />
注意:在使用該類的時候,必須保證acquire和release是成對出現的。
三、保持螢幕常亮
最好的方式是在Activity中使用FLAG_KEEP_SCREEN_ON的Flag。
1 public class MainActivity extends Activity {2 @Override3 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 + 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 @Override10 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,不要做電池殺手。
安卓電量最佳化之WakeLock鎖機制全面解析