Android 保持設(shè)備喚醒

2018-08-02 18:24 更新

編寫(xiě):jdneo - 原文:http://developer.android.com/training/scheduling/wakelock.html

為了避免電量過(guò)度消耗,Android設(shè)備會(huì)在被閑置之后迅速進(jìn)入睡眠狀態(tài)。然而有時(shí)候應(yīng)用會(huì)需要喚醒屏幕或者是喚醒CPU并且保持它們的喚醒狀態(tài),直至一些任務(wù)被完成。

想要做到這一點(diǎn),所采取的方法依賴于應(yīng)用的具體需求。但是通常來(lái)說(shuō),我們應(yīng)該使用最輕量級(jí)的方法,減小其對(duì)系統(tǒng)資源的影響。在接下來(lái)的部分中,我們將會(huì)描述在設(shè)備默認(rèn)的睡眠行為與應(yīng)用的需求不相符合的情況下,我們應(yīng)該如何進(jìn)行對(duì)應(yīng)的處理。

保持屏幕常亮

某些應(yīng)用需要保持屏幕常亮,比如游戲與視頻應(yīng)用。最好的方式是在你的Activity中(且僅在Activity中,而不是在Service或其他應(yīng)用組件里)使用FLAG_KEEP_SCREEN_ON屬性,例如:

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);
  }

該方法的優(yōu)點(diǎn)與喚醒鎖(Wake Locks)不同(喚醒鎖的內(nèi)容在本章節(jié)后半部分),它不需要任何特殊的權(quán)限,系統(tǒng)會(huì)正確地 管理應(yīng)用之間的切換,且不必關(guān)心釋放資源的問(wèn)題。

另外一種方法是在應(yīng)用的XML布局文件里,使用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_SCRRE_ON等效。你可以選擇最適合你的應(yīng)用的方法。在Activity中通過(guò)代碼設(shè)置常亮標(biāo)識(shí)的優(yōu)點(diǎn)在于:你可以通過(guò)代碼動(dòng)態(tài)清除這個(gè)標(biāo)示,從而使屏幕可以關(guān)閉。

Notes:除非你不再希望正在運(yùn)行的應(yīng)用長(zhǎng)時(shí)間點(diǎn)亮屏幕(例如:在一定時(shí)間無(wú)操作發(fā)生后,你想要將屏幕關(guān)閉),否則你是不需要清除FLAG_KEEP_SCRRE_ON 標(biāo)識(shí)的。WindowManager會(huì)在應(yīng)用進(jìn)入后臺(tái)或者返回前臺(tái)時(shí),正確管理屏幕的點(diǎn)亮或者關(guān)閉。但是如果你想要顯式地清除這一標(biāo)識(shí),從而使得屏幕能夠關(guān)閉,可以使用getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)方法。

保持CPU運(yùn)行

如果你需要在設(shè)備睡眠之前,保持CPU運(yùn)行來(lái)完成一些工作,你可以使用PowerManager系統(tǒng)服務(wù)中的喚醒鎖功能。喚醒鎖允許應(yīng)用控制設(shè)備的電源狀態(tài)。

創(chuàng)建和保持喚醒鎖會(huì)對(duì)設(shè)備的電源壽命產(chǎn)生巨大影響。因此你應(yīng)該僅在你確實(shí)需要時(shí)使用喚醒鎖,且使用的時(shí)間應(yīng)該越短越好。如果想要在Activity中使用喚醒鎖就顯得沒(méi)有必要了。如上所述,可以在Activity中使用FLAG_KEEP_SCRRE_ON讓屏幕保持常亮。

使用喚醒鎖的一種合理情況可能是:一個(gè)后臺(tái)服務(wù)需要在屏幕關(guān)閉時(shí)利用喚醒鎖保持CPU運(yùn)行。再次強(qiáng)調(diào),應(yīng)該盡可能規(guī)避使用該方法,因?yàn)樗鼤?huì)影響到電池壽命。

不必使用喚醒鎖的情況

  1. 如果你的應(yīng)用正在執(zhí)行一個(gè)HTTP長(zhǎng)連接的下載任務(wù),可以考慮使用DownloadManager。
  2. 如果你的應(yīng)用正在從一個(gè)外部服務(wù)器同步數(shù)據(jù),可以考慮創(chuàng)建一個(gè)SyncAdapter
  3. 如果你的應(yīng)用需要依賴于某些后臺(tái)服務(wù),可以考慮使用RepeatingAlarm或者Google Cloud Messaging,以此每隔特定的時(shí)間,將這些服務(wù)激活。

為了使用喚醒鎖,首先需要在應(yīng)用的Manifest清單文件中增加WAKE_LOCK權(quán)限:

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

如果你的應(yīng)用包含一個(gè)BroadcastReceiver并使用Service來(lái)完成一些工作,你可以通過(guò)WakefulBroadcastReceiver管理你喚醒鎖。后續(xù)章節(jié)中將會(huì)提到,這是一種推薦的方法。如果你的應(yīng)用不滿足上述情況,可以使用下面的方法直接設(shè)置喚醒鎖:

PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
Wakelock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
        "MyWakelockTag");
wakeLock.acquire();

可以調(diào)用wakelock.release()來(lái)釋放喚醒鎖。當(dāng)應(yīng)用使用完畢時(shí),應(yīng)該釋放該喚醒鎖,以避免電量過(guò)度消耗。

使用WakefulBroadcastReceiver

你可以將BroadcastReceiver和Service結(jié)合使用,以此來(lái)管理后臺(tái)任務(wù)的生命周期。WakefulBroadcastReceiver是一種特殊的BroadcastReceiver,它專注于創(chuàng)建和管理應(yīng)用的PARTIAL_WAKE_LOCK。WakefulBroadcastReceiver會(huì)將任務(wù)交付給Service(一般會(huì)是一個(gè)IntentService),同時(shí)確保設(shè)備在此過(guò)程中不會(huì)進(jìn)入睡眠狀態(tài)。如果在該過(guò)程當(dāng)中沒(méi)有保持住喚醒鎖,那么還沒(méi)等任務(wù)完成,設(shè)備就有可能進(jìn)入睡眠狀態(tài)了。其結(jié)果就是:應(yīng)用可能會(huì)在未來(lái)的某一個(gè)時(shí)間節(jié)點(diǎn)才把任務(wù)完成,這顯然不是你所期望的。

要使用WakefulBroadcastReceiver,首先在Manifest文件添加一個(gè)標(biāo)簽:

<receiver android:name=".MyWakefulReceiver"></receiver>

下面的代碼通過(guò)startWakefulService()啟動(dòng)MyIntentService。該方法和startService()類似,除了WakeflBroadcastReceiver會(huì)在Service啟動(dòng)后將喚醒鎖保持住。傳遞給startWakefulService()的Intent會(huì)攜帶有一個(gè)Extra數(shù)據(jù),用來(lái)標(biāo)識(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)Service結(jié)束之后,它會(huì)調(diào)用MyWakefulReceiver.completeWakefulIntent()來(lái)釋放喚醒鎖。completeWakefulIntent()方法中的Intent參數(shù)是和WakefulBroadcastReceiver傳遞進(jìn)來(lái)的Intent參數(shù)一致的:

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);
    }
}


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)