Flutter 應用熱更新

Flutter 熱更新簡介

所謂熱更新,指的是當應用代碼出現缺陷問題時,不須要從新打包提交App Store便可完成缺陷的修復。衆所周知,使用原生技術開發的應用體驗雖然好,但開發、上線週期長也經常被詬病,特別是當應用出現線上問題時,不得不從新打包發佈,大大的影響了用戶體驗,而熱更新技術就是爲有效解決線上缺陷而提出的。android

不過,熱更新雖然具備很大的優勢,可是濫用熱修復也會給應用帶來很差的體驗,而且蘋果對於熱更新和修復是明令禁止的,因此熱更新主要針對的是國內Android市場。目前,Flutter對外開放的SDK是不支持熱更新的,可是在Flutter的源碼裏有一部分預埋的熱更新相關的代碼,能夠經過一些必要的手段在Android端實現動態更新功能。編程

衆所周知,不管是新建立的Flutter項目,仍是原生工程以Moudle或者aar的方式集成Flutter,最終Flutter在原生Android端應用中都是以混合的形式存在的。因此,當咱們拆開一個Flutter在release模式下編譯生成的aar包時,其目錄結構下圖所示。bash

在這裏插入圖片描述
實際開發中,只須要關注assets、jni、libs這三個目錄便可,其餘都是原生的殼工程產物。

  • jni:該文件目錄下存放的是libflutter.so文件,該文件是Flutter引擎層的C++實現,提供skia繪製引擎、Dart和Text紋理繪製等支持。
  • libs:該文件目錄下存放的是flutter.jar文件,該文件爲Flutter嵌入層的 Java實現,主要爲Flutter的原生層提供平臺功能支持,好比建立線程。
  • assets:該文件目錄主要用於存放Flutter應用層的資源,包括images、font等。

而衆觀目前全部的Flutter熱更新方案中,其基本原理實現都是同樣的,即經過修改libapp.so的加載路徑,把它替換成開發者本身的libapp_hot.so來實現熱更新。咱們能夠打開io.flutter.embedding.engine.loader包中的FlutterLoader類來查看libapp.so包的加載邏輯。網絡

在原生Android開發中,咱們可使用Tinker、AndFix、Sophix和Robust等熱修復框架來實現應用的熱更新操做。綜合比較,Tinker是一款不錯的熱修復框架,而且它支持修復so文件,既然Flutter熱更新的基本原理是替換libapp.so,那麼咱們就能夠利用Tinker熱更新功能將須要修復的libapp_hot.so送達客戶端,而後再加載libapp_hot.so文件便可實現代碼的熱更新。架構

接入Bugly

在使用Tinker以前,須要咱們在原生Android平臺接入Tinker。不過,在Flutter開發中,咱們能夠經過集成Bugly來集成Tinker,由於Bugly默認集成了Tinker,而且還提供缺陷上報和應用升級等功能,可謂一箭雙鵰。 接入Bugly以前,須要先開通Bugly帳號,並在官網註冊本身的產品,若是尚未帳號,能夠打開Bugly官網註冊一個。使用Android Studio打開Flutter項目的Android工程,而後在根目錄的build.gradle文件中添加以下腳本,以下所示。app

buildscript {

dependencies {
// tinkersupport插件, 其中lastest.release表示拉取最新版本
        classpath "com.tencent.bugly:tinker-support:1.2.0"
    }
}

複製代碼

接着,打開Android工程app目錄下的build.gradle文件,並在android節點和dependencies節點添加以下配置腳本。框架

android {
     …  //省略其餘配置
     defaultConfig {
       ndk {
         //設置支持的so庫架構
         abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
        }
      }
    }

dependencies {
     implementation 'com.android.support:multidex:1.0.3'
     implementation 'com.tencent.bugly:crashreport_upgrade:1.4.2'
     //指定tinker依賴版本
     implementation 'com.tencent.tinker:tinker-android-lib:1.9.14' 
  }

複製代碼

當須要更新升級SDK的時候,只須要更新配置腳本中的版本號便可。接下來,在build.gradle的同級目錄下建立一個名爲tinker-support.gradle的配置文件,並添加以下腳本。異步

apply plugin: 'com.tencent.bugly.tinker-support'

def bakPath = file("${buildDir}/bakApk/")
//填寫每次構建生成的基準包目錄
def baseApkDir = "app-0208-15-10-00"

tinkerSupport {
    //開啓tinker-support插件,默認值true
enable = true

    //指定歸檔目錄,默認值當前module的子目錄tinker
autoBackupApkDir = "${bakPath}"

    //是否啓用覆蓋tinkerPatch配置功能,默認值false
overrideTinkerPatchConfiguration = true

    // @{link tinkerPatch.oldApk }
    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"
baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    //構建基準包和補丁包都要指定不一樣的tinkerId,而且必須保證惟一性
    tinkerId = "base-1.0.1"
    enableProxyApplication = false
    supportHotplugComponent = true
}

tinkerPatch {
    ignoreWarning = false
    useSign = true
    dex {
        dexMode = "jar"
        pattern = ["classes*.dex"]
        loader = []
    }
    lib {
        pattern = ["lib/*/*.so"]
    }
    res {
       pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
       ignoreChange = []
       largeModSize = 100
    }
    packageConfig {
    }
    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
    }
    buildConfig {
        keepDexApply = false
    }
}

複製代碼

在上面的配置腳本中,咱們須要重點關注baseApkDir和tinkerId兩個屬性。其中,baseApkDir表示每次構建生成的基準包目錄,而tinkerId表示構建基準包和補丁包須要指定的惟一標識。而後,再次打開build.gradle文件,在build.gradle文件的頭部引入tinker-support.gradle配置腳本文件,以下所示。ide

//依賴Tinker插件腳本
apply from: 'tinker-support.gradle'

複製代碼

Bugly提供了兩種初始化SDK的方式,區別是是否須要反射Application。首先,自定義一個繼承自TinkerApplication的Application類,以下所示。異步編程

public class SampleApplication extends TinkerApplication {
    public SampleApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL, "com.xzh.hotreload.SampleApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", true);
    }
}

複製代碼

能夠發現,SampleApplication須要傳入四個參數,須要變動的就是第二個參數,表示自定義的ApplicationLike,以下所示。

public class SampleApplicationLike extends DefaultApplicationLike {

    public static final String TAG = "Tinker.SampleApplicationLike";

    public SampleApplicationLike(Application application, int tinkerFlags,
            boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,
            long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }


    @Override
    public void onCreate() {
        super.onCreate();
        // SDK初始化,appId替換成你的在Bugly平臺申請的appId, 調試時,將第三個參數改成true
        Bugly.init(getApplication(), "900029763", false);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        MultiDex.install(base);

        // 安裝tinker
        // TinkerManager.installTinker(this); 替換成下面Bugly提供的方法
        Beta.installTinker(this);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
        getApplication().registerActivityLifecycleCallbacks(callbacks);
    }
}

複製代碼

SampleApplicationLike類中Bugly初始化時須要用到appId,若是沒有能夠到Bugly官網註冊應用,而後系統會生成一個對應的appId,以下圖所示。

在這裏插入圖片描述
因爲Tinker須要開啓MultiDex,因此集成Bugly時還須要在build.gradle配置文件中添加multidex插件纔可使用MultiDex.install()方法。完成上述操做後,還須要在AndroidManifest.xml配置中添加以下配置。

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

//兼容Android N或者以上設備,必須配置FileProvider來訪問共享路徑的文件
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.fileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>

複製代碼

須要說明的是,製做Android正式環境的基準包時,還須要在build.gradle文件中配置簽名信息,以下所示。

android {
     …  //省略其餘配置
     signingConfigs {
        release {
            try {
                storeFile file("./keystore/release.keystore")
                storePassword "testres"
                keyAlias "testres"
                keyPassword "testres"
            } catch (ex) {
                throw new InvalidUserDataException(ex.toString())
            }
        }
    }

複製代碼

當製做簽名正式包時,爲了不由於混淆而形成代碼功能異常,還須要在Proguard混淆文件中增長配置以下來避免混淆SDK。

-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆規則
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
-keep class android.support.**{*;}

複製代碼

到此,原生Android接入Bugly就大致完成了。若是接入過程有任何問題,能夠參考Bugly官網接入文檔

Flutter應用熱更新

接下來,咱們經過 執行Android的熱更新操做以前,須要先製做應用的基準包。執行flutter build apk –release打包命令,或者打開Android Studio右邊的Gradle面板執行打包操做,以下圖所示。

在這裏插入圖片描述
執行assemble操做後,系統會在Flutter項目的build/app/bakApk文件夾下生成對應的基準包,而且每一個正式簽名的的基準包目錄都會包含基準包、混淆配置文件和資源Id文件,以下圖所示。
在這裏插入圖片描述
而後,將生成的app-release.apk正式基準包上傳到Bugly的後臺。當線上版本出現缺陷問題時,就可使用tinker-support插件生成對應的補丁包。 修復代碼裏面的缺陷問題後,還須要修改tinker-support.gradle文件裏面對應的baseApkDir和tinkerId的屬性值,而後才能執行補丁包生成操做,以下圖所示。
在這裏插入圖片描述
執行完補丁包生成操做以後,就會在Flutter工程的根目錄的build/outputs文件夾下生成對應的補丁包文件,以下圖所示。
在這裏插入圖片描述
而後,將生成的帶簽名的補丁包文件上傳到Bugly後臺,選擇補丁的下發範圍,點擊當即下發讓補丁生效,而後從新啓動Flutter應用就能夠看到應用更新後的效果。

注: 本文部分摘自《Flutter應用跨平臺開發實戰》(即將出版) 參考資料:

1,移動跨平臺方案對比:WEEX、React Native、Flutter和PWA
2,Flutter入門與環境搭建
3,Flutter開發之Dart語言基礎
4,Flutter基礎知識
5,Flutter開發之基礎Widgets
6,Flutter 應用程序調試
7,Flutter For Web入門實戰
8,Flutter開發之異步編程
9,Flutter開發之網絡請求
10,Flutter開發之JSON解析
11,Flutter開發之路由與導航
12,Flutter 必備開源項目
13,Flutter 國際化適配實戰
14,Flutter應用集成極光推送
15,Flutter混合開發
16,構建屬於本身的Flutter混合開發框架
17,Flutter 應用性能檢測與優化
18,Flutter異常監測與上報
19,Flutter的Hot Reload是如何作到的
20,Apple爲何不封殺 Flutter,之後會封殺嗎
21,《Flutter in action》開源

相關文章
相關標籤/搜索