简介:
关于热修复的介绍现在网上有很多,所以在此我就不过多BB,此篇博客的特点有两个,首先,这是一个针对Android studio3.0用户的博客,其次,这里采用的是命令行的方式,这是方式在工作中并不经常使用,相反,在工作中基本都是使用gradle配置的方式,但是命令行的方式相比于gradle是简单很多的,所以这就这是一个入门级的tinker集成,意在让大家了解tinker这个相对最为全面的热修复框架(高手请绕路,前方低能)。
1、在gradle.properties中配置我们tinker的版本。这种配置方式就相当于定义一个变量,用于指定我们导入依赖的版本,这样对于同一框架的多个依赖可以进行很好的版本控制。
粘贴板:TINKER_VERSION=1.9.0
2、module中导入依赖,在这里可以看到我们刚刚定义的变量在这里得以使用
粘贴板:
//可选,用于生成application类
implementation("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
//tinker的核心库
annotationProcessor("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
compileOnly("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
implementation "com.android.support:multidex:1.0.2"
3、创建一个管理类,来管理我们Tinker中所有的api,方便我们的使用和以后对其他工程的植入。代码都有注释,这里就直接上代码了
package com.jiaxin.tinkerthree.tinker;
import android.content.Context;
import com.tencent.tinker.lib.tinker.Tinker;
import com.tencent.tinker.lib.tinker.TinkerInstaller;
import com.tencent.tinker.loader.app.ApplicationLike;
/**
* Created by Zzw on 2017/12/24.14:06
*
* @function 对Tinker的所有api做一层封装
*/
public class TinkerManger {
//判断tinker是否已经初始化
private static boolean isInstalled = false;
private static ApplicationLike mAppLike;
/**
* 由外部调用完成tinker 的初始化
* @param applicationLike
*/
public static void instaillTinker(ApplicationLike applicationLike){
mAppLike = applicationLike;
if (isInstalled){
return;
}
TinkerInstaller.install(mAppLike);//完成tinker初始化
isInstalled = true;
}
/**
* 完成patch文件加载
* @param path
*/
public static void loadPatch(String path){
if (Tinker.isTinkerInstalled()){
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), path);
}
}
/**
* 通过applicationLike获取context
* @return
*/
private static Context getApplicationContext(){
if (mAppLike != null){
return mAppLike.getApplication().getApplicationContext();
}
return null;
}
}
3、根据官方的要求我们要使用ApplicationLike这个类,来生成application,所以我们这里创建一个类来继承他
package com.jiaxin.tinkerthree.tinker;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.support.multidex.MultiDex;
import com.tencent.tinker.anno.DefaultLifeCycle;
import com.tencent.tinker.loader.app.ApplicationLike;
import com.tencent.tinker.loader.shareutil.ShareConstants;
/**
* Created by Zzw on 2017/12/24.14:11
*/
@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);//使应用支持分包
TinkerManger.instaillTinker(this);
}
}
注意这里我们使用了 一个注解,注解中后面两个参数可以认为是固定参数,第一个是我们指定的生成Application类的名字,这里我们叫他"MyTinkerApplication"
以上配置都完毕后我们编译整个工程,tinker的注解就会为我们生成一个MyTinkerApplication类。
4、清单文件中配置
这里的配置就是指定application为我们刚刚生成的"MyTinkerApplication",还有一个TINKER_ID的配置,这个主要是因为tinker在加载补丁文件的时候会只能的判断当前版本和补丁版本是否一致,如果不一致就会认为此补丁文件无效,就不会加载,所以我们这里做测试的话两次apk生成的时候只要这里不该动就好了
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jiaxin.tinkerthree">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
<!-- 此处指定application为tinker为我们生成的application -->
android:name=".tinker.MyTinkerApplication"
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">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}" />
<!-- 此处为tinker配置当前的应用版本 -->
<meta-data
android:name="TINKER_ID"
android:value="tinker_id_123456"
/>
</application>
</manifest>
5、以上步骤完成后,我们tinker的配置工作基本也就完成了,接下来只要使用我们的tinker管理类,生成两个不同版本的apk即可,这里因为tinker是支持布局文件的修改的,所以我模拟了点击一个按钮增加一个按钮的功能, 下面是我activity中的使用,布局就不写了哈
package com.jiaxin.tinkerthree;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.jiaxin.tinkerthree.tinker.TinkerManger;
import java.io.File;
public class MainActivity extends AppCompatActivity {
//log key
public static final String TAG = "myLog";
//file's end name
private static final String FILE_END = ".apk";
//patch file's path
private String mPatchDir;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPatchDir = Environment.getExternalStorageDirectory().getAbsolutePath();
Log.d(TAG, "onCreate: " + mPatchDir);
File file = new File(mPatchDir);
if (file.exists()){
Toast.makeText(this, "文件存在", Toast.LENGTH_SHORT).show();
}else{
boolean mkdirs = file.mkdir();
Toast.makeText(this, mkdirs ? "创建成功" : "创建失败", Toast.LENGTH_SHORT).show();
}
}
/**
* @click
* @param view
* load tinker patch for application
*/
public void loadPatch(View view){
TinkerManger.loadPatch(getPathName());
}
//get tinker patch fileName
private String getPathName() {
return mPatchDir.concat("/tinker").concat(FILE_END);
}
}
6、好,接下来我们只要改变布局文件生成两个版本不同的apk即可,我这里是在新的apk中添加了一个按钮,所以old版本的apk只有一个按钮,用于点击加载补丁文件,new版本的apk中在第一个按钮的基础上又添加了一个按钮。
7、两个apk版本生成成功后接下来就是生成补丁apk,我们采用的是命令行的方式,所以当然是要使用tinker为我们提供的工具,我们将所有要使用到的文件和新旧两个版本的apk文件以及jks签名文件放在同一个目录下(建议新建一个文件夹),然后打开命令行工具进入该文件夹,执行jar文件:
java -jar tinker-patch-cli-1.7.7.jar
可以看到tinker提示我们该命令需要一堆参数,可以看到新旧版本apk文件我们已经准好了,只差一个tinker_config文件,我们打开这个文件,修改其中的loader为我们的application的名字,修改issue配置为我们签名的配置和密码
8、配置完毕后,打开命令行工具,指定好参数在执行刚刚的命令就可以在output文件夹中看到tinker为我们生成的apk文件,将patch_signed.apk(签名版的)这个文件改为我们一开始指定的文件名称(我这里是tinker.apk),再将其拷贝到我们一开始指定的路径,我这里指定的是sd下默认路径,安装old版本apk整个过程就完成了~~。
9、打开安装好的旧版本apk,点击按钮,程序会闪退,再次打开就会发现后添加的按钮就已经被添加上去了,这里闪退不用担心,这是tinker默认的设置,因为tinker在加载补丁文件完毕后,不重新启动是不会生效的。这个也可以通过配置来更改,具体可以通过查看相关文档来实现。