会社のアプリでバックグラウンドタスクを実装することがあり、Androidのバックグラウンドタスク事情に関心を持ったためまとめたいと思います。
Androidのバックグラウンドタスク
上の動画では、バッテリーを消費しないことが良いアプリであると強く主張され、バッテリー消費を改善するためにどのようなことができるのかが紹介されています。
特にバックグラウンドのタスクはバッテリーを消費してしまう傾向にあり、いかに最適化できるかがバッテリーを消費しないアプリを作る上での課題でした。
例えば、めちゃくちゃ重たいデータをバックグラウンドでダウンロードしてくるとすると、大幅にバッテリーを消費してしまします。
JobScheduler
そこで、JobSchedulerという仕組みがAPI Level21から追加されました。
これを使うと、バックグラウンドでの処理をバッテリーの充電状態やネットワークとの接続状態から最適なタイミングで実行してくれます。
自前でバックグラウンド処理を実装して、充電されていない状態でバッテリーを食う処理が走ってしまった、、、なんてことを防いでくれます。
しかしこのJobScheduler、API Level21以上出ないと使えないため、Lollipop以下のヴァージョン(Kitkatなど)では使うことができません。
FirebaseJobDispatcher
そこで、FirebaseJobDispatcherというライブラリが発表されています。
これはライブラリという形でJobSchedulerの機能を使うことができます。API Levelも9以上であれば良いため、OSのヴァージョンをさほど意識しなくても済みます。
その代わり、Google Play Serviceのインストールが必要となります。
使い方
まずはbuild.gradleのdependencyに
compile 'com.firebase:firebase-jobdispatcher:0.7.0'
を記述します。
そして、READMEに書いてある通り、com.firebase.jobdispatcherのJobServiceを継承したサービスを実装します。
しかし、
http://qiita.com/chikara_funabashi/items/52f7f5bc07f9cf4e339c
の記事にあるように、JobServiceを継承したSimpleJobServiceというより使い勝手の良いクラスがあるので、そちらを使用してみます。
すると、
public class MyBackgroundService extends SimpleJobService {
public static final String TAG = "my_background_tag";
@Override
public int onRunJob(JobParameters jobParameters) {
if (jobParameters.getExtras() == null) {
return RESULT_FAIL_RETRY;
}
// 実行したい処理
return RESULT_SUCCESS;
}
}
onRunJobに実行したい処理を記述します。
また、AndroidManifestに以下の記述を追記します。
<service
android:exported="false"
android:name=".MyBackgroundService">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
</service>
そして、FirebaseJobDispatcherオブジェクトを生成します。
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
この時、GooglePlayDriverを渡してあげます。
このfispatcherを使ったシンプルなジョブの設定をしてみると
Job simpleJob = dispatcher.newJobBundle()
//実行したい処理を書いたSimpleJobServiceを継承したクラスをセット
.setService(MyBackgroundService.class)
//ジョブのタグをセット
.setTag(MyBackgroundService.TAG)
.build();
dispatcher.schedule(simpleJob);
これで、バックグラウンドで処理が動いてくれます。
しかし、JobDispatcherはもっと細かい設定もすることができます。
例えば、実行するサービスにパラメータを渡すことができます。
Bundleを生成して、ユニークなTAGと共に渡してあげたいパラメータをセットしてあげます。
Bundle jobParameter = new Bundle();
//parameterには渡してあげたいオブジェクトを
jobParameter.putParcelable(MyBackgroundService.TAG, parameter);
なお、
jobParameter.putInt(MyBackgroundService.TAG, parameterInt);
jobParameter.putString(MyBackgroundService.TAG, parameterString);
でも、渡すパラメータはBundleの仕様に則っていれば大丈夫です。
そして、
public class MyBackgroundService extends SimpleJobService {
public static final String TAG = "my_background_tag";
@Override
public int onRunJob(JobParameters jobParameters) {
if (jobParameters.getExtras() == null) {
return RESULT_FAIL_RETRY;
}
Hoge parameter = jobParameters.getExtras().getParcelable(TAG);
// parameter使って実行したい処理
return RESULT_SUCCESS;
}
}
onRunJobのjobParametersで受け取ることができます。parameterを使って処理を記述ができます。
また、
Job simpleJob = dispatcher.newJobBuilder()
.setService(MyBackgroundService.class)
.setTag(MyBackgroundService.TAG)
.setRecurring(false)
.setLifetime(Lifetime.UNTIL_NEXT_BOOT)
.setTrigger(Trigger.executionWindow(0, 60))
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setConstraints(
Constraint.ON_UNMETERED_NETWORK,
Constraint.DEVICE_CHARGING
)
.setExtras(myExtrasBundle)
.build();
のように、Jobをビルドするにあたって、色々と設定できます。
ピックアップすると、
setTrigger(Trigger.executionWindow(0, 60))
Trigger.executionWindowの第一引数と第二引数にsecondsを渡してあげると、その時間のどこかでジョブを実行してくれます。
例えば、この例だと今から60秒後のいずれかで、バッテリー、ネットワーク、他のアプリの実行状況を考慮して最適なタイミングで実行をしてくれます。
setConstraints(
Constraint.ON_UNMETERED_NETWORK,
Constraint.DEVICE_CHARGING
)
ここで、考慮したい状況を設定できます。
ON_UNMETERED_NETWORKはネットワーク上限ありの回線であるか否か(wifi接続か否か)
DEVICE_CHARGINGは充電中か否かです。
終わりに
以上、簡単なAndroidのバックグラウンドサービスの事情と、FirebaseJobDispatcherの使い方紹介でした。
参考
https://github.com/firebase/firebase-jobdispatcher-android
http://qiita.com/chikara_funabashi/items/52f7f5bc07f9cf4e339c
http://qiita.com/ushi3_jp/items/96f2b968ca009a7a1d56