8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

いまさらWorkManager〜基本編⑥(唯一無二のWorker)〜

Posted at

はじめに

この記事で説明した通り、様々な機能を活用していきましょう。
WorkRequestをキューに追加したにもかかわらず、処理によってはもう一度キューに今一度追加してしまい、思わぬ不具合につながる事もあります。
アプリではキューに1つだけにしたい!!ってこともあります。
その様な要望にも答えられるようにWorkManagerは機能を提供しています。
今回はキューに追加するWorkRequestをユニークにする方法を説明します。

動作環境

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

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

目標

キューに格納されているWorkRequestは1つだけにする!

完成イメージ

今回は、定期実行と複数回実行の2つを用意して、実行しています。
見た目ではこれまでと変わらないので、あまり面白味はないと思います。

概要

詳細な説明の前に、キューないでユニークにする方法の概要を理解しておきましょう。
キューないでユニークにするには以下の内容を理解する必要があります。

  • キューないでユニークにするメソッドによるWorkRequestの追加
  • 追加するWorkRequestに名前をつける
  • すでに同じ名前のWorkRequestが存在する場合の挙動の設定

図にしてみました。

ユニークなWork.png

説明

ユニークなWorkRequestの追加

キューないでユニークにするためには以下のメソッドを呼び出して追加します。

  • enqueueUniqueWork
  • enqueueUniquePeriodicWork

WorkRequestの種類によって呼び出すメソッドが異なります。
1回のみの実行であれば、 enqueueUniqueWork 。繰り返し実行する場合は、 enqueueUniquePeriodicWork です。

それではそれぞれのメソッドの詳細を確認してみましょう。

enqueueUniqueWork

メソッド名 引数
enqueueUniqueWork String uniqueWorkName,
ExistingWorkPolicy existingWorkPolicy,
OneTimeWorkRequest work
enqueueUniqueWork String uniqueWorkName,
ExistingWorkPolicy existingWorkPolicy,
List<OneTimeWorkRequest> work

メソッドが2つありますが、第③引数のWorkRequestが単一か複数の違いでしかありません。
それでも、引数の詳細を確認しましょう。

引数 説明
第1引数 String WorkRequestをユニークにするための名前
第2引数 ExistingWorkPolicy 同じ名前でキューに追加されたときの挙動
第3引数 OneTimeWorkRequest or <ListOneTimeWorkRequest> WorkRequestのオブジェクト

今回のポイントは第1引数と第2引数にありそうです。

第1引数と第2引数の説明の前に、実装例を確認してみましょう。

val request = OneTimeWorkRequest.Builder(MyWorker::class.java)
    .apply {
        ・・・省略・・・
    }.build()

manager.enqueueUniqueWork(onetimeWorkName, ExistingWorkPolicy.KEEP, request)

enqueueUniquePeriodicWorkメソッドも確認しておきます。
enqueueUniquePeriodicWork

メソッド名 引数
enqueueUniquePeriodicWork String uniqueWorkName,
ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy,
PeriodicWorkRequest periodicWork

enqueueUniqueWorkメソッドとよく似ています。

引数 説明
第1引数 String WorkRequestをユニークにするための名前
第2引数 ExistingPeriodicWorkPolicy 同じ名前でキューに追加されたときの挙動
第3引数 PeriodicWorkRequest WorkRequestのオブジェクト

いろいろ気になる事もあると思いますが、実装例も確認してみましょう。

val request = PeriodicWorkRequest.Builder(MyWorker::class.java, 20, TimeUnit.MINUTES)
    .apply {
       ・・・省略・・・
        }.build()
        setInputData(data)
    }.build()

manager.enqueueUniquePeriodicWork(periodicWorkName, ExistingPeriodicWorkPolicy.REPLACE, request)

enqueueUniqueWorkメソッドと比べてみると第2引数の型が明らかに違いますね。

第2引数はどちらもEnum型です。

それぞれの持てる値と説明を並べてみます。

ExistingWorkPolicy

説明
APPEND 同じ名前で追加されたWorkRequestの状態が完了状態でない場合は、追加されたWorkRequestをペンディング中のWorkRequestの子供として追加し、順次実行されます。
KEEP 同じ名前で追加されたWorkRequestの状態が完了状態でない場合は、追加されません。
REPLACE 同じ名前で追加されたWorkRequestの状態が完了状態でない場合は、現在キューに入っているWorkRequestをキャンセル後削除し、追加されたWorkRequestをキューに追加します。

ExistingPeriodicWorkPolicy

説明
KEEP 同じ名前で追加されたWorkRequestの状態が完了状態でない場合は、追加されません。
REPLACE 同じ名前で追加されたWorkRequestの状態が完了状態でない場合は、現在キューに入っているWorkRequestをキャンセル後削除し、追加されたWorkRequestをキューに追加します。

繰り返し実行の場合、APPENDは存在しません。

最後に、第1引数ですが、これはIDやタグとは別物になり、キューに追加されるWorkRequestを表す値です。そのため、IDやタグを指定した状態でも、関係なくユニークになるような文字列を指定します。

実装コード

この記事で動作確認したコードをすべて掲載します。
AndroidManifest.xmlはテンプレートで作成された状態から変更していないため、省略します。

build.gradle
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "jp.co.casareal.workmanageruniquework"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    def work_version = "1.0.0"

    implementation "android.arch.work:work-runtime-ktx:$work_version"

}
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">
    <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textPersonName"
            android:text="Casareal"
            android:ems="10"
            android:id="@+id/editInput"
            app:layout_constraintTop_toTopOf="parent" android:layout_marginEnd="8dp"
            app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="8dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/btnStart"/>
    <Button
            android:text="WorkStart"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btnStart"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
            app:layout_constraintTop_toBottomOf="@+id/editInput"
            android:layout_marginBottom="291dp" app:layout_constraintBottom_toTopOf="@+id/btnOnetimeStart"/>
    <Button
            android:text="oneTimeButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btnOnetimeStart" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
            app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="8dp"
            app:layout_constraintTop_toBottomOf="@+id/btnStart"
            android:layout_marginBottom="32dp" app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginTop="16dp"/>
</android.support.constraint.ConstraintLayout>
MyWorker.kt
package jp.co.casareal.workmanageruniquework

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
import java.text.SimpleDateFormat

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

    private val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

    companion object {
        var nid = 1
        var count = 1
    }


    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 {


        val notification = NotificationCompat.Builder(applicationContext, id).apply {
            setContentText("受け取ったメッセージ:${inputData.getString("message")},${count}回目のメッセージ")
            setSmallIcon(R.drawable.ic_launcher_background)
        }

        notificationManager.notify(MyWorker.nid, notification.build())

        return Result.success()
    }
}
MainActivity.kt
package jp.co.casareal.workmanageruniquework

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.work.*
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

    private val manager = WorkManager.getInstance()
    private val periodicWorkName = "periodicmywork"
    private val onetimeWorkName = "onetimemywork"


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


        btnOnetimeStart.setOnClickListener {
            val request = OneTimeWorkRequest.Builder(MyWorker::class.java)
                .apply {
                    val data = Data.Builder().apply {
                        putString("message", editInput.text.toString())
                    }.build()
                    setInputData(data)
                }.build()

            manager.enqueueUniqueWork(onetimeWorkName, ExistingWorkPolicy.KEEP, request)
        }

        btnStart.setOnClickListener {


            val request = PeriodicWorkRequest.Builder(MyWorker::class.java, 20, TimeUnit.MINUTES)
                .apply {
                    val data = Data.Builder().apply {
                        putString("message", editInput.text.toString())
                    }.build()
                    setInputData(data)
                }.build()

            manager.enqueueUniquePeriodicWork(periodicWorkName, ExistingPeriodicWorkPolicy.REPLACE, request)
        }
    }
}

まとめ

キュー内でユニークにするには、WorkManagerの下記のメソッドを呼び出すだけで実現可能な事が理解できました。これらをうまく利用することによって、WorkRequestの輻輳を防ぐことができます。

  • enqueueUniqueWork
  • enqueueUniquePeriodicWork
8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?