スリープとBroadcast
端末がスリープ状態の場合でもBroadcastReceiver.onReceive()の呼び出しは通常どおり行われる。
ただし、onReceive()からServiceを呼び出して処理させる場合、その処理が中断されてしまう可能性がある。
確実にバックグラウンド処理を行うためには、Wakelockによってスリープ状態を制御する必要がある。
この処理は通常、ServiceでWakelockを扱う場合、PowerManagerを使って以下の様に記述する。
<uses-permission android:name="android.permission.WAKE_LOCK" />
PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
Wakelock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK , "MyWakelockTag");
// wakeLock開始
wakeLock.acquire();
// 処理
// wakeLock解除
wakeLock.release();
これでも問題なく動くのだが、Androidではスリープ時にBroadcastを扱うための便利な仕組みが用意されている。
WakefulBroadcastReceiver
WakefulBroadcastReceiverはSupport Libraryに含まれるBroadcastReceiverのサブクラスである。
スリープ状態でServiceを扱うためのメソッドが二つ追加されている。
https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html
Manifest記述は通常のBroadcastReceiverと変わらない。
当然、Manifestへのuses-permission記述も必要となる。
<uses-permission android:name="android.permission.WAKE_LOCK" />
<receiver android:name=".MyBroadcastReceiver">
...
</receiver>
<service android:name=".MyService" />
サービスの起動にはstartServiceではなくstartWakefulServiceを使用する。
このメソッドは内部でstartServiceを呼んだあとWakelock.acquire()でスリープ制御を行っている。
public class MyBroadcastReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(context,MyService.class);
// サービス起動
startWakefulService(context,serviceIntent);
}
}
サービス側では処理の終了時に必ずWakefulBroadcastReceiver.completeWakefulIntent()を呼ぶ。
また、startWakefulService()ではIntent.putExtra()でwakelock制御IDをIntentに追記しているため、Service起動時のIntentを引数に入れること。
public class MyService extends IntentService {
public MyService() {
super("MyService");
}
@Override
protected void onHandleIntent(final Intent intent) {
try {
// サービス内部の処理
} finally {
// Wakelockの解除処理が必ず呼ばれるようにしておく
WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
}
}
まとめ
コード量を大幅に削減できてID管理とかも裏でやってくれるのは楽だ。
スリープ時にBroadcastを処理することはよくあるので、ぜひ覚えておきたい。