이전 글 : 2019/10/17 - [프로그래밍/Android] - Android - WorkManager (4) -Observing intermediate Worker progress
출처- Android developer chaining work
WorkManager를 사용하면 여러 종속 작업을 지정하고 작업 순서를 정의하는 일련의 작업을 작성하고 대기열에 넣을 수 있습니다. 이는 특정 작업에서 여러 작업을 실행해야 할 때 특히 유용합니다.
Chain of work을 만들려면 WorkContinuation 인스턴스를 반환하는 WorkManager.beginWith (OneTimeWorkRequest) 또는 WorkManager.beginWith (List<OneTimeWorkRequest>)를 사용할 수 있습니다.
그런 다음 WorkContinuation을 사용하여 WorkContinuation then(OneTimeWorkRequest) 또는 WorkContinuation.then(List<OneTimeWorkRequest>)을 사용하여 종속 OneTimeWorkRequests를 추가 할 수 있습니다.
WorkContinuation.then(...)을 호출 할 때마다 WorkContinuation의 새 인스턴스를 리턴합니다. OneTimeWorkRequests 목록을 추가하면 이러한 요청이 병렬로 실행될 수 있습니다.
마지막으로 WorkContinuation.enqueue () 메서드를 사용하여 WorkContinuations 체인을 enqueue () 할 수 있습니다.
그럼 예제 code를 확인 해 보겠습니다.
File->New->New Project 선택 후 Fragment+ViewModel로 project를 생성합니다.
그리고 다음 worker들을 만들어 줍니다.
MyWorker_A.kt
class MyWorker_A_1 (appContext : Context, workerParams: WorkerParameters) : Worker(appContext,workerParams){
override fun doWork(): Result {
Log.d("TEST", "start MyWorker_A_1")
var count = 0
var doCounting = true
while (doCounting){
Log.d("TEST","MyWorker_A_1 now Count : ${++count}" )
if(count == 3){
doCounting = false
}
Thread.sleep(1000)
}
val outputData = workDataOf(Pair("outputData", "MyWorker_A_1 from doWork"))
return Result.success(outputData)
}
}
class MyWorker_A_2 (appContext : Context, workerParams: WorkerParameters) : Worker(appContext,workerParams){
override fun doWork(): Result {
Log.d("TEST", "start MyWorker_A_2")
var count = 0
var doCounting = true
while (doCounting){
Log.d("TEST","MyWorker_A_2 now Count : ${++count}" )
if(count == 3){
doCounting = false
}
Thread.sleep(1000)
}
val outputData = workDataOf(Pair("outputData", "MyWorker_A_2 from doWork"))
return Result.success(outputData)
}
}
class MyWorker_A_3 (appContext : Context, workerParams: WorkerParameters) : Worker(appContext,workerParams){
override fun doWork(): Result {
Log.d("TEST", "start MyWorker_A_3")
var count = 0
var doCounting = true
while (doCounting){
Log.d("TEST","MyWorker_A_3 now Count : ${++count}" )
if(count == 3){
doCounting = false
}
Thread.sleep(1000)
}
val outputData = workDataOf(Pair("outputData", "MyWorker_A_3 from doWork"))
return Result.success(outputData)
}
}
MyWorker_B.kt
class MyWorker_B (appContext : Context, workerParams: WorkerParameters) : Worker(appContext,workerParams){
override fun doWork(): Result {
Log.d("TEST", "start MyWorker_B")
var count = 0
var doCounting = true
while (doCounting){
Log.d("TEST"," MyWorker_B now Count : ${++count}" )
if(count == 3){
doCounting = false
}
Thread.sleep(1000)
}
val outputData = workDataOf(Pair("outputData", "outputData from doWork"))
return Result.success(outputData)
}
}
MyWorker_C.kt
class MyWorker_C (appContext : Context, workerParams: WorkerParameters) : Worker(appContext,workerParams){
override fun doWork(): Result {
Log.d("TEST", "start MyWorker_C")
var count = 0
var doCounting = true
while (doCounting){
Log.d("TEST","MyWorker_C now Count : ${++count}" )
if(count == 3){
doCounting = false
}
Thread.sleep(1000)
}
val outputData = workDataOf(Pair("outputData", "outputData from doWork"))
return Result.success(outputData)
}
}
그 다음 Fragment에 실행을 위한 Btn을 만들고 다음과 같이 WorkRequest들을 추가 합니다.
class MainFragment : Fragment() {
companion object {
fun newInstance() = MainFragment()
}
private lateinit var viewModel: MainViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.main_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
// TODO: Use the ViewModel
val A_1 = OneTimeWorkRequestBuilder<MyWorker_A_1>().build()
val A_2 = OneTimeWorkRequestBuilder<MyWorker_A_2>().build()
val A_3 = OneTimeWorkRequestBuilder<MyWorker_A_3>().build()
val B = OneTimeWorkRequestBuilder<MyWorker_B>().build()
val C = OneTimeWorkRequestBuilder<MyWorker_C>().build()
start_btn.setOnClickListener {
WorkManager.getInstance(context!!).beginWith(listOf(A_1,A_2,A_3)).then(B).then(C).enqueue()
}
}
}
이렇게 추가후 실행 버튼을 누르면 A_1, A_2, A_3 WorkRequest들이 병렬로 실행 된 후 B WorkRequest가 실행되고, 마지막으로 C WorkRequest가 실행 됩니다.
Input Mergers
OneTimeWorkRequests 체인을 사용하는 경우 상위 OneTimeWorkRequests의 출력이 하위 항목에 입력으로 전달됩니다. 따라서 위 예에서 A_1, A_2 및 A_3의 출력은 압축 요청에 대한 입력으로 전달됩니다.
여러 상위 OneTimeWorkRequests의 입력을 관리하기 위해 WorkManager는 InputMergers를 사용합니다.
WorkManager에서 제공하는 두 가지 유형의 InputMergers가 있습니다.
- OverwritingInputMerger는 모든 입력의 모든 키를 출력에 추가하려고 시도합니다. 충돌이 발생하면 이전에 설정 한 키를 덮어 씁니다.
- ArrayCreatingInputMerger는 입력 병합을 시도하여 필요한 경우 배열을 만듭니다.
그럼 simple 예제 code를 보겠습니다.
우선 MainFragment에 B workRequest를 만들때 inputMerger를 추가 합니다.
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
// TODO: Use the ViewModel
val A_1 = OneTimeWorkRequestBuilder<MyWorker_A_1>().build()
val A_2 = OneTimeWorkRequestBuilder<MyWorker_A_2>().build()
val A_3 = OneTimeWorkRequestBuilder<MyWorker_A_3>().build()
val B = OneTimeWorkRequestBuilder<MyWorker_B>().setInputMerger(ArrayCreatingInputMerger::class).build()
val C = OneTimeWorkRequestBuilder<MyWorker_C>().build()
start_btn.setOnClickListener {
WorkManager.getInstance(context!!).beginWith(listOf(A_1,A_2,A_3)).then(B).then(C).enqueue()
}
}
그리고 MyWork_B에 결과를 노출할 Log를 추가 합니다.
class MyWorker_B (appContext : Context, workerParams: WorkerParameters) : Worker(appContext,workerParams){
override fun doWork(): Result {
Log.d("TEST", "start MyWorker_B")
val list = inputData.getStringArray("outputData")
list?.let {
Log.d("TEST","MyWorker_B outputData list : ${list[0]}, ${list[1]}, ${list[2]}")
}
var count = 0
var doCounting = true
while (doCounting){
Log.d("TEST"," MyWorker_B now Count : ${++count}" )
if(count == 3){
doCounting = false
}
Thread.sleep(1000)
}
val outputData = workDataOf(Pair("outputData", "outputData from doWork"))
return Result.success(outputData)
}
}
MyWorker_A_1 ~ 3모두 Result.success에 key를 outputData로 하는 data를 입력해서 return 하고 이습니다. 그래서 MyWorker_B에서 inputData중에 outputData를 key하는 StringArray를 get 합니다. (ArrayCreatingInputMerger을 선택했으므로 위의 설명처럼 배열을 만들어서 MyWorker_B에서 획득 합니다.)
그래서 Log를 확인해보면
MyWorker_A_1~3 이 병렬로 실행되고, MyWorker_B에서 그 값을 획득하고 노출하고 있는것을 확인 할 수 있습니다.
Chaining and Work Statuses
OneTimeWorkRequests 체인을 만들 때 명심해야 할 것이 몇 가지 있습니다.
- 모든 상위 OneTimeWorkRequest가 성공하면 (즉, Result.success ()를 반환) 종속 OneTimeWorkRequests는 차단 해제됩니다 (ENQUEUED로 전환).
- 상위 OneTimeWorkRequest가 실패하면 (Result.failure ()를 반환하면 모든 종속 OneTimeWorkRequests도 FAILED로 표시됩니다.
- 상위 OneTimeWorkRequest가 취소되면 모든 종속 OneTimeWorkRequest도 CANCELLED로 표시됩니다.
'2023년 이전 > Android' 카테고리의 다른 글
Android 백그라운드 - Thread, Handler ,Looper의 차이 (0) | 2019.11.12 |
---|---|
Android - WorkManager (6) - 취소, 반복작업, 고유 작업 (0) | 2019.10.17 |
Android - WorkManager (4) -Observing intermediate Worker progress (0) | 2019.10.17 |
Android - WorkManager (3) - Work States and observing work (0) | 2019.10.17 |
Android - WorkManager (2) WorkRequest (0) | 2019.10.16 |