所谓热更新,指的是当应用代码出现缺陷问题时,不须要从新打包提交App Store便可完成缺陷的修复。众所周知,使用原生技术开发的应用体验虽然好,但开发、上线周期长也经常被诟病,特别是当应用出现线上问题时,不得不从新打包发布,大大的影响了用户体验,而热更新技术就是为有效解决线上缺陷而提出的。android
不过,热更新虽然具备很大的优势,可是滥用热修复也会给应用带来很差的体验,而且苹果对于热更新和修复是明令禁止的,因此热更新主要针对的是国内Android市场。目前,Flutter对外开放的SDK是不支持热更新的,可是在Flutter的源码里有一部分预埋的热更新相关的代码,能够经过一些必要的手段在Android端实现动态更新功能。编程
众所周知,不管是新建立的Flutter项目,仍是原生工程以Moudle或者aar的方式集成Flutter,最终Flutter在原生Android端应用中都是以混合的形式存在的。因此,当咱们拆开一个Flutter在release模式下编译生成的aar包时,其目录结构下图所示。bash
而众观目前全部的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文件便可实现代码的热更新。架构
在使用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,以下图所示。
<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官网接入文档。
接下来,咱们经过 执行Android的热更新操做以前,须要先制做应用的基准包。执行flutter build apk –release打包命令,或者打开Android Studio右边的Gradle面板执行打包操做,以下图所示。
注: 本文部分摘自《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》开源