集成Tinker最主要的两个部分:一个是接入文档,另一个是github上的demo,可以将tinker-sample-android单独下载下来,运行,参考里边的配置。集成中遇到了一些问题,记载一下
集成步骤
1.在项目的build.gradle,配置
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath ("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
问题1.如果出现下图的Bug
说明sdk中有方法已经过时,需要将gradle 版本调低一些
2.在app的build.gradle,引入tinker库
dependencies {
.......
//optional, help to generate the final application
//生成application时使用
compileOnly("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") {changing=true}
//tinker's main Android lib
implementation("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}"){changing=true}
implementation "com.android.support:multidex:1.0.3"
}
在gradle.properties里定义TINKER_VERSION=1.9.1,方便管理
3.参考demo和文档,对build.gradle进行配置
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "drag.mandala.com.tinkerdemo"
minSdkVersion 22
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath true
}
}
}
signingConfigs {
release {
try {
storeFile file("test.jks")
storePassword "aaaaaa"
keyAlias "test1"
keyPassword "aaaaaa"
} catch (ex) {
throw new InvalidUserDataException(ex.toString())
}
}
}
buildTypes {
release {
//打开混淆,要不然不能生产mapping文件
minifyEnabled true
//配置signConfig,否则提示can't the get signConfig for this build
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//optional, help to generate the final application
//生成application时使用
compileOnly("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") {changing=true}
//tinker's main Android lib
implementation("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}"){changing=true}
implementation "com.android.support:multidex:1.0.3"
}
def bakPath = file("${buildDir}/bakApk")
ext{
tinkerEnable = true
//tinkerOldApkPath,tinkerApplyMappingPath,tinkerApplyResourceMappingPath初始值为${bakPath},
//打包成功以后,将build/bakApk下生成的文件名放到${bakPath}后,修改 minifyEnabled为 true,要不然不会生产mapping文件
tinkerOldApkPath = "${bakPath}/app-release-1126-14-00-54.apk"
tinkerId = "1.0"
tinkerApplyMappingPath = "${bakPath}/app-release-1126-14-00-54-mapping.txt"
tinkerApplyResourceMappingPath = "${bakPath}/app-release-1126-14-00-54-R.txt"
}
def buildWithTinker(){
return ext.tinkerEnable
}
def getOldApkPath(){
return ext.tinkerOldApkPath
}
def getApplyMappingPath(){
return ext.tinkerApplyMappingPath
}
def getApplyResourceMappingPath(){
return ext.tinkerApplyResourceMappingPath
}
def getTinkerIdValue(){
return ext.tinkerId
}
if(buildWithTinker()){
//启用了tinker
apply plugin: 'com.tencent.tinker.patch'
//所有tinker相关的参数配置
tinkerPatch{
oldApk = getOldApkPath()//指定old apk文件路径
ignoreWarning = false //不忽略tinker的警告,有警告就停止生成patch文件
useSign = true //强制patch文件使用签名
tinkerEnable = buildWithTinker() //指定是否启用tinker
buildConfig{
applyMapping = getApplyMappingPath() //指定old apk打包时所使用的混淆文件
applyResourceMapping = getApplyResourceMappingPath() // 指定old apk的资源文件
tinkerId = getTinkerIdValue() // 指定TinkerId
keepDexApply = false
isProtectedApp = false
supportHotplugComponent = false
}
dex{
dexMode = "jar" //只能是'raw'或者'jar'
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"] // 指定dex文件目录,第二个是官方例子配置
loader = ["drag.mandala.com.tinkerdemo.MyTinkerApplication"] //指定加载patch文件时用到的类
}
lib{
pattern = ["lib/*/*.so"]
}
res{
pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] //指定tinker可以修改的所有资源文件路径
// ignoreChange = ["assets/sample.txt"] //在编译时会忽略该文件的新增、删除与修改
ignoreChange = ["assets/sample_meta.txt"]
largeModSize = 100 //资源修改的默认值 如果大于largeModSize,tinker将使用bsdiff算法。
// 这可以降低补丁包的大小,但是会增加合成时的复杂度。默认大小为100kb
}
//不必须的配置
//说明配置信息
packageConfig{
configField("patchMessage", "tinker is sample to use")
configField("platform", "all")
configField("patchVersion", "1.0")
}
/**
* if you don't use zipArtifact or path, we just use 7za to try
*/
sevenZip {
/**
* optional,default '7za'
* the 7zip artifact path, it will use the right 7za with your platform
*/
zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
/**
* optional,default '7za'
* you can specify the 7za path yourself, it will overwrite the zipArtifact value
*/
// path = "/usr/local/bin/7za"
}
}
List<String> flavors = new ArrayList<>();
project.android.productFlavors.each { flavor ->
flavors.add(flavor.name)
}
//是否配置多渠道
boolean hasFlavors = flavors.size() > 0
/**
* bak apk and mapping
*/
android.applicationVariants.all { variant ->
/**
* task type, you want to bak
*/
def taskName = variant.name
def date = new Date().format("MMdd-HH-mm-ss")
tasks.all {
if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
it.doLast {
copy {
def fileNamePrefix = "${project.name}-${variant.baseName}"
def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"
def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath
if (variant.metaClass.hasProperty(variant, 'packageApplicationProvider')) {
def packageAndroidArtifact = variant.packageApplicationProvider.get()
if (packageAndroidArtifact != null) {
try {
from new File(packageAndroidArtifact.outputDirectory.getAsFile().get(), variant.outputs.first().apkData.outputFileName)
} catch (Exception e) {
from new File(packageAndroidArtifact.outputDirectory, variant.outputs.first().apkData.outputFileName)
}
} else {
from variant.outputs.first().mainOutputFile.outputFile
}
} else {
from variant.outputs.first().outputFile
}
into destPath
rename { String fileName ->
fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
}
from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
into destPath
rename { String fileName ->
fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
}
from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
from "${buildDir}/intermediates/symbol_list/${variant.dirName}/R.txt"
into destPath
rename { String fileName ->
fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
}
}
}
}
}
}
}
问题2: 如果出现下图的提示
需要配置:
**问题3:**出现下图的Bug
在gradle.properties配置android.enableAapt2=true
问题4: 不能生成mapping文件,这个需要打开混淆
minifyEnabled true
如果提示“can’t the get signConfig for this build”,需要配置signConfig
signingConfig signingConfigs.release
完整配置
buildTypes {
release {
//打开混淆,要不然不能生产mapping文件
minifyEnabled true
//配置signConfig,否则提示can't the get signConfig for this build
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
4.写一个继承自DefaultApplicationLike的类
@DefaultLifeCycle(application = ".MyTinkerApplication",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class CustomTinkerLike extends DefaultApplicationLike
{
public CustomTinkerLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent)
{
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
}
@Override
public void onBaseContextAttached(Context base)
{
super.onBaseContextAttached(base);
//you must install multiDex whatever tinker is installed!
MultiDex.install(base);
TinkerManager.setTinkerApplicationLike(this);
TinkerManager.initFastCrashProtect();
//should set before tinker is installed
TinkerManager.setUpgradeRetryEnable(true);
//installTinker after load multiDex
//or you can put com.tencent.tinker.** to main dex
TinkerManager.installTinker(this);
Tinker tinker = Tinker.with(getApplication());
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
getApplication().registerActivityLifecycleCallbacks(callback);
}
}
build一下,AndroidManifest.xml里的<application 的name设置成注解里的application,
<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/AppTheme"
android:name=".MyTinkerApplication">
...
配置权限,否则onReceiveUpgradePatch的时候会闪退
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
5.其他的类可以复制demo里的,别忘了在AndroidManifest.xml配置SampleResultService
6.配置完成以后,先打包
打包成功以后,查看build文件夹,
将这三个文件名赋值到build.gradle中的对应位置
将app-release-1126-14-00-54.apk安装到手机上,作为一个有Bug的app,修改bug,生成patch文件
生成成功以后,继续看build文件夹
这个放到手机对应的目录下,目录地址是onReceiveUpgradePatch方法里的
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");
点击按钮加载patch,杀死进程,重启,就会是修改后的app
问题5: 如果报com.tencent.tinker.build.util.TinkerPatchException: resource must contain resources.arsc pattern,检查build.gradle里的res下的pattern中的"resources.arsc"是否写错。还有就是通过AndroidStudio方式生成差异包的时候如果不小心修改了XML也会报这个错
问题6: onLoadPatchListenerReceiveFail: patch receive fail: /storage/emulated/0/patch_signed_7zip.apk, code: -2
检查文件路径是否和代码里的一致,查看清单文件中是否有添加SD卡访问权限,如果是Android7.0要考虑FileProvider(Android7.0不支持直接访问sd卡)
问题7: Tinker does not support instant run mode, please trigger build by assembleDebug or disable instant run
去掉instance run