いまさらWorkManager〜基本編①（必要最小限で実装：１回のみの実行）〜

# はじめに
この[記事](https://qiita.com/naoi/items/e08f081fe42ef3694046)で説明した通り、早速、WorkManagerの基本を学習していきます。
今回は、本当に基本的な内容を網羅しているだけです。複雑な技術については応用編で説明します。

# 動作環境
この記事の動作環境は以下のとおりです。

* Android Studio：3.3
* Kotln：1.3.11
* Open JDK:1.8
* compileSdkVersion：28
* targetSdkVersion：28
* minSdkVersion：19

# WorkManager

## 目標
今回は一番シンプルな使い方を理解します。最小限のクラス数を理解することによって、まずは取っ掛かりをつかみたいと思います。

### 完成イメージ
この記事では下図をゴールとします。
WorkerManagerのバックグラウンド処理で通知を表示するだけです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99960/a3710a59-b9af-69bd-42f8-0a364748ad60.gif" width=40%>


## 準備
### プロジェクトの作成
はじめに簡単なプロジェクトを作っていきます。
プロジェクトの基本的な構成は以下のとおりです。

* compileSdkVersion：28
* minSdkVersion：19
* targetSdkVersion：28
* AndroidX：1.0.0-rc02


## 実装 

### build.gradle
WorkManagerはAndroid Jetpackで追加されているため、アプリのbuild.gradleに下記を追記してライブラリを追加します。

```groovy:build.gradle
dependencies {
    def work_version = "1.0.0-rc02"
    // Kotlin + coroutines
    implementation "android.arch.work:work-runtime-ktx:$work_version"
}
```

### 実装イメージ
ソースコードや手順を説明する前に、これから実装するソースコードの全体像を図で確認しておきます。

![onetime_code_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99960/667e3de4-03c1-4e24-a96f-4708a4965f15.png)




ここでの登場するクラスは以下の3つです。

* Worker
* OneTimeWorkRequest
* WorkManager

しかし、その他に1つ抽象クラスがありそれを理解しなければなりません。

* WorkRequest

です。
クラスの詳細について確認していきます。

### WorkManagerのクラス群
実装イメージの図で出てきたクラスについて説明します。
各クラスの詳細は以下の通りです。

|パッケージ|クラス|役割|
|----|---|---|
|androidx.work|Worker|実際に定期的にバックグラウンドで処理する内容を定義したもの|
|androidx.work|WorkRequest|WorkManagerのキューに追加する処理の抽象クラス。<br>この抽象クラスを継承したクラスは、「OneTimeWorkRequest（1度だけの処理）」と「PeriodicWorkRequest（定期的に実行する処理）」の2つあります。|
|androidx.work|OneTimeWorkRequest|Workerクラス情報を保持し、1度だけバックグラウンドで処理するリクエストを表すクラス。<br>本記事は1回だけの実行方法を説明します。定期的に実行する方法は、別の記事で紹介します。|
|androidx.work|WorkManager|Work（処理）を保持するキューを扱うクラスです。Workの作業状況の監視やキャンセルなどが行えます。|

WorkRequestクラスの継承関係は下図のとおりです。

![WorkRequestの継承関係.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99960/1a8d96ce-f93f-5ffe-b379-e2333aef7a76.png)


### 手順
実装の手順を確認していきましょう。
手順は以下の通りです。

1. Workerクラスを継承し、doWorkメソッドをオーバーライドする
2. OneTimeWorkRequestオブジェクトを作成する
3. WorkManagerを取得する

### 実装内容
それでは、実際のコードを確認して、必要最小限にしたWorkManagerを確認していきます。

#### レイアウトリソースファイル
WorkManagerの説明に入る前に、画面イメージだけをおさえておきます。

##### 画面イメージ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99960/e82d39a1-41fd-c495-9888-f1e6fa56770b.png" width="50%">

##### XML
```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <Button
            android:id="@+id/buttonStart"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="WorkManagerスタート"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>
```
#### Workerクラス
説明するクラスの全体像を始めに確認します。
今回は、10回通知を表示させるため、Notification Channelの登録などの処理もはいっています。

```kotlin:MyWorker.kt
package jp.co.casareal.workmanagerbasicsample

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.support.v4.app.NotificationCompat
import androidx.work.Worker
import androidx.work.WorkerParameters

class MyWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {

    val notificationManager =
        applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    // カテゴリー名（通知設定画面に表示される情報）
    val name = "通知のタイトル的情報を設定"
    // システムに登録するChannelのID
    val id = "casareal_chanel"
    // 通知の詳細情報（通知設定画面に表示される情報）
    val notifyDescription = "この通知の詳細情報を設定します"


    init {
        // Channelの取得と生成
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationManager.getNotificationChannel(id) == null
            val mChannel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
            mChannel.apply {
                description = notifyDescription
            }
            notificationManager.createNotificationChannel(mChannel)
        }
    }

    override fun doWork(): Result {

        repeat(10) {

            val notification = NotificationCompat.Builder(applicationContext,id).apply {

                setContentText("${it}回目のメッセージ")
                setSmallIcon(R.drawable.ic_launcher_background)
            }

            notificationManager.notify(1, notification.build())

            Thread.sleep(1000)
        }
        return Result.success()
    }
}
```
定期的に実行したい処理はWorkerクラスを継承して実装します。

```kotlin:クラスの継承
class MyWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params){
}
```
WorkerクラスのコンストラクターでContextとWorkerParametersが必要になるので、MyWorkerクラスのコンストラクタで受け取るようにしています。WorkerParametersについては、別の記事で説明します。

```kotlin:doWorkのオーバーライド
    override fun doWork(): Result {
     ・・・省略・・・
        return Result.success()
    }
```

doWorkメソッドに実際の行いたい処理を記述していきます。
よく確認してみうるとResultという初めて見るクラスがありますね。
Resultくらすについて説明します。

|パッケージ|クラス|役割|
|----|---|---|
|androidx.work|ListenableWorker.Result|Worker#doWorkの処理結果を表すクラスです。<br>success（）、failure（）、またはretry（）、あるいはそれらから派生したメソッドやクラスを呼び出し、処理結果を表します。|

このクラスの使い所や詳細な動作は別の記事で説明します。

これでWorkerクラスは完成です。

#### WorkManagerとOneTimeWorkRequest.Builder
続いて、定義したWorkerクラスをWorkManagerのキューに追加する処理を確認していきます。

```kotlin:MainActivity.kt
package jp.co.casareal.workmanagerbasicsample

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val manager = WorkManager.getInstance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        buttonStart.setOnClickListener {
            val request = OneTimeWorkRequest.from(MyWorker::class.java)
            manager.enqueue(request)
        }
    }
}
```

はじめに、WorkManagerのオブジェクトを取得します。
WorkManagerはSingletonで定義されているため、getInstanceメソッドを呼び出す必要があります。

```kotlin:WorkManagerの取得
private val manager = WorkManager.getInstance()
```

続いて、WorkRequestを実装したオブジェクトを取得します。
本記事では繰り返しではなく1度きりの処理であるため、OneTimeWorkRequestクラスのオブジェクトを生成します。
OneTimeWorkRequestクラスのオブジェクトを生成するには、fromメソッドを呼び出します。
引数にWorkerを継承したクラスのクラス情報を渡します。

```kotlin:OneTimeWorkRequestの生成
val request = OneTimeWorkRequest.from(MyWorker::class.java)
```

最後に作成したWorkRequestをWorkManagerのキューに追加します。キューに追加するには、enqueueメソッドを呼び出します。
引数にはWorkRequestを継承したクラスのオブジェクトを渡します。
追加されたWorkはWorkManagerにより実行されます。
実行する際の条件などが設定されている場合は、条件に一致している際に実行します。
しかし、本記事では特に条件は指定していないため、ほぼ即時に実行されます。

```kotlin:WorkManagerのキューへ追加
manager.enqueue(request)
```

# まとめ
WorkManagerを必要最小限で使用する場合は、少ないクラスで利用できます。
しかし、このままではWorkManagerの良さが全く活かしきれていません。
WorkManagerにはまだまだ機能が沢山あります。それらを活用する前に最小限の実装からどのように実行されているかを理解すればさらなる機能を利用する際、理解がはやくなるのではないでしょうか。
今後は、更に活用する記事を書いていきます。