Android Oreo対応 - バックグラウンドジョブ編

## 背景
Android Oreoのリリースにより、従来バックグラウンド上常に動くServiceは動かなくなるため、対応しました。以下はその対応の記載です。

Android Oreo Background Limit

https://developer.android.com/about/versions/oreo/background?hl=ja

## 対応策
Serviceはそのまま使えないので、Androidドキュメントに勧められたJobSchedulerを使って、実装しようと思います。

選択肢として以下のライブラリがあります。

![accaa1e9-daee-fb4a-d74f-f1dff959bb1f.png](https://qiita-image-store.s3.amazonaws.com/0/37718/c8cac0c3-ab24-2131-3d65-0ba417c58cc0.png)

ちなみに、[android-job](https://github.com/evernote/android-job)というevernoteが作ったライブラリもありますが、WorkManagerは同じ実装をしたようです。

その中でできればAPIレベル低い、かつ柔軟に対応してくれるのは基準としてWorkManagerを選びました。

### メリット
- API 14+
- No require Google Play Service
- Googleが出している
- 端末のOSバージョンとアプリのtargetSdkVersionにより、柔軟に対応してくれる

### デメリット
- WorkManagerはまだalpha版
  - https://developer.android.com/jetpack/docs/release-notes

## 実装
- Workerを実装

```java
public class MyWorker extends Worker {
    public static final String TAG = MyWorker.class.getSimpleName();

    @NonNull
    @Override
    public Result doWork() {
        LogUtil.debug(TAG, "Start My Worker");

        try {
            MYTracking myTracking = MyTracking.getInstance(getApplicationContext());
            myTracking.start();
        }
        catch (Exception e) {
            e.printStackTrace();
            // TODO: deal with exception
            return Result.RETRY;
        }
        return Result.SUCCESS;
    }
}
```

- Trackingクラスを実装

```java
public class MyTracking {

    private MyTracking() {

    }

    public static MyTracking getInstance(Context _context) {
        context = _context;

        return instance;
    }

    public void start() {
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            LogUtil.e(TAG, "Need ACCESS_FINE_LOCATION permission to start MyWorker!");
            return;
        }

        // 制約を設定する
        Criteria criteria = new Criteria();
        criteria.setSpeedRequired(false);
        criteria.setAltitudeRequired(false);
        criteria.setBearingRequired(false);
        criteria.setCostAllowed(true);

        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        String bestProvider = locationManager.getBestProvider(criteria, true);
        LogUtil.debug(TAG, "The best provider is: " + bestProvider);

        Looper looper = Looper.getMainLooper();
        MyListener myListener = new MyListener(context);

        locationManager.requestSingleUpdate(bestProvider, myListener, looper);
    }
}
```

- Mainのcreateメソッドの中からWorkerを呼び出す

```java
private void startTracking() {
        Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
        PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(MyWorker.class, REPEAT_INTERVAL, TimeUnit.MINUTES).setConstraints(constraints).addTag(MyWorker.TAG).build();
        // すでにMyWorkerが存在している場合は、入れ替えます。
        WorkManager.getInstance().enqueueUniquePeriodicWork("MyWorker", ExistingPeriodicWorkPolicy.REPLACE, request);
    }
```

## 感想
WorkManagerを使ってバックグラウンドジョブの実装は大分楽になりました。

## 参照
- https://developer.android.com/about/versions/oreo/background?hl=ja
- https://developer.android.com/jetpack/docs/release-notes
- https://developer.android.com/jetpack/