當(dāng)Android設(shè)備空閑時(shí)呜叫,屏幕會(huì)變暗空繁,然后關(guān)閉屏幕,最后會(huì)停止CPU的運(yùn)行朱庆,這樣可以防止電池電量掉的快盛泡。在休眠過(guò)程中自定義的Timer、Handler娱颊、Thread傲诵、Service等都會(huì)暫停凯砍。但有些時(shí)候我們需要改變Android系統(tǒng)默認(rèn)的這種狀態(tài):比如玩游戲時(shí)我們需要保持屏幕常亮,比如一些下載操作不需要屏幕常亮但需要CPU一直運(yùn)行直到任務(wù)完成拴竹。
保持屏幕常亮
- 最好的方式是在Activity中使用FLAG_KEEP_SCREEN_ON 的Flag悟衩。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
這個(gè)方法的好處是不像喚醒鎖(wake locks),需要一些特定的權(quán)限(permission)栓拜。并且能正確管理不同app之間的切換座泳,不用擔(dān)心無(wú)用資源的釋放問(wèn)題。
- 另一個(gè)方式是在布局文件中使用android:keepScreenOn屬性:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
...
</RelativeLayout>
android:keepScreenOn = ” true “的作用和FLAG_KEEP_SCREEN_ON一樣幕与。使用代碼的好處是你允許你在需要的地方關(guān)閉屏幕挑势。
注意:一般不需要人為的去掉FLAG_KEEP_SCREEN_ON的flag,windowManager會(huì)管理好程序進(jìn)入后臺(tái)回到前臺(tái)的的操作啦鸣。如果確實(shí)需要手動(dòng)清掉常亮的flag潮饱,使用:
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
保持CPU運(yùn)行
需要使用PowerManager這個(gè)系統(tǒng)服務(wù)的喚醒鎖(wake locks)特征來(lái)保持CPU處于喚醒狀態(tài)。喚醒鎖允許程序控制宿主設(shè)備的電量狀態(tài)诫给。創(chuàng)建和持有喚醒鎖對(duì)電池的續(xù)航有較大的影響香拉,所以,除非是真的需要喚醒鎖完成盡可能短的時(shí)間在后臺(tái)完成的任務(wù)時(shí)才使用它蝙搔。比如在Acitivity中就沒(méi)必要用了缕溉。如果需要關(guān)閉屏幕,使用上述的FLAG_KEEP_SCREEN_ON吃型。
只有一種合理的使用場(chǎng)景证鸥,是在使用后臺(tái)服務(wù)在屏幕關(guān)閉情況下hold住CPU完成一些工作。 要使用喚醒鎖勤晚,如果不使用喚醒鎖來(lái)執(zhí)行后臺(tái)服務(wù)枉层,不能保證因CPU休眠未來(lái)的某個(gè)時(shí)刻任務(wù)會(huì)停止,這不是我們想要的赐写。 (有的人可能認(rèn)為我以前寫(xiě)的后臺(tái)服務(wù)就沒(méi)掉過(guò)鏈子呀運(yùn)行得挺好的鸟蜡,1.可能是你的任務(wù)時(shí)間比較短;2.可能CPU被手機(jī)里面很多其他的軟件一直在喚醒狀態(tài)挺邀。)揉忘。下面是很多網(wǎng)友有同樣的問(wèn)題:
喚醒鎖可劃分為并識(shí)別四種用戶喚醒鎖:
請(qǐng)注意,自 API 等級(jí) 17 開(kāi)始端铛,F(xiàn)ULL_WAKE_LOCK 將被棄用泣矛。應(yīng)用應(yīng)使用 FLAG_KEEP_SCREEN_ON。
-
第一步就是添加喚醒鎖權(quán)限:
<uses-permission android:name="android.permission.WAKE_LOCK" />
-
直接使用喚醒鎖:
PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE); WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag"); wakeLock.acquire();
注意:在使用該類的時(shí)候禾蚕,必須保證acquire和release是成對(duì)出現(xiàn)的您朽。
但推薦的方式是使用WakefulBroadcastReceiver:使用廣播和Service(典型的IntentService)結(jié)合的方式可以讓你很好地管理后臺(tái)服務(wù)的生命周期。
WakefulBroadcastReceiver是BroadcastReceiver的一種特例换淆。它會(huì)為你的APP創(chuàng)建和管理一個(gè)PARTIAL_WAKE_LOCK 類型的WakeLock哗总。WakefulBroadcastReceiver把工作交接給service(通常是IntentService)几颜,并保證交接過(guò)程中設(shè)備不會(huì)進(jìn)入休眠狀態(tài)。如果不持有WakeLock讯屈,設(shè)備很容易在任務(wù)未執(zhí)行完前休眠蛋哭。最終結(jié)果是你的應(yīng)用不知道會(huì)在什么時(shí)候能把工作完成,相信這不是你想要的涮母。
-
使用WakefulBroadcastReceiver第一步就是在Manifest中注冊(cè):
<receiver android:name=".MyWakefulReceiver"></receiver>
-
使用startWakefulService()方法來(lái)啟動(dòng)服務(wù)具壮,與startService()相比,在啟動(dòng)服務(wù)的同時(shí)哈蝇,并啟用了喚醒鎖。
public class MyWakefulReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // Start the service, keeping the device awake while the service is // launching. This is the Intent to deliver to the service. Intent service = new Intent(context, MyIntentService.class); startWakefulService(context, service); } }
-
當(dāng)后臺(tái)服務(wù)的任務(wù)完成攘已,要調(diào)用MyWakefulReceiver.completeWakefulIntent()來(lái)釋放喚醒鎖炮赦。
public class MyIntentService extends IntentService { public static final int NOTIFICATION_ID = 1; private NotificationManager mNotificationManager; NotificationCompat.Builder builder; public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { Bundle extras = intent.getExtras(); // Do the work that requires your app to keep the CPU running. // ... // Release the wake lock provided by the WakefulBroadcastReceiver. MyWakefulReceiver.completeWakefulIntent(intent); } }
采用定時(shí)重復(fù)的Service開(kāi)啟:
-
1、利用Android自帶的定時(shí)器AlarmManager實(shí)現(xiàn)
Intent intent = new Intent(mContext, ServiceTest.class); PendingIntent pi = PendingIntent.getService(mContext, 1, intent, 0); AlarmManager alarm = (AlarmManager)getSystemService(Service.ALARM_SERVICE); if(alarm != null) { alarm.cancel(pi); // 鬧鐘在系統(tǒng)睡眠狀態(tài)下會(huì)喚醒系統(tǒng)并執(zhí)行提示功能 alarm.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+1000, 2000, pi);// 確切的時(shí)間鬧鐘//alarm.setExact(…); //alarm.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pi); }
2样勃、該定時(shí)器可以啟動(dòng)Service服務(wù)吠勘、發(fā)送廣播、跳轉(zhuǎn)Activity峡眶,并且會(huì)在系統(tǒng)睡眠狀態(tài)下喚醒系統(tǒng)剧防。所以該方法不用獲取電源鎖和釋放電源鎖。
注意:在19以上版本辫樱,setRepeating中設(shè)置的頻繁只是建議值(6.0 的源碼中最小值是60s)峭拘,如果要精確一些的用setWindow或者setExact。
首先Android手機(jī)有兩個(gè)處理器狮暑,一個(gè)叫Application Processor(AP)鸡挠,一個(gè)叫Baseband Processor(BP)。AP是ARM架構(gòu)的處理器搬男,用于運(yùn)行Linux+Android系統(tǒng)拣展;BP用于運(yùn)行實(shí)時(shí)操作系統(tǒng)(RTOS),通訊協(xié)議棧運(yùn)行于BP的RTOS之上缔逛。非通話時(shí)間备埃,BP的能耗基本上在5mA左右,而AP只要處于非休眠狀態(tài)褐奴,能耗至少在50mA以上按脚,執(zhí)行圖形運(yùn)算時(shí)會(huì)更高。另外LCD工作時(shí)功耗在100mA左右歉糜,WIFI也在100mA左右乘寒。一般手機(jī)待機(jī)時(shí),AP匪补、LCD伞辛、WIFI均進(jìn)入休眠狀態(tài)烂翰,這時(shí)Android中應(yīng)用程序的代碼也會(huì)停止執(zhí)行。
Android為了確保應(yīng)用程序中關(guān)鍵代碼的正確執(zhí)行蚤氏,提供了Wake Lock的API甘耿,使得應(yīng)用程序有權(quán)限通過(guò)代碼阻止AP進(jìn)入休眠狀態(tài)。但如果不領(lǐng)會(huì)Android設(shè)計(jì)者的意圖而濫用Wake Lock API竿滨,為了自身程序在后臺(tái)的正常工作而長(zhǎng)時(shí)間阻止AP進(jìn)入休眠狀態(tài)佳恬,就會(huì)成為待機(jī)電池殺手。比如前段時(shí)間的某應(yīng)用于游,比如現(xiàn)在仍然干著這事的某應(yīng)用毁葱。
那么Wake Lock API有啥用呢?比如心跳包從請(qǐng)求到應(yīng)答贰剥,比如斷線重連重新登陸這些關(guān)鍵邏輯的執(zhí)行過(guò)程倾剿,就需要Wake Lock來(lái)保護(hù)。而一旦一個(gè)關(guān)鍵邏輯執(zhí)行成功蚌成,就應(yīng)該立即釋放掉Wake Lock了前痘。兩次心跳請(qǐng)求間隔5到10分鐘,基本不會(huì)怎么耗電担忧。除非網(wǎng)絡(luò)不穩(wěn)定芹缔,頻繁斷線重連,那種情況辦法不多瓶盛。
AlarmManager 是Android 系統(tǒng)封裝的用于管理 RTC 的模塊最欠,RTC (Real Time Clock) 是一個(gè)獨(dú)立的硬件時(shí)鐘,可以在 CPU 休眠時(shí)正常運(yùn)行蓬网,在預(yù)設(shè)的時(shí)間到達(dá)時(shí)窒所,通過(guò)中斷喚醒 CPU。(極光推送就是利用這個(gè)來(lái)做的帆锋。)
總結(jié):
- 關(guān)鍵邏輯的執(zhí)行過(guò)程吵取,就需要Wake Lock來(lái)保護(hù)。如斷線重連重新登陸
- 休眠的情況下如何喚醒來(lái)執(zhí)行任務(wù)锯厢?用AlarmManager皮官。如推送消息的獲取