Tinker简单集成
一:导入依赖
//可选,用于生成application类
provided('com.tencent.tinker:tinker-android-anno:1.9.2')
//tinker的核心库
compile('com.tencent.tinker:tinker-android-lib:1.9.2')
//分包
compile 'com.android.support:multidex:1.0.1'
二:在AndroidManifast中配置:
<application>
<meta-data android:name="TINKER_ID" android:value="tinker_id_b168b32"/>
</application>
三:创建类TinkerManager
public class TinkerManager {
//是否安装
private static boolean isInstalled = false;
private static ApplicationLike app;
/**
* 安装
*/
public static void installTinbker(ApplicationLike applicationLike){
app=applicationLike;
//判断是否安装
if(isInstalled){
return;
}
TinkerInstaller.install(applicationLike);
isInstalled=true;
}
/**
* 添加补丁包
* @param path
*/
public static void addPatch(String path){
if(isInstalled){
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),path);
}
}
/**
* 从ApplicationLike中拿到上下文
*/
public static Context getApplicationContext(){
return app.getApplication().getApplicationContext();
}
}
四:创建Application继承DefaultApplicationLikepublic class CustomApplication extends DefaultApplicationLike{
public CustomApplication(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.installTinbker(this);
}
}
五:给CustomApplication 类添加注解(第四步的类上添加此注解)@DefaultLifeCycle(
application = ".MyTinkerApplication", //application name to generate 应用程序名称生成
flags = ShareConstants.TINKER_ENABLE_ALL) //tinkerFlags above tinkerFlags上面
添加上之后编译一下,AndroidStudio3.0之后会报错Error:Execution failed for task ':app:javaPreCompileDebug'.
> Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor. Please add them to the annotationProcessor configuration.
- tinker-android-anno-1.9.2.jar (com.tencent.tinker:tinker-android-anno:1.9.2)
Alternatively, set android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true to continue with previous behavior. Note that this option is deprecated and will be removed in the future.
See https://developer.android.com/r/tools/annotation-processor-error-message.html for more details.
解决方法 在app的build中 defaultConfig中 添加
javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
解决之后再次编译,会生成一个.MyTinkerApplication不用管在哪里,清单文件中注册一下就可以六:主页面代码操作一波
添加新功能或者修复bug主要是通过一个补丁包,在此点击事件添加一个补丁包,补丁包放在自定义文件夹下
在此自定义文件夹为( /storage/emulated/0/tpatch/)
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button btn_tinker;
private String path="";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取id
btn_tinker = (Button)findViewById(R.id.btn_tinker);
//监听
btn_tinker.setOnClickListener(this);
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
//File.separator 代表一个 /
path=Environment.getExternalStorageDirectory().getAbsolutePath().concat(File.separator).concat("tpatch/");
File file = new File(path);
if(!file.exists()){
file.mkdir();
}
}
}
@Override
public void onClick(View view) {
switch (view.getId()){
//点击添加新功能 或者修复bug
case R.id.btn_tinker:
TinkerManager.addPatch(path.concat("out.apk"));
break;
}
}
}
上面完成之后,就差生成一个补丁包放入我们的手机或模拟器自定义的文件夹下了
开始生成补丁包使用gradle接入
一:Gradle配置
1:在Project下build.gradle中配置
dependencies {
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.2')
}
2:在model的build.gradle 添加 apply tinker插件
apply plugin: 'com.tencent.tinker.patch'
3:在model的build.gradle最下面配置
def bakPath = file("${buildDir}/bakApk/")
ext{
tinkerOldApkPath = "${bakPath}/"
tinkerEnabled = true;
//不开启混淆
//tinkerApplyMappingPath = "${bakPath}/"
tinkerApplyResourcePath = "${bakPath}/"
//多渠道打包
//tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
}
def getOldApkPath() {
return ext.tinkerOldApkPath
}
//是否使用Tinker
def buildWithTinker() {
return ext.tinkerEnabled
}
def getApplyMappingPath() {
return ext.tinkerApplyMappingPath
}
def getApplyResourceMappingPath() {
return ext.tinkerApplyResourcePath
}
def getTinkerIdValue() {
return "tinker_id_b168b32"
}
//多渠道打包
/*def getTinkerBuildFlavorDirectory() {
return ext.tinkerBuildFlavorDirectory
}*/
tinkerPatch{
tinkerEnable = buildWithTinker()
oldApk = getOldApkPath()
ignoreWarning = false
useSign = true
buildConfig{
//不开启混淆不用他
//applyMapping = getApplyMappingPath()
applyResourceMapping = getApplyResourceMappingPath()
tinkerId = getTinkerIdValue()
keepDexApply = false //分包 暂时不用 太小
isProtectedApp = false //加固 暂时不加
}
dex{
dexMode = "jar"
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]
//全路径Application
loader = ["com.geekghost.gp.tinker.MyTinkerApplication"]
}
lib{
pattern = ["lib/*/*.so"]
}
res{
pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = ["assets/sample_meta.txt"]
largeModSize = 100
}
packageConfig{
configField ("version_name","1")
configField ("change_log","添加了一些新功能")
}
}
4:此脚本在最下面复制 比较固定 用于生产老的apk文件
List<String> 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配置完成之后
签名生成一个apk包 把apk包安装,现在安装的是没有修复bug的或者是没有新功能的apk包
三:安装完成后
找到app下 build文件夹下面的 bakApk文件夹 这个文件夹里如果有东西先删除一下,要不然太乱了
如果没有此文件夹就忽略删除这一步
1 首先打开右侧的Project下的Gradle,找到Tasks\build\assembleRelease
双击assembleRelease就会编译一个apk包与r.txt文档
生成在app build bakApk下面
2 或者在项目的outputs\apk下也可以在Terminal(命令行)输入 gradle assembleRelease 代替双击操作
选一即可 现在app build bakApk下面有两个文件 一个apk r.txt
把bakApk的老的apk的名字copy下来把 tinkerOldApkPath = "${bakPath}/" 填好
把bakApk的老的R文件的名字copy下来把tinkerApplyResourcePath = "${bakPath}/" 填好
如下:
ext{
tinkerOldApkPath = "${bakPath}/tinker-release-0118-11-10-48.apk"
tinkerApplyResourcePath = "${bakPath}/tinker-release-0118-11-10-48-R.txt"
}
四:现在就可以写一些新功能或者修复bug的代码了
添加了一些新功能或者修复完bug之后需要 配置一个正式的签名文件 (可以快捷生成)
1:自己手写
android {
signingConfigs {
release {
keyAlias 'key0'
keyPassword '123456'
storeFile file('C:/Users/ZangH/Desktop/Tinker/tk.jks')
storePassword '123456'
}
}
}
2:快捷键生成
右键项目选择 open module settings ; 快捷键是 ctrl+shift+alt+s
选择此app 里的signing 点击+号 信息填上
name = 随便
key Alias = 你的Alias Alias一般默认为key0
key Password = 123456 or 你的密码
Store File = 签名.jks文件
Store Password = 123456 or 你的密码
信息填完再选择 Build Types
debug release选择release
signing config 选择默认
一波操作就完成了,点击ok
随便选一种方式即可
五:生成有新功能的apk包与补丁文件
点击打开右侧的Project下的Gradle,找到Tasks\tinker\tinkerPatchRelease
双击tinkerPatchRelease就会编译一个新的apk包与r.txt文档
在app build bakApk下面
现在app build bakApk下面有4个文件 2个旧的2个新的
到了现在就可以结尾了,只需要把补丁文件放到手机或模拟器中就行了,那么补丁文件在哪里
在app\build\outputs\apk\tinkerPatch\release里
(patch_signed.apk)就是我们的补丁文件
接下来只需把补丁包放入我们定义的修改路径就好了
放入后,运行程序,点击按钮添加补丁文件,程序立马闪退你就成功了,再次运行就行了
那么补丁文件是什么时候生成的? 双击tinkerPatchRelease编译时生成的
六:如果再需要更新呢? 就不用那么麻烦了 因为只需要配置一次就可以了
以后每次更新只需要把下面的两个路径改成上一次最新的路径就行了 路径app build bakApk下面找
ext{
tinkerOldApkPath = "${bakPath}/tinker-release-0118-11-10-48.apk"
tinkerApplyResourcePath = "${bakPath}/tinker-release-0118-11-10-48-R.txt"
}
新代码写完之后,再次操作一下第五步就行了