안드로이드

[Android] CoroutineWorker와 Hilt를 사용한 파일 다운로드 및 압축 해제

코딩하는후운 2024. 6. 14. 18:14
반응형

CoroutineWorker와 Hilt를 사용한 파일 다운로드 및 압축 해제

백그라운드에서도 다운로드가 진행 되야 해서 WorkManager를 사용할 예정
- CoroutineWorker가 있다고 한다.

 

Gradle

기존 WorkManager를 사용하던 나는 종속성 변경을 하였다.

// 기존
implementation "androidx.work:work-runtime:2.8.1"

// 변경
implementation "androidx.work:work-runtime-ktx:2.8.1"

이유는

  • KTX 버전은 보다 간결하고 Kotlin에 적합한 확장된 기능들을 제공
  • KTX 버전에서 CoroutineWorker가 포함.

KTX는 Worker 클래스를 전달하거나 전달하기 위해 Data 객체 생성해야 할 때 편한 구문을 제공.

Java

Data myData = new Data.Builder()
          .putInt(KEY_ONE_INT, aInt)
          .putIntArray(KEY_ONE_INT_ARRAY, aIntArray)
          .putString(KEY_ONE_STRING, aString)
          .build();

Kotlin

val data = workDataOf(
        KEY_MY_INT to myIntVar,
        KEY_MY_INT_ARRAY to myIntArray,
        KEY_MY_STRING to myString
    )
    
inline fun workDataOf(vararg pairs: Pair<String, Any?>): Data

Kotlin의 경우 workDataOf를 사용해 쉽게 이용 가능

 

CoroutineWorker

Worker와 CoroutineWorker의 차이

  Worker CoroutineWorker
doWork() 동기 처리 함수 중단 가능, 비동기 처리
  onStopped() 함수 직접 구현 자동으로 중단과 취소처리

기본적으로 Dispatchers.Default에서 실행됩니다.
- withContext()를 통해 변경 가능

 

실전

CoroutineWorker 설정

CoroutineWorker는 비동기 작업을 백그라운드에서 실행할 수 있는 기능을 제공합니다. 이를 이용해 파일을 다운로드하고, Hilt를 사용해 Repository를 주입받아 API 호출을 처리합니다.

하다보니 hilt로 inject를 받는 방법과
직접 WorkerFactory를 구현하여 Repository를 주입하는 방식이 있었다.

WorkManager Configuration 커스터마이즈

WorkManager는 기본적으로 AndroidManifest.xml에 정의된 초기화 프로그램을 통해 기본 설정을 사용합니다.
그러나, 커스텀 설정이나 커스텀 WorkerFactory를 사용하려면 이를 오버라이드해야 합니다.

WorkerFactory 제공

Hilt를 사용하여 Worker에 의존성을 주입하려면, WorkManager가 HiltWorkerFactory를 사용하도록 구성해야 합니다.
이를 위해서는 Application 클래스에서 WorkManager Configuration을 제공해야 합니다.

 


직접 주입 하는 방식

class FileDownloadWorker(
    context: Context,
    workerParams: WorkerParameters,
    private val downloadRepository: DownloadFileRepository
) : CoroutineWorker(context, workerParams) {

    override suspend fun doWork(): Result {
        val downloadUrl = inputData.getString("downloadUrl") ?: return Result.failure()
        return try {
            val response = downloadRepository.downloadFile(downloadUrl)
            if (response.isSuccessful && response.body() != null) {
                // 파일 저장 및 압축 해제 로직
                Result.success()
            } else {
                Result.failure()
            }
        } catch (e: Exception) {
            Result.failure()
        }
    }
}

Application (저는 Application이 Java였음..)

// implements androidx.work.Configuration.Provider 추가
public class MyApp extends KnApp implements androidx.work.Configuration.Provider {

@NonNull
@Override
public androidx.work.Configuration getWorkManagerConfiguration() {
    DownloadWorkerFactory downloadWorkerFactory = EntryPoints.get(
            MyApp.get(),
            EntryPointModule.class
    ).getDownloadWorkFactory();

    return new androidx.work.Configuration.Builder()
            .setWorkerFactory(downloadWorkerFactory)
            .build();
}

Factory

class DownloadWorkerFactory @Inject constructor(
    private val downloadFileRepository: DownloadFileRepository
) : WorkerFactory() {

    override fun createWorker(
        appContext: Context,
        workerClassName: String,
        workerParameters: WorkerParameters
    ): ListenableWorker? {
        return if (workerClassName == FileDownloadWorker::class.java.name) {
            // FileDownloadWorker 인스턴스를 생성하여 반환
            FileDownloadWorker(appContext, workerParameters, downloadFileRepository)
        } else {
            // 다른 Worker 클래스에 대한 처리를 추가할 수 있습니다.
            null
        }
    }
}

EntryModule

@InstallIn(SingletonComponent::class)
@EntryPoint
interface EntryPointModule {
	fun getDownloadWorkFactory(): DownloadWorkerFactory
}

AndroidManifests.xml

WorkManager에 Hilt를 적용하기 위해 custom configuration을 만들었기 때문에 기본 초기자를 제거 해야 한다고 함
제거해주지 않으면 WorkManager가 동작하지 않는다.
Custom WorkManager Configuration and Initialization | Android Developer

 

맞춤 WorkManager 구성 및 초기화  |  Background work  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 맞춤 WorkManager 구성 및 초기화 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 기본적으로 WorkManager는

developer.android.com

AndroidManifests.xml

// AndroidManifest에, 프로젝트에서 App Startup 라이브러리를 쓰고 있다면 아래와 같이 추가한다.
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <!-- If you are using androidx.startup to initialize other components -->
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
 </provider>
 
 // App Startup 라이브러리를 쓰고 있지 않다면 아래와 같이 추가하면 된다.
  <provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove">
 </provider>
 
 // work-runtime-ktx 버전이 2.6 미만이라면 아래와 같이 추가하면 된다.
 <provider
    android:name="androidx.work.impl.WorkManagerInitializer"
    android:authorities="${applicationId}.workmanager-init"
    tools:node="remove" />
    
    
 // work-runtime-ktx 버전이 2.6 이상인 경우에는, WorkManager 내부 구현체가 App Startup 라이브러리를 사용하고 있다고 한다.

 

hilt-compiler 디펜던시 추가

  • 나는 추가 안했는데도 다운로드 잘 되는듯?
    • 왠지 @HiltWorker 로 Worker안에 것들을 주입 받지 않아서 그런듯

대부분 WorkManager에 hilt를 적용하는 이유가, 프로젝트 구조가 hilt 기반으로 되어있을 것이다.
그래서 hilt-work 디펜던시만 추가할 것이고, 실제로 구현을 완료하고 빌드도 잘 된다.
하지만 실행해보면 WorkManager가 동작하지 않는다.

kapt("androidx.hilt:hilt-compiler:1.0.0")
  • androidx.hilt:hilt-compiler
  • com.google.dagger:hilt-android-compiler

이렇게까지 하면 잘 동작할 것이다.


Hilt로 주입 하는 방식

@HiltWorker
HiltWorkerFactory

implementation "androidx.hilt:hilt-work:1.0.0"

디펜던시를 추가하면 WorkManager와 관련된 annotation을 사용할 수 있다.

문제

Could not instantiate com.vaultmicro.kidsnote.manager.FileDownloadWorker
java.lang.NoSuchMethodException: 패키지명.manager.FileDownloadWorker.<init> [class android.content.Context, class androidx.work.WorkerParameters]

주입이 제대로 되지 않는 것 같다.

 

기본 Factory는 주입이 되는것 같다.

class MyApplication : Application(), Configuration.Provider {
    @Inject
    lateinit var workFactory: HiltWorkerFactory

    override fun getWorkManagerConfiguration(): Configuration =
    	Configuration.Builder()
            .setWorkerFactory(workerFactory)
            .build()
@HiltWorker
class PingWorker @AssistedInject constructor(
    @Assisted appContext: Context,
    @Assisted workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {

    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
        
    }
}

기본 Work는 제대로 주입 되는것 같은데 repository를 넣으면 repository가 제대로 주입 안되는 듯..
그래서 결국 Factory구현 해주어 Work는 직접 주입하도록 작업 하는 방법으로..!

 

참조 :
https://junyoeng.tistory.com/entry/WorkManager-및-Kotlin
https://velog.io/@beokbeok/WorkManager-hilt-not-working

반응형