欢迎访问原文Tinker 热修复框架集成实战
欢迎访问原文Tinker 热修复框架集成实战
欢迎访问原文Tinker 热修复框架集成实战
欢迎访问原文Tinker 热修复框架集成实战
最让移动端开发崩溃的就是,刚强推了一个版本就出了重大 bug,只能重新发版解决 但集成了热修复框架就能迎刃而解,经过对比最终选用了腾讯微信团队的 Tinker 性能还不错,支持的修复场景也多,主要是免费(ಥ_ಥ)
下面介绍一下接入流程
一、接入
推荐 Android Gradle Plugin Version 使用 3.1.4
Gradle Version 使用 4.4
首先在 gradle.properties 配置版本号,方便后期维护
TINKER_VERSION=1.9.13.2
在项目的 build.gradle 配置依赖
classpath "com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}"
在 app 的 build.gradle 配置依赖
compileOnly "com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}"
annotationProcessor "com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}"
implementation "com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}"
二、多渠道配置
//证书配置
signingConfigs {
debug {
storeFile file('../debug.jks')
storePassword ''
keyAlias ''
keyPassword ''
}
release {
storeFile file('../release.jks')
storePassword ''
keyAlias ''
keyPassword ''
}
}
//build 配置
buildTypes {
debug {
minifyEnabled false
useProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
signingConfig signingConfigs.debug
buildConfigField "boolean", "ISDEBUG", "true"
}
release {
minifyEnabled false
useProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
signingConfig signingConfigs.release
buildConfigField "boolean", "ISDEBUG", "false"
}
}
//多渠道配置
productFlavors {
product {
//生产
applicationId "com.kevin.blog"
versionCode 1
versionName '1.0.0'
manifestPlaceholders = [app_name : "@string/app_name"]
}
develop {
//测试
applicationId "com.kevin.develop.blog"
versionCode 1
versionName '1.0.0'
manifestPlaceholders = [app_name : "@string/app_name_test"]
}
}
三、Tinker 配置
配置项含义可到官网查询
apply plugin: 'com.tencent.tinker.patch'
tinkerPatch {
oldApk = ''
ignoreWarning = false
useSign = true
tinkerEnable = true
buildConfig {
applyResourceMapping = ''
tinkerId = ''
keepDexApply = false
isProtectedApp = false
supportHotplugComponent = true
}
dex {
dexMode = "jar"
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]
loader = ["tinker.sample.android.app.BaseBuildInfo"]
}
lib {
pattern = ["lib/*/*.so"]
}
res {
pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = ["assets/sample_meta.txt"]
largeModSize = 100
}
sevenZip {
zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
}
}
四、Gradle 脚本实现自动打包
原创超级方便的脚本,可以把基础包和补丁复制到 tinker 目录下
//productVersion
def productVersionCode = 1
def productVersionName = '1.0.0'
//develop
def developVersionCode = 1
def developVersionName = '1.0.0'
//10 release 20 debug
def buildType = 10
// 1 product 2 develop
def productFlavor = 2
def patchPath = ''
def getPatchPath(){
return patchPath
}
def bakPath = file("../tinker/")
ext {
tinkerEnabled = true
tinkerOldApkPath = ""
tinkerApplyResourcePath = ""
tinkerId = ""
switch (buildType + productFlavor) {
case 11:
tinkerOldApkPath =
"${bakPath}/product_release/TinkerBase${productVersionName}_product_release/TinkerBase${productVersionName}_product_release.apk"
tinkerApplyResourcePath =
"${bakPath}/product_release/TinkerBase${productVersionName}_product_release/TinkerBase${productVersionName}_product_release-R.txt"
tinkerId = "${productVersionName}"
patchPath = "/outputs/apk/product/tinkerPatch/product/release/patch_signed_7zip.apk"
break
case 12:
tinkerOldApkPath =
"${bakPath}/develop_release/TinkerBase${developVersionName}_develop_release/TinkerBase${developVersionName}_develop_release.apk"
tinkerApplyResourcePath =
"${bakPath}/develop_release/TinkerBase${developVersionName}_develop_release/TinkerBase${developVersionName}_develop_release-R.txt"
tinkerId = "${developVersionName}"
patchPath = "/outputs/apk/develop/tinkerPatch/develop/release/patch_signed_7zip.apk"
break
case 21:
tinkerOldApkPath =
"${bakPath}/product_debug/TinkerBase${productVersionName}_product_debug/TinkerBase${productVersionName}_product_debug.apk"
tinkerApplyResourcePath =
"${bakPath}/product_debug/TinkerBase${productVersionName}_product_debug/TinkerBase${productVersionName}_product_debug-R.txt"
tinkerId = "${productVersionName}"
patchPath = "/outputs/apk/product/tinkerPatch/product/debug/patch_signed_7zip.apk"
break
case 22:
tinkerOldApkPath =
"${bakPath}/develop_debug/TinkerBase${developVersionName}_develop_debug/TinkerBase${developVersionName}_develop_debug.apk"
tinkerApplyResourcePath =
"${bakPath}/develop_debug/TinkerBase${developVersionName}_develop_debug/TinkerBase${developVersionName}_develop_debug-R.txt"
tinkerId = "${developVersionName}"
patchPath = "/outputs/apk/product/tinkerPatch/product/debug/patch_signed_7zip.apk"
break
}
}
//文件复制
def date = new Date().format("YYYY-MM-dd_HH-mm-ss")
android.applicationVariants.all { variant ->
def taskName = variant.name
tasks.all {
if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
it.doLast {
def baseName = variant.baseName.replaceAll(/-/, "_")
def fileNamePrefix = "${project.name}-${variant.baseName}"
def newFileNamePrefix = "TinkerBase${variant.productFlavors[0].versionName}_${baseName}"
def destPath = file("${bakPath}/${baseName}/TinkerBase${variant.productFlavors[0].versionName}_${baseName}")
if (destPath != null && !destPath.exists()) {
copy {
from variant.outputs.first().outputFile
into destPath
rename { String fileName ->
fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
}
from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
into destPath
rename { String fileName -> fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
}
}
}
}
}
if ("tinkerPatch${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
it.doLast {
def baseName = variant.baseName.replaceAll(/-/, "_")
def fileNamePrefix = "patch_signed_7zip"
def newFileNamePrefix = "TinkerPatch(Base_${baseName}_${variant.productFlavors[0].versionName})_${date}"
def destPath = file("${bakPath}/${baseName}/TinkerBase${variant.productFlavors[0].versionName}_${baseName}/patch")
copy {
from "${buildDir}/${patchPath}"
into destPath
rename { String fileName ->
fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
}
}
}
}
}
}
}
五、Application 改造
这是比较麻烦的一步,如果你的 application 不是继承 Application 那就麻烦了,需要改造为直接继承 Application
改完之后可以愉快的开始了
首先建立 TinkerApplication 继承 DefaultApplicationLike,代码如下
@SuppressWarnings("unused")
@DefaultLifeCycle(
application = ".MyApplication", //application 类名
loaderClass = "com.tencent.tinker.loader.TinkerLoader",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class TinkerApplication extends DefaultApplicationLike {
private static ApplicationComponent mApplicationComponent;
public static ApplicationComponent getApplicationComponent(){
return mApplicationComponent;
}
public TinkerApplication(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
long applicationStartElapsedTime, long applicationStartMillisTime,
Intent tinkerResultIntent) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime,
applicationStartMillisTime, tinkerResultIntent);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
//把你原来 application 内容移到这里
MultiDex.install(base);
TinkerInstaller.install(this);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
getApplication().registerActivityLifecycleCallbacks(callback);
}
}
注意原 application 中引用 application 的地方改成 getApplication()
然后把 AndroidManifest.xml 里 application 路径改为 @DefaultLifeCycle 注解中声明的路径即可
六、使用
安装
TinkerInstaller.onReceiveUpgradePatch(context, 路径);
卸载
Tinker.with(getApplicationContext()).cleanPatch();
Tinker.with(getApplicationContext()).cleanPatchByVersion(版本号)
杀死应用
ShareTinkerInternals.killAllOtherProcess(getApplicationContext());
Hack 方式修复 so
TinkerLoadLibrary.installNavitveLibraryABI(this, abi);
非 Hack 方式修复 so
TinkerLoadLibrary.loadLibraryFromTinker(getApplicationContext(), "lib/" + abi, 模块名);
TinkerLoadLibrary.loadArmLibrary(getApplicationContext(), 模块名);
TinkerLoadLibrary.loadArmV7Library(getApplicationContext(), 模块名)
1
KevinRed OP (゚Д゚≡゚д゚)!?没人看嘛
|