Tinker的继承和使用
Tinker-API概览:https://github.com/Tencent/tinker/wiki/Tinker-API概览
Tinker-自定义扩展:https://github.com/Tencent/tinker/wiki/Tinker-自定义扩展
Tinker案例:
https://github.com/Tencent/tinker/blob/master/tinker-sample-android/app/src/main/java/tinker/sample
添加依赖
//可选,用于生成application类 provided不参与打包
provided('com.tencent.tinker:tinker-android-anno:1.9.1')
//tinker的核心库
compile('com.tencent.tinker:tinker-android-lib:1.9.1')
//支持多分包
compile "com.android.support:multidex:1.0.1"
TinkerManager
public class TinkerManager {
private static boolean isInstall = false;
private static ApplicationLike mAppLike;
/**
* 完成Tinker的初始化
*/
public static void installTinker(ApplicationLike applicationLike) {
mAppLike = applicationLike;
if (isInstall) {
return;
}
TinkerInstaller.install(applicationLike);//完成tinker的初始化
isInstall = true;
}
/**
* 发起补丁修复请求
*/
public static void loadPatch(String path) {
if (Tinker.isTinkerInstalled()) {
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), path);
}
}
//获取上下文
private static Context getApplicationContext() {
if (mAppLike != null) {
return mAppLike.getApplication().getApplicationContext();
}
return null;
}
}
CustomTinkerLike用于生成Application
@DefaultLifeCycle(application = ".MyTinkerApplication",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class CustomTinkerLike extends ApplicationLike {
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);
//支持多分包
MultiDex.install(base);
TinkerManager.installTinker(this);
}
}
grade参数配置
整个项目中添加依赖:
//Tinker
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1')
参数配置:官网接入指南:
https://github.com/Tencent/tinker/wiki/Tinker-接入指南
有相关说明,可以直接拷贝官网提供的指南https://github.com/Tencent/tinker/blob/master/tinker-sample-android/app/build.gradle
apply plugin: 'com.android.application'
def javaVersion = JavaVersion.VERSION_1_7
def bakPath = file("${buildDir}/bakApk/")
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "com.peakmain.tinker"
minSdkVersion 19
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
compileOptions {
sourceCompatibility javaVersion
targetCompatibility javaVersion
}
signingConfigs {
signingConfigs {
try {
keyAlias 'Tinker'
keyPassword '123456'
storeFile file('E:/Android/FestEc/tinker/src/keystore/release.jks')
storePassword '123456'
} catch (ex) {
throw new InvalidUserDataException(ex.toString())
}
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
//可选,用于生成application类 provided不参与打包
provided("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}"){changing=true}
//tinker的核心库
compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}"){changing=true}
//支持多分包
compile "com.android.support:multidex:1.0.1"
}
ext {
tinkerEnabled = true
tinkerOldApkPath = "${bakPath}/"
tinkerID = "1.0"
tinkerApplyMappingPath = "${bakPath}/"
tinkerApplyResourcePath = "${bakPath}/"
}
def buildWithTinker() {
return ext.tinkerEnabled;
}
def getOldApkPath() {
return ext.tinkerOldApkPath
}
def getApplyMappingPath() {
return ext.tinkerApplyMappingPath
}
def getApplyResourceMappingPath() {
return ext.tinkerApplyResourcePath
}
def getTinkerIdValue() {
return ext.tinkerID
}
if (buildWithTinker()) {
//启用tinker
apply plugin: 'com.tencent.tinker.patch'
tinkerPatch {
oldApk = getOldApkPath()//指定old apk的路径
ignoreWarning = false//是否忽略警告,false表示不忽略tinker的警告,有警告则中止文件的生成
useSign = true//强制使用签名
tinkerEnable = buildWithTinker()//指定是否启用tinker
buildConfig {
applyMapping = getApplyMappingPath()//指定old apk打包时所使用的混淆文件
applyResourceMapping = getApplyResourceMappingPath()//指定old apk的资源文件
tinkerId = getTinkerIdValue()//指定Tinker的id
keepDexApply = false
}
dex {
dexMode = "jar"
//指定dex文件目录
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]
loader = [
"com.peakmain.tinker.MyTinkerApplication"//指定加载patch文件时用到的类
]
}
lib {
pattern = ["lib/*/*.so"]
}
res {
pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
//指定tinker可以修改的资源路径
ignoreChange = ["assets/sample_meta.txt"]//指定不受影响的资源路径
largeModSize = 100//资源修改的默认大小
}
packageConfig {
configField("patchMessage", "tinker is sample to use")
configField("platform", "all")//平台
configField("patchVersion", "1.0")//版本号
}
}
List flavors = new ArrayList<>();
project.android.productFlavors.each { flavor ->
flavors.add(flavor.name)
}
boolean hasFlavors = flavors.size() > 0
def date = new Date().format("MMdd-HH-mm-ss")
/**
* bak apk and mapping
*/
android.applicationVariants.all { variant ->
/**
* task type, you want to bak
*/
def taskName = variant.name
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
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"
into destPath
rename { String fileName ->
fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
}
}
}
}
}
}
}
gradle.properties添加
TINKER_VERSION=1.9.1
修改整个项目的build
//Tinker
classpath ("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}"){changing=true}
生成签名apk之后修改builder中ext
build之后选择tinkerPatchRelease
此时会生成tinkerPatch
有时候会报错:使用Tinker can't the get signConfig for this build,这个代表你没有添加签名配置,可是代码明明添加了,还是报错,解决办法
buildTypes {
release {
minifyEnabled true
//添加这一行
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
此时就已经完成生成patch文件
TinkerService进行后台下载
public class TinkerService extends Service {
private static final String FILE_END = ".apk";//文件后缀名
private static final int DOWNLOAD_PATCH = 0x01;
private static final int UPDATE_PATCH = 0x02;
private String mPatchFileDir;//patch要保存的文件夹
private String mFilePatch;//patch文件保存的路径
private BasePatch mBasePatchInfo;//服务器patch信息
@Override
public void onCreate() {
super.onCreate();
init();
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_PATCH:
checkPatchInfo();
break;
case DOWNLOAD_PATCH:
downloadPatch();
break;
}
}
};
private void downloadPatch() {
mFilePatch=mPatchFileDir.concat(String.valueOf(System.currentTimeMillis()))
.concat(FILE_END);
RequestCenter.downloadFile(mBasePatchInfo.data.downloadUrl, mFilePatch, new DisposeDownloadListener() {
@Override
public void onProgress(int progrss) {
}
@Override
public void onSuccess(Object responseObj) {
TinkerManager.loadPatch(mFilePatch);
}
@Override
public void onFailure(Object reasonObj) {
stopSelf();
}
});
}
private void checkPatchInfo() {
RequestCenter.requestPatchUpdateInfo(new DisposeDataListener() {
@Override
public void onSuccess(Object responseObj) {
mBasePatchInfo = (BasePatch) responseObj;
mHandler.sendEmptyMessage(DOWNLOAD_PATCH);
}
@Override
public void onFailure(Object reasonObj) {
stopSelf();
}
});
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//检查是否有patch更新
mHandler.sendEmptyMessage(UPDATE_PATCH);
return START_NOT_STICKY;//被系统回收将不再重启
}
private void init() {
mPatchFileDir = getExternalCacheDir().getAbsolutePath() + "/tpatch";
File patchFileDir = new File(mPatchFileDir);
try {
if (patchFileDir == null || !patchFileDir.exists()) {
patchFileDir.mkdir();
}
} catch (Exception e) {
e.printStackTrace();
//无法正常创建,停止服务
stopSelf();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}