본문 바로가기
2023년 이전/Android

Android - WorkManager(1)

by JeongUPark 2019. 10. 16.
반응형

출처 - Android Developer WorkManager 페이지

 

Android Oreo 부터 긴 작업의 백그라운드 서비스와 브로드캐스트가 제한이 되었습니다. (자세한 사항은 여기서 확인해 주세요) 그래서 Android Oreo 이후 백그라운드 서비스 또는 브로드캐스트를 사용하기 위해서는 WorkManager를 사용하게 되었습니다. 

즉 WorkManager는 즉시 실행하지 않아도되며, 앱이 종료되거나 장치가 다시 시작 되더라도 안정적으로 실행해야하는 작업을위한 것입니다. 예를 들어 이미지를 서버에 업로드 해야 하거나, 데이터를 분석하고 이를 데이터베이스에 저장해야 하는 작업에는 WorkManager 를 사용하는것이 좋습니다.

그러나 사용자가 현재 보고있는 UI 를 빠르게 변경해야 하는 작업이나 물건 구입 과정에서의 결제 진행 등 즉시 처리해야 하는 작업은 WorkManager 를 사용하지 않는것이 좋습니다.

WorkManager 의 작업은 반드시 실행되지만 그 처리가 상황에 따라 지연 되거나 도중에 중단될 경우 다시 실행 될수 있다는 것을 꼭 기억해야 합니다. 적절한 상황에서, WorkManager 는 AlarmManager 나 JobScheduler, JobDispather 를 대체하는 훌륭한 백그라운드 작업 처리 방법입니다

(WorkManager를 써야하는 사황에 대해서는 Android Developer에 Guide to background processing에서 자세히 확인 하실 수 있습니다.)

 

그럼 간단한 사용 방법을 확인해 보겠습니다.

우선 File -> New -> New Project를 선택하고 Fragment+ViewModel을 선택하여 프로젝트를 만듭니다.

WorkManager를  프로젝트에 추가 합니다. 추가 방법은

Project build.gradle에 다음과 같이  Google Maven repository가 추가 되있어야 하고(프로젝트를 만들면 보통은 추가되어 있을 것 입니다.)

allprojects {
    repositories {
        google()
        ....     
    }
}

Module build.gradle에 다음 라이프러리들을 추가합니다.

dependencies {
  def work_version = "2.2.0"

    // (Java only)
    implementation "androidx.work:work-runtime:$work_version"

    // Kotlin + coroutines
    implementation "androidx.work:work-runtime-ktx:$work_version"

    // optional - RxJava2 support
    implementation "androidx.work:work-rxjava2:$work_version"

    // optional - GCMNetworkManager support
    implementation "androidx.work:work-gcm:$work_version"

    // optional - Test helpers
    androidTestImplementation "androidx.work:work-testing:$work_version"
  }

 

 

Background task를 만들 때는 work class를 활용하여 만듭니다. 그리고 그 동작은 dowork() Method에서하며 결과를 반환 해야 합니다. 즉 작업의 단위를 결정합니다. 그리고 그 결과를 반환하는데,  Result.failure(), Result.success(), Result.retry()가 있습니다.

그럼 Background task를 만들기 위해 MyWorker라는 class를 만들고 Worker class를 확장합니다. 그리고 dowork() Method를 overrride 합니다.

class MyWorker (appContext : Context, workerParams: WorkerParameters) : Worker(appContext,workerParams){
    override fun doWork(): Result {
        var count = 0
        var doCounting = true
        while (doCounting){
            Log.d("TEST","now Count : ${++count}" )
            if(count == 60){
                doCounting = false
            }
            Thread.sleep(1000)
        }
        return Result.success()
    }

}

위의 doWork에 작성된 동작은 1초마다 count를 하고 그 값을 console Log로 표출 합니다.

그리고 MainFragment를 다음과 같이 작성합니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.main.MainFragment">

    <TextView
        android:id="@+id/message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MainFragment"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/workmanager_start_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="Doing"/>
</androidx.constraintlayout.widget.ConstraintLayout>
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)
        val myWorkRequest = OneTimeWorkRequestBuilder<MyWorker>().build()
        workmanager_start_btn.setOnClickListener {
           WorkManager.getInstance(context!!).enqueue(myWorkRequest)
        }
    }

}

 Woker가 작업 단위를 정의한다면, WorkRequest는 작업 실행 및 시기를 정합니다.  한번 작업은  OneTimeWorkRequest로 주기적인 작업은  PeriodicWorkRequest을 사용합니다. 

여기서 저는 한번의 작업을 위해 OneTimeWorkRequest를 만들었습니다. OneTimeWorkRequest는 위의 code 처럼 OneTimeWorkRequestBuilder를 통하여 만들어 수 있습니다. (OneTimeWorkRequestBuilder 는 jvmTarget 설정이 필요 합니다. 그러므로 Module build.gradle의 android 블럭 안에 다음과 같이 추가합니다.)

android{
	.....
    
	kotlinOptions {
        jvmTarget = "1.8"
    }
}

그리고 이렇게 생성된 WorkRequest를 WorkManager에 enqueue 하면 동작 합니다. 작업자가 실행되는 정확한 시간은 WorkRequest에 사용 된 제약 조건과 시스템 최적화에 따라 다릅니다.

 

그럼 작업 한 App을 실행해 보면

 

이런 결과를 볼 수 있고 doing 버튼을 누르면

Log를 볼 수 있습니다. 이 로그는 App을 Background 상태에 두어도 계속 동작합니다.(단, App을 완전히 종료 시키면 멈춥니다.)

 

반응형