مرة أخرى حول التعددية النمطية لتطبيقات Android

إن تقسيم تطبيق Android المترابط إلى وحدات ليس بالأمر الجديد ، وأصبحت طريقة تنظيم الكود هذه أكثر شيوعًا. لقد تطرقنا بالفعل إلى هذا الموضوع في الاجتماع المخصص لأفضل ممارسات العمل مع الوحدات بين الزملاء. لقد جمعنا هذه التجربة واختبرناها في مشروعنا ونريد أن نشارك النتائج والنصائح التي توصلنا إليها. لذلك ، يمكن أن تكون هذه المقالة مفيدة لكل من أولئك الذين يفكرون فقط في الانفصال ، وأولئك الذين بدأوه بالفعل.





يفكر المطورون عادة في استخدام الوحدات النمطية المتعددة لتسريع أوقات الإنشاء. لكن هذا لم يكن أهم شيء بالنسبة لنا. بالإضافة إلى سرعة البناء ، توفر الوحدات النمطية المتعددة أيضًا بنية أكثر صرامة والقدرة على إعادة استخدام الميزات بين المشاريع.



, . , , . 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