ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 스튜디오 WorkManager
    Android Studio 2023. 7. 26. 01:30
    728x90

    공식문서 : https://developer.android.com/topic/libraries/architecture/workmanager?hl=ko 

     

     

    앱 아키텍처: 데이터 영역 - WorkManager로 작업 예약 - Android 개발자  |  Android Developers

    데이터 영역 라이브러리에 관한 이 앱 아키텍처 가이드를 통해 지속적인 작업 유형과 기능 등을 알아보세요.

    developer.android.com

     

    WorkManager는 지속적인 작업에 권장되는 솔루션입니다. 앱이 다시 시작되거나 시스템이 재부팅될 때 작업이 예약된 채로 남아 있다면 그 작업은 유지됩니다. 대부분의 백그라운드 처리는 지속적인 작업을 통해 가장 잘 처리되므로 WorkManager는 백그라운드 처리에 권장하는 기본 API입니다.

     

    WorkManager가 처리하는 지속적인 작업의 유형 3가지

     

    Immediate(즉시): 즉시 시작하고 곧 완료해야 하는 작업입니다. 신속하게 처리될 수 있습니다.
    Long Running(장기 실행): 더 오래(10분 이상이 될 수 있음) 실행될 수 있는 작업입니다.
    Deferrable(지연 가능): 나중에 시작하며 주기적으로 실행될 수 있는 예약된 작업입니다.

     

     

     

    다음은 WorkManager의 특징입니다.

     

    1. 작업 제약 조건( Work constraints)

    작업 제약조건을 사용하여 해당 작업을 실행하는데 있어 최적인 조건을 선언하여 정의하고 해당 제약 조건을 만족할 때만 실행합니다. 

     

    2. 강력한 예약 관리 (Robust scheduling)

     

    WorkManager를 사용하면 가변 일정 예약 기간을 통해 한 번 또는 반복적으로 실행할 작업을 예약할 수 있습니다. 작업에 태그 및 이름을 지정하여 고유 작업 및 대체 가능한 작업을 예약하고 작업 그룹을 함께 모니터링하거나 취소할 수 있습니다.
    예약된 작업은 내부적으로 관리되는 SQLite 데이터베이스에 저장되며 WorkManager에서 기기를 재부팅해도 작업이 유지되고 다시 예약되도록 보장합니다. 또한 WorkManager는 절전 기능을 사용하고 권장사항(예: 잠자기 모드)을 준수하므로 배터리 소모를 걱정하지 않아도 됩니다.

     

    3. 신속 처리 작업 (Expedited work)

    WorkManager를 사용하면 백그라운드에서 즉시 실행할 작업을 예약할 수 있습니다. 사용자에게 중요하고 몇 분 내에 완료되는 작업에는 신속 처리 작업을 사용해야 합니다.

     

    4. 유연한 재시도 정책 (Flexible retry policy)

    경우에 따라 작업이 실패하기도 하는데 WorkManager는 구성가능한 지수 백오프 정책(BackoffPolicy)을 비롯하여 유연한 재시도 정책을 제공합니다.

     

    5. 작업 체이닝 (Work chaining)

    복잡한 관련 작업의 경우 직관적인 인터페이스를 사용하여 개별 작업을 함께 체이닝하면서 순차적으로 실행할 작업과 동시에  실행할 작업을 제어할 수 있습니다.

     

    6. 내장 스레딩 상호 운용성(Built-In threading interoperability)

    WorkManager는 코루틴 및 RxJava와 원활하게 통합되며 자체 비동기 API를 연결할 수 있는 유연성을 제공합니다.

     

     

    안정적인 작업에 WorkManager 사용하기

    WorkManager는 사용자가 화면을 벗어나 이동하거나, 앱이 종료되거나 기기가 다시 시작되더라도 안정적으로 실행되어야 하는 작업을 대상으로 설계되었습니다.

     

    1. 백엔드 서비스에 로그 또는 분석내용 전송

    2. 주기적으로 서버와 애플리케이션 데이터를 동기화

     

    주의사항1

    WorkManager는 앱 프로세스가 사라지더라도 안전하게 종료될 수 있는 진행 중인 백그라운드 작업을 위한 것이 아닙니다. 즉각적인 실행이 필요한 모든 작업을 위한 일반적인 솔루션도 아닙니다. 따라서 백그라운드 작업을 위하여 적합한 솔루션은 하기 링크에서 확인해 볼 수 있습니다.

     

    https://developer.android.com/guide/background?hl=ko 

     

    백그라운드 처리 가이드  |  Android 개발자  |  Android Developers

    백그라운드 처리 가이드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 백그라운드 데이터 처리는 사용자의 기대에 부응하고 사용자에게 도움이 되는 Androi

    developer.android.com

     

    주의사항2

    코루틴은 특정 사용 사례에 권장 되는 솔루션이지만, 지속적인 작업에서는 사용해서는 안됩니다.

    코루틴은 동시 실행 프레임워크, WorkManager는 지속적인 작업을 위한 라이브러리 라는 점을 유의 해야 합니다.

     

    또한 백그라운드 작업을 위한 솔루션중 AlarmManager는 시계 또는 캘린더에만 사용해야 합니다.

     

     

     

    공식문서 보고 따라하기(간단한 작업)

     

    클라이언트(안드로이드)

    class MainActivity : AppCompatActivity() {
        companion object{
            val PORT_NUMBER = 44444
            val SERVER_IP = ipAdress
        }
    
       lateinit var activityMainBinding: ActivityMainBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(activityMainBinding.root)
    
            activityMainBinding.run{
                button.setOnClickListener {
                    // 일회성 작업 예약
                    val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)
                    WorkManager
                        .getInstance(this@MainActivity)
                        .enqueue(myWorkRequest)
                }
    
                button2.setOnClickListener {
                    // 신속 처리 작업 예약
                    val request = OneTimeWorkRequestBuilder<MyWork>()
                        .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
                        .build()
    
                    WorkManager.getInstance(this@MainActivity)
                        .enqueue(request)
                }
                button3.setOnClickListener {
                    val constraints = Constraints.Builder()
                        .setRequiredNetworkType(NetworkType.UNMETERED)
                        .setRequiresCharging(true)
                        .build()
    
                    val myWorkRequest: WorkRequest =
                        OneTimeWorkRequestBuilder<MyWork>()
                            .setConstraints(constraints)
                            .build()
    
                    WorkManager.getInstance(this@MainActivity)
                        .enqueue(myWorkRequest)
    
                }
    
                button4.setOnClickListener {
    
                    val sdf = SimpleDateFormat("HH:mm:ss");
                    var now = sdf.format(System.currentTimeMillis())
                    textView.text = "버튼 누른 시간 : $now"
    
                    val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
                        .setInitialDelay(10, TimeUnit.SECONDS)
                        .build()
    
                    WorkManager.getInstance(this@MainActivity)
                        .enqueue(myWorkRequest)
                }
    
                button5.setOnClickListener {
                    val sdf = SimpleDateFormat("HH:mm:ss");
                    var now = sdf.format(System.currentTimeMillis())
                    textView.text = "버튼 누른 시간 : $now"
    
                    // 실패하면 지정한 시간에 따라 다시 시도하며, 실패할 경우 점점 재시도를 시작하는 시간은 점점 늘어난다.
                    val myWorkRequest = OneTimeWorkRequestBuilder<MyWork2>()
                        .setBackoffCriteria(
                            BackoffPolicy.LINEAR,
                            10000,
                            TimeUnit.MILLISECONDS)
                        .build()
    
    
                    WorkManager.getInstance(this@MainActivity)
                        .enqueue(myWorkRequest)
                }
            }
        }
    
    }
    
    class MyWork2(appContext: Context, workerParams: WorkerParameters):
        Worker(appContext, workerParams) {
        // doWork 는 WorkManager에서 제공하는 백그라운드 스레드에서 비동기적으로 실행된다.
        override fun doWork(): Result {
    
            thread {
                val socket = Socket(SERVER_IP, PORT_NUMBER)
    
                val outputStream = socket.getOutputStream()
                val dataOutputStream = DataOutputStream(outputStream)
                val sdf = SimpleDateFormat("HH:mm:ss");
                var now = ""
    
                for(i in 1 .. 10000){
                    sleep(100)
                    now = sdf.format(System.currentTimeMillis())
                    dataOutputStream.writeUTF("$now")
                }
                socket.close()
            }
    
            // Indicate whether the work finished successfully with the Result
            return Result.retry()
        }
    }
    
    
    class MyWork(appContext: Context, workerParams: WorkerParameters):
        Worker(appContext, workerParams) {
        // doWork 는 WorkManager에서 제공하는 백그라운드 스레드에서 비동기적으로 실행된다.
        override fun doWork(): Result {
    
            thread {
                val socket = Socket(SERVER_IP, PORT_NUMBER)
    
                val outputStream = socket.getOutputStream()
                val dataOutputStream = DataOutputStream(outputStream)
                val sdf = SimpleDateFormat("HH:mm:ss");
                var now = ""
    
                for(i in 1 .. 10000){
                    sleep(100)
                    now = sdf.format(System.currentTimeMillis())
                    dataOutputStream.writeUTF("$now")
                }
                socket.close()
            }
    
            // Indicate whether the work finished successfully with the Result
            return Result.success()
        }
    }

     

    서버

    import netscape.javascript.JSObject
    import java.io.BufferedInputStream
    import java.io.BufferedReader
    import java.io.DataInputStream
    import java.io.DataOutputStream
    import java.io.ObjectInputStream
    import java.io.Serializable
    import java.lang.Thread.sleep
    import java.net.ServerSocket
    import kotlin.concurrent.thread
    fun main(){
        while(true){
            // 사용할 포트번호
            val portNumber = 44444
            // 서버 객체를 생성한다.
            val serverSocket = ServerSocket(portNumber)
            println("접속대기")
            val socket = serverSocket.accept()
    
            var str : String? = null
    
            val sis = socket.getInputStream()
            val ois = DataInputStream(sis)
    
            try{
                while(true){
                    str = ois.readUTF()
                    println("${str} 수신")
                }
    
            } catch (e: Exception){
                socket.close()
                serverSocket.close()
            }
        }
    }
Designed by Tistory.