[Android元件化]Android app Bundle

Cang_Wang發表於2018-07-20

Android元件化架構

以下是我這個系列的相關文章,有興趣可以參考一下,可以給個喜歡或者關注我的文章。 [Android]如何做一個崩潰率少於千分之三噶應用app--章節列表

國內的外掛化出不了海,而Google終於也出了元件化和外掛化的模型了,這一節就是帶你感受一下來自官方的威力。 1.只有上線google市場的應用才能使用。 2.先下載Android Studio 3.3吧,gradle會預設使用最新的4.9,騷年 不符合以上的條件的同學,請自動略過吧

優勢

1.初始下載的大小更加小 2.可以只下載地區資源 3.安裝更加快 4.可以動態更新

限制

1.手機要有google store和google play 2.需要上傳你的簽名檔案到google play 3.最低版本支援api 21 4.低於api21的第一次下載就會下載完整包,會優化地區和資源配置,但是無法做到動態更新 5.base app不能大於100M,動態更新的aab檔案不要大於10M,而且最好要有下載提示

App分為3種狀態

1.Base App 首次安裝到手機的資源和檔案,基礎的dex資源

2.Configuration APKs native libraries 和適配當前手機螢幕解析度的資源

3.Dynamic feature APKs 不需要在首次安裝就載入的模組,動態載入模組,打包後是.aab字尾的檔案。

操作基礎介紹 動態更新的module必須使用Dynamic Feature Module

Dynamic Feature Module.png

預設就是選擇Android 5.0版本

.png

應用於app商店使用下載和安裝動態功能的模組,如果下載的機型低於api21,會直接全量下載動態模組。

配置動態更新資訊.png

建立後生成的AndroidManifest.xml檔案,其中dist中的內容是用於動態更新配置的。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dist="http://schemas.android.com/apk/distribution"
    package="com.hotflyer.bussiness">

    <dist:module
        dist:onDemand="true"
        dist:title="@string/title_bussiness">
        <dist:fusing dist:include="true" />
    </dist:module>
</manifest>
複製程式碼

程式碼分析

1.最重要使用到的庫是play:core,這個庫是使用動態更新提供介面

api 'com.google.android.play:core:1.3.0'
複製程式碼

2.依賴關係如圖

image.png
載入到模組的時候使用到SplitInstallRequest

    // Create request to install a feature module by name.
        val request = SplitInstallRequest.newBuilder()
                .addModule(name)
                .build()

        // Load and install the requested feature module.
        manager.startInstall(request)
複製程式碼

3.新增載入監聽

/** Listener used to handle changes in state for install requests. */
    private val listener = SplitInstallStateUpdatedListener { state ->
        val multiInstall = state.moduleNames().size > 1
        state.moduleNames().forEach { name ->
            // Handle changes in state.
            when (state.status()) {
                SplitInstallSessionStatus.DOWNLOADING -> {  //網路拉取動態模組
                    //  In order to see this, the application has to be uploaded to the Play Store.
                    displayLoadingState(state, "Downloading $name")
                }
                SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> {  //需要使用者確認選項(如更新play商店)
                    /*
                      This may occur when attempting to download a sufficiently large module.

                      In order to see this, the application has to be uploaded to the Play Store.
                      Then features can be requested until the confirmation path is triggered.
                     */
                    startIntentSender(state.resolutionIntent().intentSender, null, 0, 0, 0)
                }
                SplitInstallSessionStatus.INSTALLED -> {  //成功下載回撥
                    onSuccessfulLoad(name, launch = !multiInstall)
                }

                SplitInstallSessionStatus.INSTALLING ->   //安裝中
                      displayLoadingState(state, "Installing $name")
                SplitInstallSessionStatus.FAILED -> {    //安裝失敗
                    Log.e(TAG, "Error: ${state.errorCode()} for module ${state.moduleNames()}")
                }
            }
        }
    }

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        manager = SplitInstallManagerFactory.create(this)  //拆分app管理
        initializeViews()
    }

    override fun onResume() {
        // Listener can be registered even without directly triggering a download.
        manager.registerListener(listener)   //註冊監聽
        super.onResume()
    }

    override fun onPause() {
        // Make sure to dispose of the listener once it's no longer needed.
        manager.unregisterListener(listener)  //銷燬監聽
        super.onPause()
    }
複製程式碼

4.跳轉到相應關係,因為依賴關係的問題,只能通過包名跳轉。

  /** Launch an activity by its class name. */
    private fun launchActivity(className: String) {
        Intent().setClassName(packageName, className)
                .also {
                    startActivity(it)
                }
    }
複製程式碼

但是熟悉跳轉邏輯,應該會明白這種情況,可以做一個適配的路由也是可以正常跳轉的。

5.還能動態移除模組

 /** Request uninstall of all features. */
    private fun requestUninstall() {

        toastAndLog("Requesting uninstall of all modules." +
                "This will happen at some point in the future.")

        val installedModules = manager.installedModules.toList()
        manager.deferredUninstall(installedModules).addOnSuccessListener {
            toastAndLog("Uninstalling $installedModules")
        }
    }
複製程式碼

6.需要注意的是動態新增native so需要使用SplitInstallHelper.loadLibrary載入

 SplitInstallHelper.loadLibrary(this, "hello-jni")
複製程式碼

7.base module中需要配置dynamicFeature,才會編譯AndroidManifest.xml中方向合併Dynamic library的AndroidManifest.xml

android{
    dynamicFeatures = [':features:kotlin',
                       ':features:java',
                       ':features:native',
                       ':features:assets', ":bussiness"]
}
複製程式碼

在build/intermediates/merged_manifests中可以找到合併的AndroidManifest

8.在build/intermediates/feature_set_metadata中可以看到feature-metadata.json 上面是有一些Dynamic library的資訊的

9.play core的庫是經過混淆的,除了個別幾個對外使用的檔案外,全部混淆,部分原理只能參照國內的外掛化了。 提供了SplitCompat.install的方式安裝,但是最低只相容到api19,即4.4

10.需要配置Application,有兩種方式,繼承SplitCompatApplication,或者使用SplitCompat.install(this)

11.編譯方式如圖,會生成.aab格式的檔案

編譯.png

注意點

1.因為會將四大元件資訊都預先註冊到合併的base AndroidManifest當中,那麼無法新增四大元件,暫時不能像國內這樣熱更新。 2.比較適合使用的場景是熱修復和地區適配場景。 3.動態library也是需要上傳給google稽核的

相比於普通的元件化架構,其啟動入口是從base 的Application當中,其他的動態模組都需要下載後才能使用。

那麼就需要一個啟動載入畫面下載App module和重要的module,然後再使用後臺下載載入其他內容,然後模組跳轉前,需要捕獲異常和預判定模組載入是否完成,保證程式不會崩潰。

……2018.7.23更新…… google store 釋出應用,需要選擇是否使用App Bundle,如果使用了,那麼將會重新去打包檔案。會造成,你在google市場上下載的包和在其他市場中下載的包,包簽名不一致,無法覆蓋安裝。如果在國內渠道釋出,將無法用google市場中的app覆蓋安裝。 如果保持簽名選退出計劃,如果使用Android app bundle 選繼續

簽名方式提示.png
詳細說明
image.png

這個只有一次機會,選擇之後,不會存在更改了,所以請慎重。

群1已滿,可以進群2學習元件化

元件化交流群2

相關文章