【Android】Firebase JobDispatcherの使い方


　6.0以降からよりDozeモードや消費電力関連の進捗があり、ネットワークの状態や充電中かどうか、などなど、より気にする必要がでてきました。
　そこでAndroid API 21から、JobSchedulerが登場し、またPlayServiceにGcmNetworkManagerが登場し、ネットワークや充電状態をみて適切なタイミングでバックグランドタスクを実行する仕組みが追加されました。

　I/OでもDozeモードと一緒にJobScheduler、JobDispatcherが説明されてましたね。
　https://www.youtube.com/watch?v=VC2Hlb22mZM

# 事前知識
## JobSchedulerとは (API 21から追加)
　https://developer.android.com/reference/android/app/job/JobInfo.Builder.html
　Android API 21から追加されました。
　ネットワークや充電状態、端末のスリープ状況をみて適切なタイミングでバックグランドタスクを実行してくれる仕組みです。簡単に例を示すと、JobServiceを継承したサービスをつくりそこでバックグランドなタスクを実行します。

```java
public class MyJobService extends JobService {

    @Override
    public boolean onStartJob(JobParameters params) {
        // 適切なタイミングで呼ばれるので、重たい処理をバックグランドで実行し、
        // 終了したらjobFinishedを呼ぶ
        new Thread(new Runnable() {
            @Override
            public void run() {
                    jobFinished(mParams, false);
            }
        }).start();
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        jobFinished(params, false);
        return false;
    }
}
```

　ネットワークのタイプや充電中かなどの条件を登録し、タスクを実行するサービスを指定して登録を行うとOS側で適切なタイミングでタスクの実行が行えます。

```java
JobScheduler jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo jobInfo = new JobInfo.Builder(jobId, new ComponentName(this, TestJobService.class))
            .setMinimumLatency(5 * 1000)
            .setOverrideDeadline(50 * 1000)
            .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
            .setRequiresDeviceIdle(true)
            .setRequiresCharging(false)
            .build();
jobScheduler.schedule(jobInfo);
```

## GcmNetworkManager (API 21以下で使用可能)
　JobSchedulerは残念なことに、Android API 21からです。サポートライブラリにも含まれません。

　代わりにPlayServiceの方に、GcmNetworkManagerが追加されました。これでJobSchedulerと同じようなことができます。

https://developers.google.com/android/reference/com/google/android/gms/gcm/GcmNetworkManager

# 本題
## Firebase JobDispatcherとは
　Google IOでもJobSchedulerとFirebaseと一緒に紹介されました。
　簡単に言うとGCM Network Managerのオープンソースなライブラリ形式の置き換え版です。
以下からcloneして取り込めばライブラリとして利用できます。
　ほぼほぼGcmNetworkManagerやJobSchedulerと同じです。
　https://github.com/firebase/firebase-jobdispatcher-android

## 導入
　リポジトリのREADMEに書いてある通りに。

まずはレポジトリをclone。

```
git clone https://github.com/firebase/firebase-jobdispatcher-android
cd firebase-jobdispatcher-android
```

aarにバンドルします。

```
./gradlew aar
```

できたjobdispatcher-release.aarをAndroid Studioのプロジェクトに取り込みます。(aarの取り込み方はまた別途説明しようかな。)

```
cp jobdispatcher/build/outputs/aar/jobdispatcher-release.aar
```
## 実装 
com.firebase.jobdispatcherのJobServiceを継承したサービスを実装します。

JobServiceを継承したSimpleJobServiceというクラスも準備されてたのでこれを継承したほうが楽かもしれないです!

onRunJob()に、バックグランドの処理をそのまま書けば良い感じです。
AsyncTaskのなかで呼ばれてたので、そのまま重い処理を書けば良い。

```java
public class MyJobService extends SimpleJobService {

    protected static final String TAG = "MyJobService";

    @Override
    public int onRunJob(JobParameters jobParameters) {

        Log.d(TAG, "onRunJob start "+jobParameters.getTag()+", "+jobParameters);
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "onRunJob stop "+jobParameters);

        return RESULT_SUCCESS;
    }
}

```

Manifestに宣言も忘れずに

```xml
<service android:name=".MyJobService" android:exported="false">
  <intent-filter>
    <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
  </intent-filter>
</service>
```

あとは、充電中か否か、定額制のネットワークか否か(Constraint.ON_ANY_NETWORK)などの条件を設定してタスクを登録すれば、適切なタイミングで実行してくれます

```java
for (int i=0; i<10; i++){

    Log.d(TAG, "schedule start "+i);

    Driver myDriver = new GooglePlayDriver(this);
    FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(myDriver);

    Bundle args = new Bundle();
    args.putLong("n", i);

    Job job = dispatcher.newJobBuilder()
            .setService(MyJobService.class)
            .setTag("push-"+i)
            .setConstraints(Constraint.ON_ANY_NETWORK)
            .setTrigger(Trigger.NOW)
            .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
            .setRecurring(false)
            .setExtras(args)
            .build();

    int result = dispatcher.schedule(job);
    if (result != FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS) {
        // handle error
        Log.d(TAG, "schedule error.");
    }
}
```

| 定数 | 説明 |
|:-:|:-:|
| ON_UNMETERED_NETWORK | Only run the job when an unmetered network is available. |
| ON_ANY_NETWORK |  Only run the job when a network connection is available. If both this and ON_ANY_NETWORK is provided, ON_ANY_NETWORK will take precedence. |
| DEVICE_CHARGING  | Only run the job when a network connection is available. If both this and ON_ANY_NETWORK is provided, ON_ANY_NETWORK will take precedence.  |

## なぜGCM Network ManagerではなくFirebase JobDispatcherを使うの(・・?
JobSchedulerは21からなのでまだ使えないので置いとくとして、GCM Network Managerを使えばいいのではと思うかもしれないですね。

単に個人的にだけど、GCMの実装をFCMに取り替えたときに、もうGCMの実装はいらなくなったので
com.google.android.gms:play-services-gcm:9.0.2
を消し去りました。GCM Network Managerは上記に含まれるのでこれを消すと使えない。
だけどGCMの処理は置き換えて使ってないので、とりたい！ということでJobDispatcherを使うことに

# まとめ
ほぼJobSchedulerやGCMNetworkManagerと同じ感じです!
比較的簡単に実装できました!
ライブラリなので、OSのバージョンも気にせず、PlayServiceと違ってオープンソースで中身を見れるので、動作を追えていい感じですね。

# 感想
うう。。。といいつつも、まだ怠けてIntentServiceに書いてればいいやと思ってしまう今日此の頃。
快適で、確実に動くアプリをつくるために頑張ろ（＾ｖ＾）