Sekali lagi tentang multi-modularitas aplikasi Android

Memecah aplikasi Android monolitik menjadi modul bukanlah hal baru, dan cara mengatur kode ini menjadi lebih umum. Kami telah menyentuh topik ini pada pertemuan yang didedikasikan untuk praktik terbaik bekerja dengan modul di antara rekan kerja. Kami telah mengumpulkan pengalaman ini, mengujinya di proyek kami dan kami ingin berbagi kesimpulan dan saran yang kami dapatkan. Oleh karena itu, artikel ini dapat bermanfaat baik bagi mereka yang baru saja memikirkan tentang divisi tersebut, maupun bagi mereka yang sudah memulainya.





Pengembang biasanya berpikir untuk menggunakan multi-modularitas untuk mempercepat waktu pembuatan. Tapi itu bukanlah hal terpenting bagi kami. Selain kecepatan build, multi-modularitas juga menyediakan arsitektur yang lebih ketat dan kemampuan untuk menggunakan kembali fitur antar project.



, . , , . Gradle - , Buck Bazel. , 300 .



. . - Android Wear. , .



, Java, , internal. : api + impl. , . .



, , Dagger, . , , . Kotlin, โ€” , .



, , .





, , .



-, . , . . , AppComponent, ( KAPT) . , โ€” , Gradle Android Gradle Plugin, , .



-, . . . , . Kotlin Multiplatform . , .



-, . . , , . , .



( , ) โ€” . Maven-. , .



Git- . - .



: , , , .





:



  1. App- โ€” , Feature-.



  2. Feature- โ€” , , -. , , - (, UI- , , UI). Feature- API Feature- Core-.

    Feature- API , API . internal , API, ยซยป Feature-. , . API Impl , , .

    .



  3. Core- โ€” , , Feature-. , . Core- . : module-injector.



  4. Module-injector โ€” , . , . .





โ€” API Impl Feature-, Feature- . internal- Kotlin.



Example- , App-. ( ) , . .



:



Module-Injector



, . , . , , . , , Dagger ( DI-). , :



interface ComponentHolder<C : BaseAPI, D : BaseDependencies> { 
    fun init(dependencies: D) 
    fun get(): C
    fun reset() 
} 

interface BaseDependencies

interface BaseAPI


. Feature- . ( , ) internal, .



- , , , Kotlin ( 2020-) module-injector. . .



, , โ€” . : , . UI, , โ€” .



Feature- , - , Core-. , :



, API Feature-. Feature-: :feature_purchase_api :feature_purchase_impl. API- , :module-injector. API-.



, . , .



ComponentHolder-:



object PurchaseComponentHolder : ComponentHolder<PurchaseFeatureApi, PurchaseFeatureDependencies> {
    private var purchaseComponentHolder: PurchaseComponent? = null

    override fun init(dependencies: PurchaseFeatureDependencies) {
        if (purchaseComponentHolder == null) {
            synchronized(PurchaseComponentHolder::class.java) {
                if (purchaseComponentHolder == null) {
                    purchaseComponentHolder = PurchaseComponent.initAndGet(dependencies)
                }
            }
        }
    }

    override fun get(): PurchaseFeatureApi {
        checkNotNull(purchaseComponentHolder) { "PurchaseComponent was not initialized!" }
        return purchaseComponentHolder!!
    }

    override fun reset() {
        purchaseComponentHolder = null
    }
}


:



  • .
  • init(), .
  • get(), API .
  • reset(), , .


. , .



PurchaseFeatureApi PurchaseFeatureDependencies , .



ComponentHolder Dagger-:



@Component(dependencies = [PurchaseFeatureDependencies::class], modules = [PurchaseModule::class])
@PerFeature
internal abstract class PurchaseComponent : PurchaseFeatureApi {

    companion object {
        fun initAndGet(purchaseFeatureDependencies: PurchaseFeatureDependencies): PurchaseComponent {
            return DaggerPurchaseComponent.builder()
                    .purchaseFeatureDependencies(purchaseFeatureDependencies)
                    .build()
        }
    }
}


initAndGet(), ComponentHolder. , Dagger . DI- .



, app :



@Module
class AppModule {

    @Singleton
    @Provides
    fun provideScannerFeatureDependencies(featurePurchase: PurchaseFeatureApi): ScannerFeatureDependencies {
        return object : ScannerFeatureDependencies {
            override fun dbClient(): DbClient = CoreDbComponent.get().dbClient()
            override fun httpClient(): HttpClient = CoreNetworkComponent.get().httpClient()
            override fun someUtils(): SomeUtils = CoreUtilsComponent.get().someUtils()
            override fun purchaseInteractor(): PurchaseInteractor = featurePurchase.purchaseInteractor()
        }
    }

    //      -   
    @Provides
    fun provideFeatureScanner(dependencies: ScannerFeatureDependencies): ScannerFeatureApi {
        ScannerFeatureComponentHolder.init(dependencies)
        return ScannerFeatureComponentHolder.get()
    }
    ...
}


provideScannerFeatureDependencies() ScannerFeatureDependencies, provideFeatureScanner() ComponentHolder- .



, app- . , . app- , . app- .



, .



, ComponentHolder reset(), . UI, reset() Lifecycle Observer- ( Activity, ):



public override fun onPause() {
   super.onPause()
           ...
   if (isFinishing) {
       AntitheftFeatureComponentHolder.reset()
   }
}


, , . get().



// get()  Feature-:
object PurchaseComponentHolder : ComponentHolder<PurchaseFeatureApi, PurchaseFeatureDependencies> {
    private var purchaseComponentHolder: PurchaseComponent? = null
...
    override fun get(): PurchaseFeatureApi {
        checkNotNull(purchaseComponentHolder) { "PurchaseComponent was not initialized!" }
        return purchaseComponentHolder!!
    }

    override fun reset() {
        purchaseComponentHolder = null
    }
// get()  app-:
//    @Singleton       Provider,  ,   get()  Dagger-
@Provides
   fun provideFeatureScanner(dependencies: ScannerFeatureDependencies): ScannerFeatureApi {
       ScannerFeatureComponentHolder.init(dependencies)
       return ScannerFeatureComponentHolder.get()
   }
}


, DI- app- (, Singleton Dagger). init() , , reset() .



API- โ€” Provider<T> :



class GlobalNavigator @Inject constructor(
       //  Provider    get()                
       private val featureScanner: Provider<ScannerFeatureApi>,
       private val featureAntitheft: Provider<AntitheftFeatureApi>,
       private val context: Context
) : Navigator {
   ...
   featureScanner.get().scannerStarter().start(context) //  
   ...
}


UI



, API , . API , UI-, Activity, , View.



Activity API :



interface AntitheftStarter {
    fun start(context: Context)
}


Activity , Intent:



internal class AntitheftStarterImpl @Inject constructor() : AntitheftStarter {
    override fun start(context: Context) {
        val intent = Intent(context, AntitheftActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        context.startActivity(intent)
    }
}


FragmentManager , API , . : Cicerone, Navigation Component FragmentManager.



, , . , .





Core-ui



UI- , , UI- . UIKit. , Application.



, theme.xml, (, Example-, , ). core-ui , , , . UIKit (api implementation) Feature-, UI. .



Core-strings



, , .



, , , : . . , . .



Core-native



C++-. JNI-, Java-. , SDK. , , ABI. , .






. , , . , , , .



, .




All Articles