프로그래밍/Kotlin

의존성을 가지는 Initializer 만들기

Lou Park 2024. 6. 1. 15:07

Android Jetpack 라이브러리 중 하나인 App Startup은 안드로이드 앱 구동에 필요한 초기 설정들을 체계적으로 초기화하는데 유용한 라이브러리다. 아래는 App Startup의 사용 예시 코드인데, dependencies 함수를 보면 예시 코드의 로거 Initializer는 WorkManagerInitializer에 의존하고 있음을 알 수 있다.

// Initializes ExampleLogger.
class ExampleLoggerInitializer : Initializer<ExampleLogger> {
    override fun create(context: Context): ExampleLogger {
        // WorkManager.getInstance() is non-null only after
        // WorkManager is initialized.
        return ExampleLogger(WorkManager.getInstance(context))
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        // Defines a dependency on WorkManagerInitializer so it can be
        // initialized after WorkManager is initialized.
        return listOf(WorkManagerInitializer::class.java)
    }
}

 

꽤나 유용하지만 App Startup 라이브러리의 Initializer는 안드로이드 플랫폼 종속적이며, 비동기처리도 할 수 없다.

 

안드로이드가 아니더라도 비슷한 종류의 작업을 처리하는 툴이 또 있다.

바로 Gradle! 아래 코드에는 taskX가 이루어지기 위해서는 taskY가 실행되어야함이라는 정보가 있다.

 

tasks.register("taskX") {
    dependsOn("taskY")
    doLast {
        println("taskX")
    }
}
tasks.register("taskY") {
    doLast {
        println("taskY")
    }
}

 

 

 

이런 것들에 힌트를 얻어서, 아래 코드 스니펫 처럼 적으면 알아서 의존성에따라 초기화를 시켜주는 Initializer를 만들고 싶었다.

 

 

먼저 Initializer 인터페이스다.

특별한건 없고, suspend 함수인 create가 있으며 공통적으로 알아야하는 Context를 받아서 Result를 반환한다.

 

 

다음은 InitializerBuilder의 구현부다.

ktor를 배껴쓸때 영감을 받아...직접 InitializerBuilder를 생성해서 쓰게 하진않고, Kotlin DSL을 활용하도록 구성했다.

build를 하면 Initializer<Unit>이 만들어지고 등록된 노드들을 순회하며 초기화 작업을 수행하게 된다. 그래프에 있는 노드의 초기화 작업이 실패할경우, 초기화도 실패하게 된다.

 

 

Gradle 공식 문서를 보다보면 이러한 Task Graph가 있는데, 방금 만든 Initializer를 이용해서 제대로 동작하는지 검증해보자! 왼쪽 그래프대로 구성해보았다.

fun main(args: Array<String>) {
    runBlocking {
        val initializer = buildInitializer {
            register(Node("A"), "A")
                .dependsOn("B")
                .dependsOn("C")

            register(Node("C"), "C")
                .dependsOn("D")
                .dependsOn("E")

            register(Node("B"), "B")

            register(Node("D"), "D")
                .dependsOn("Z")

            register(Node("E"), "E")
                .dependsOn("Z")

            register(Node("Z"), "Z")
        }
        initializer.create(SomeContext())
    }
}

 

예측했던 대로 잘 수행된다.