在Android上編寫模塊化項目(翻譯)

來源博客:Wang Jie's Blog
本文連接:http://blog.wangjiegulu.com/2018/02/13/writing_a_modular_project_on_android
版權聲明:本博客全部文章除特別聲明外,均採用 CC BY 4.0 CN協議 許可協議。轉載請註明出處。
html

原文:https://medium.com/mindorks/writing-a-modular-project-on-android-304f3b09cb37java

在Android上編寫模塊化項目(翻譯)

當咱們在 Android Studio 上建立一個新的項目時,自帶一個 app module。這時咱們大多數人編寫整個應用的地方。每次點擊 run 按鈕都會觸發咱們整個全部 module 上的 gradle 構建,並檢查全部文件是否有變化。這就是爲何 gradle 構建會在更大的應用程序上花費 10分鐘的時間,而且減慢開發者的輸出react

要解決這個問題,複雜的應用程序,如 Uber 決定對它們的應用程序進行模塊化並從中得到了不少。下面是試用模塊化項目的一些優點:android

  • 更快的 gradle 構建
  • 跨應用/模塊複用通用的功能
  • 易於插拔到Instant apps
  • 更好的團隊工做,一我的能夠單獨負責一個模塊
  • 更流暢地git flows

因爲上述優點,當我剛開始Posts這個應用時,我就在始終堅持使用模塊化方法。對此,Android 團隊已經給咱們提供了一些工具,可是我確實遇到了一些障礙,一下是我學習到的內容:git

我該怎麼分割個人 modules ?

你的應用程序是流程集構成的,好比,Google Play 有應用詳情流,它包含了簡要,描述詳情,應用截圖,評論活動等。github

全部這些均可以歸爲同一模塊 —— app-details數據庫

你的應用會包含多個相似流程的模塊,有 authentication, settings, on-boarding等等。固然還有一些不須要UI元素呈現的模塊如 —— notifications, analytics, first-fetch等等。這些模塊包含與流程有關的 activities, repositories, entities和依賴注入相關東西。api

可是這些模塊中老是有一些共同的功能和工具。這就是爲何你須要一個 core 模塊。app

什麼是 core 模塊 ?

Core 模塊是一個你項目中簡單的 module 庫。core 庫能夠(除其它外),框架

怎麼使用第三方庫?

核心(core)模塊的其中一個職責是爲你的功能(feature)模塊提供外部依賴。這使得很容易實如今你的 feature 模塊中共享相同版本的庫。只須要在你的 core 模塊的 dependencies 中使用 api,這樣你就能在全部 feature 模塊中使用它們。

dependencies {
    api fileTree(include: ['*.jar'], dir: 'libs')
    api deps.support.appCompat
    api deps.support.recyclerView
    api deps.support.cardView
    api deps.support.support
    api deps.support.designSupport

    api deps.android.lifecycleExt
    api deps.android.lifecycleCommon
    api deps.android.roomRuntime
    api deps.android.roomRx

    api deps.kotlin.stdlib

    api deps.reactivex.rxJava
    api deps.reactivex.rxAndroid

    api deps.google.dagger
    kapt deps.google.daggerProcessor

    api deps.square.picasso
    api deps.square.okhttpDownloader

    api deps.square.retrofit
    api deps.square.okhttp
    api deps.square.gsonConverter
    api deps.square.retrofitRxAdapter

    implementation deps.facebook.stetho
    implementation deps.facebook.networkInterceptor

    testApi deps.test.junit
    androidTestApi deps.test.testRunner
    androidTestApi deps.test.espressoCore
}

有種依賴的可能性是隻有對 feature-a 模塊有用,可是在 feature-b 中無用。對於這種狀況,我推薦在你的 core 的依賴中使用 api,由於 proguard 注意到而不會包含在 feature-b instant app 中。

怎麼使用 Room ?

這個困擾我挺久的時間。咱們但願把咱們的數據庫定義到 core 模塊中,由於它是咱們應用程序要共享的通用的功能。爲了讓 Room 工做,你須要一個包含了全部 entity 類的數據庫文件。

@Database(entities = [Post::class, User::class, Comment::class], version = 1,exportSchema = false)
abstract class PostDb : RoomDatabase() {
    abstract fun postDao(): PostDao
    abstract fun userDao(): UserDao
    abstract fun commentDao(): CommentDao
}

可是,如上面提到的,咱們的 entity 類是被定義在 feature 模塊中,並且 core 模塊不能去訪問它們。這是我碰到障礙的地方,通過一番思考後,你作了一件最棒的事,尋求 Yigit 的幫助。

Yigit 闡明瞭觀點,你必需要在每一個 feature模塊中都建立一個新的 db 文件,而後每一個模塊一個數據庫。

這有幾個好處:

  • 遷移是模塊化的
  • 即時 app 僅包含它們須要的表
  • 查詢會更快

缺點:

  • 跨模塊數據關係將不可能

注意:爲了 Room 的註解可以工做,不要忘記在你的 feature 模塊中增長下面依賴

kapt "android.arch.persistence.room:compiler:${versions.room}"

怎麼使用 Dagger 2 ?

一樣的問題 Dagger 也遇到了。個人 core 模塊中的 application 類不能訪問和初始化我 feature 模塊中的組件。這是從屬組件完美的用例。

你的 core 組件定義了它想要暴露給依賴組件的依賴關係

@Singleton
@Component(modules = [AppModule::class, NetworkModule::class, StorageModule::class, ImageModule::class])
interface CoreComponent {

    fun context(): Context

    fun retrofit(): Retrofit

    fun picasso(): Picasso

    fun sharedPreferences(): SharedPreferences

    fun scheduler(): Scheduler
}

您的模塊組件將 CoreComponent 定義爲依賴項,並使用傳遞的依賴

@ListScope
@Component(dependencies = [CoreComponent::class], modules = [ListModule::class])
interface ListComponent {
    fun inject(listActivity: ListActivity)
}

@Module
@ListScope
class ListModule {

    /*Uses parent's provided dependencies like Picasso, Context and Retrofit*/
    @Provides
    @ListScope
    fun adapter(picasso: Picasso): ListAdapter = ListAdapter(picasso)

    @Provides
    @ListScope
    fun postDb(context: Context): PostDb = Room.databaseBuilder(context, PostDb::class.java, Constants.Posts.DB_NAME).build()

    @Provides
    @ListScope
    fun postService(retrofit: Retrofit): PostService = retrofit.create(PostService::class.java)
}

在哪裏初始化個人 components ?

我爲個人功能的全部組件建立了一個單例 holder。這個 holder 用於建立,維護和銷燬個人 component 實例。

@Singleton
object PostDH {
    private var listComponent: ListComponent? = null

    fun listComponent(): ListComponent {
        if (listComponent == null)
            listComponent = DaggerListComponent.builder().coreComponent(CoreApp.coreComponent).build()
        return listComponent as ListComponent
    }

    fun destroyListComponent() {
        listComponent = null
    }
}

注意:爲了 Dagger 的註解可以工做,不要忘記在你的 feature 模塊中增長下面依賴

kapt "com.google.dagger:dagger-compiler:${versions.dagger}"

總結

儘管把你的單獨的 application 轉成模塊化有一些棘手,其中一些我試圖經過上面的方法來解決,優勢是深入的。若是您在模塊中遇到任何障礙,請隨時在下面說起它們,咱們能夠一塊兒討論解決方案。

謝謝。

相關文章
相關標籤/搜索