Android的在线热更新方案的实现
特别需要注意配置合适的Gradle版本
Robust官网:https://github.com/Meituan-Dianping/Robust
Robust问题讨论:https://github.com/Meituan-Dianping/Robust/issues/439
1、Android工程的Gradle配置:
01.Project的build.gradle:
buildscript {
//使用低版本的kotlin
ext.kotlin_version = "1.3.72"
repositories {
google()
jcenter()
}
dependencies {
//只能使用低版本的Gradle
classpath "com.android.tools.build:gradle:3.6.4"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.meituan.robust:gradle-plugin:0.4.99'
classpath 'com.meituan.robust:auto-patch-plugin:0.4.99'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter() // Warning: this repository is going to shut down soon
//必须使用这个maven库
maven { url 'https://jitpack.io' }
}
}
2.app下的build.gradle(添加robust依赖):
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
//下面的版本号都要改
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
.....
//这是需要添加的依赖
implementation 'com.meituan.robust:robust:0.4.99'
implementation 'com.github.tbruyelle:rxpermissions:0.12'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.5'
implementation 'com.android.support:multidex:1.0.3'
3.app下的build.gradle(针对Build Apk[命令:gradlew clean assembleRelease --stacktrace --no-daemon也可以buildApk]时可能的报错):
defaultConfig {
applicationId "com.huye.robusttest3"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
//这里需要添加
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//报错重复androidsupportmultidexversion.txt时添加
packagingOptions {
exclude 'androidsupportmultidexversion.txt'
}
}
4.app下的build.gradle(完整配置):
plugins {
id 'com.android.application'
//打补丁使用auto-patch-plugin
// id 'auto-patch-plugin'
//获取初始版本的Apk使用robust
id 'robust'
id 'kotlin-android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.huye.robusttest3"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
packagingOptions {
exclude 'androidsupportmultidexversion.txt'
}
}
signingConfigs {
debug {
keyAlias "key0"
keyPassword "123456"
storeFile file('robustsign2')
storePassword "123456"
}
release {
keyAlias "key0"
keyPassword "123456"
storeFile file('robustsign2')
storePassword "123456"
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
//这个版本号也要改
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.meituan.robust:robust:0.4.99'
implementation 'com.github.tbruyelle:rxpermissions:0.12'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.5'
implementation 'com.android.support:multidex:1.0.3'
}
2、AndroidManifest.xml需要添加的动态权限:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.huye.robusttest3">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.RobustTest3"
tools:ignore="HardcodedDebugMode">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3、添加robust.xml(注意修改包名):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<switch>
<!--true代表打开Robust,请注意即使这个值为true,Robust也默认只在Release模式下开启-->
<!--false代表关闭Robust,无论是Debug还是Release模式都不会运行robust-->
<turnOnRobust>true</turnOnRobust>
<!--<turnOnRobust>false</turnOnRobust>-->
<!--是否开启手动模式,手动模式会去寻找配置项patchPackname包名下的所有类,自动的处理混淆,然后把patchPackname包名下的所有类制作成补丁-->
<!--这个开关只是把配置项patchPackname包名下的所有类制作成补丁,适用于特殊情况,一般不会遇到-->
<!--<manual>true</manual>-->
<manual>false</manual>
<!--是否强制插入插入代码,Robust默认在debug模式下是关闭的,开启这个选项为true会在debug下插入代码-->
<!--但是当配置项turnOnRobust是false时,这个配置项不会生效-->
<!--<forceInsert>true</forceInsert>-->
<forceInsert>false</forceInsert>
<!--是否捕获补丁中所有异常,建议上线的时候这个开关的值为true,测试的时候为false-->
<catchReflectException>true</catchReflectException>
<!--<catchReflectException>false</catchReflectException>-->
<!--是否在补丁加上log,建议上线的时候这个开关的值为false,测试的时候为true-->
<!--<patchLog>true</patchLog>-->
<patchLog>false</patchLog>
<!--项目是否支持progaurd-->
<proguard>true</proguard>
<!--<proguard>false</proguard>-->
<!--项目是否支持ASM进行插桩,默认使用ASM,推荐使用ASM,Javaassist在容易和其他字节码工具相互干扰-->
<useAsm>true</useAsm>
<!--<useAsm>false</useAsm>-->
<!--针对Java8级别的Lambda表达式,编译为private级别的javac函数,此时由开发者决定是否进行插桩处理-->
<forceInsertLambda>true</forceInsertLambda>
<!-- <forceInsertLambda>false</forceInsertLambda>-->
</switch>
<!--需要热补的包名或者类名,这些包名下的所有类都被会插入代码-->
<!--这个配置项是各个APP需要自行配置,就是你们App里面你们自己代码的包名,
这些包名下的类会被Robust插入代码,没有被Robust插入代码的类Robust是无法修复的-->
<packname name="hotfixPackage">
<name>com.huye.robusttest3</name>
</packname>
<!--不需要Robust插入代码的包名,Robust库不需要插入代码,如下的配置项请保留,还可以根据各个APP的情况执行添加-->
<exceptPackname name="exceptPackage">
</exceptPackname>
<!--补丁的包名,请保持和类PatchManipulateImp中fetchPatchList方法中设置的补丁类名保持一致( setPatchesInfoImplClassFullName("com.meituan.robust.patch.PatchesInfoImpl")),
各个App可以独立定制,需要确保的是setPatchesInfoImplClassFullName设置的包名是如下的配置项,类名必须是:PatchesInfoImpl-->
<patchPackname name="patchPackname">
<name>com.huye.robusttest3</name>
</patchPackname>
<!--自动化补丁中,不需要反射处理的类,这个配置项慎重选择-->
<noNeedReflectClass name="classes no need to reflect">
</noNeedReflectClass>
</resources>
4、创建MainActivity:
class MainActivity : AppCompatActivity() {
lateinit var textView: TextView
override
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById(R.id.content_tv)
val file =
File(externalCacheDir?.absolutePath + File.separator + "robust" + File.separator + "patch ")
if (!file.exists()) {
file.mkdirs()
}
RxPermissions(this)
.request(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
.subscribe { granted ->
}
}
fun getString(): String {
return "这是个Bug"
}
fun loadPatch(view: View) {
PatchExecutor(this, PatchManipulateImp(), object : RobustCallBack {
override fun onPatchListFetched(
result: Boolean,
isNet: Boolean,
patches: MutableList<Patch>?
) {
}
override fun onPatchFetched(result: Boolean, isNet: Boolean, patch: Patch?) {
Log.d("swt", "onPatchFetched")
}
override fun onPatchApplied(result: Boolean, patch: Patch?) {
Log.d("swt", "onPatchApplied")
}
override fun logNotify(log: String?, where: String?) {
Log.d("swt", "logNotify")
}
override fun exceptionNotify(throwable: Throwable?, where: String?) {
Log.d("swt", "exceptionNotify")
}
}).start()
}
@Modify
fun setTV(view: View) {
textView.text = getString()
}
@Add
fun getString1(): String {
Toast.makeText(this, "终于成功了", Toast.LENGTH_SHORT).show()
return "哈哈"
}
}
5、创建PatchManipulateImp补丁执行类:
class PatchManipulateImp : PatchManipulate() {
override fun fetchPatchList(context: Context): MutableList<Patch> {
val patch = Patch()
patch.name = "test"
patch.tempPath =
context.externalCacheDir?.absolutePath + File.separator + "robust" + File.separator + "patch"
patch.localPath =
context.externalCacheDir?.absolutePath + File.separator + "robust" + File.separator + "patch"
patch.patchesInfoImplClassFullName = "com.huye.robusttest3.PatchesInfoImpl"
val list = arrayListOf<Patch>()
list.add(patch)
return list
}
override fun verifyPatch(context: Context?, patch: Patch?): Boolean {
return true
}
override fun ensurePatchExist(patch: Patch?): Boolean {
return true
}
}
6、制作patch补丁的步骤:
01.获取初始版本的Apk,将生成的mapping.text、methodsMap.robust、robust.apkhash放到app下的robust文件夹下面
02.切换build.gradle的插件,开始制作补丁
03.将patch.jar利用adb push放到指定的位置
adb push C:\Users\huye5\Desktop\test\patch.jar /sdcard/Android/data/com.huye.robusttest3/cache/robust/patch_temp.jar
04.加载补丁文件,观看Log日志输入是否正常,出现"apply result true"就是成功了