//project配置
buildscript { dependencies { classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1') } }
//app/build.gradle 配置
dependencies { //optional, help to generate the final application provided('com.tencent.tinker:tinker-android-anno:1.9.1') //tinker's main Android lib compile('com.tencent.tinker:tinker-android-lib:1.9.1') } ... ... apply plugin: 'com.tencent.tinker.patch'
//如下实例
apply plugin: 'com.android.application'
apply plugin: 'com.tencent.tinker.patch'
android {
signingConfigs {
release {
keyAlias 'key0'
keyPassword '123456'
storeFile file('/Users/eric/Downloads/Tinker/app/tinker.jks')
storePassword '123456'
}
}
compileSdkVersion 27
defaultConfig {
applicationId "com.xieast.tinker"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath true
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
//可选,用于生成application类
provided 'com.tencent.tinker:tinker-android-anno:1.9.2'
//tinker的核心库
compile 'com.tencent.tinker:tinker-android-lib:1.9.2'
implementation 'com.android.support:multidex:1.0.1'
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'
}
//没更新前的路径
def bakPath = file("${buildDir}/bakApk/") ext {
tinkerOldApkPath = "${bakPath}/app-release-0605-15-47-00.apk"
}
def getOldApkPath() {
return ext.tinkerOldApkPath
}
//配置
tinkerPatch {
tinkerEnable true
oldApk = getOldApkPath()
ignoreWarning true
useSign true
buildConfig {
tinkerId = "tinker_id_b168b32"
keepDexApply = false
isProtectedApp = false
}
dex {
dexMode = "jar"
pattern = ["classes*.dex", "assets/secondary-dex-?.jar"]
loader = ["com.xieast.tinker.MyTinkerApplication"]
}
lib {
pattern = ["lib/*/*.so"]
}
res {
pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
largeModSize = 100
}
packageConfig {
configField("patchVersion", "1.0")
}
}
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")
}
}
}
}
}
}
project.afterEvaluate {
//sample use for build all flavor for one time
if (hasFlavors) {
task(tinkerPatchAllFlavorRelease) {
group = 'tinker'
def originOldPath = getTinkerBuildFlavorDirectory()
for (String flavor : flavors) {
def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")
dependsOn tinkerTask
def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")
preAssembleTask.doFirst {
String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
// project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"
// project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"
}
}
}
task(tinkerPatchAllFlavorDebug) {
group = 'tinker'
def originOldPath = getTinkerBuildFlavorDirectory()
for (String flavor : flavors) {
def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")
dependsOn tinkerTask
def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")
preAssembleTask.doFirst {
String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"
// project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"
// project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"
}
}
}
}
}
//如果你的应用有一个子类为android.app.Application的类,
那么你需要修改这个类,并将它的所有实现移动到SampleApplicationLike而
不是Application:
现在你应该改变你的Application类,使它成为TinkerApplication的一个子类。
正如你可以从它的API中看到的那样,它是一个没有默认构造函数的抽象类,
所以你必须定义一个无参数构造函数:
建议使用tinker-android-anno生成你Application的,你只需要为你的SampleApplicationLike类添加一个注释
@DefaultLifeCycle(application=".MyTinkerApplication",flags = ShareConstants.TINKER_ENABLE_ALL ) public class MyAppp extends DefaultApplicationLike { public MyAppp(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(getApplication().getApplicationContext()); TinkerManager.insell(this); } }/**
*
TinkerManager 管理类
*/
public class TinkerManager {
private static boolean isInstalled = false;
private static ApplicationLike mApplicationLike;
/**
* 安装tinker
*
* @param applicationLike
*/
public static void install(ApplicationLike applicationLike) {
mApplicationLike = applicationLike;
if (isInstalled) {
return;
}
TinkerInstaller.install(applicationLike);
isInstalled = true;
}
/**
* 加载补丁
*
* @param path
*/
public static void loadPatch(String path) {
if (Tinker.isTinkerInstalled()) {
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), path);
}
}
/**
* 获取上下文
*
* @return
*/
private static Context getApplicationContext() {
if (mApplicationLike != null) {
return mApplicationLike.getApplication().getApplicationContext();
}
return null;
}
}
/**
MainActivity
**/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private TextView txtShow;
private Button btnLoad;
private Button btnNew;
private Button btnNew2;
private String patchDir = "patch";
private String patchName = "tk.apk";
private File patchFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtShow = findViewById(R.id.txt_show);
btnLoad = findViewById(R.id.btn_load_patch);
btnNew = findViewById(R.id.btn_new_function);
btnNew2 = findViewById(R.id.btn_new_gradle);
//获得补丁路径
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File rootDirectory = Environment.getExternalStorageDirectory();
File patchPath = new File(rootDirectory, patchDir);
if (!patchPath.exists()) {
patchPath.mkdirs();
}
patchFile = new File(patchPath, patchName);
}
btnLoad.setOnClickListener(this);
btnNew.setOnClickListener(this);
btnNew2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_load_patch:
//加载补丁
String path = patchFile.getAbsolutePath();
Log.i(TAG, "path: " + path);
TinkerManager.loadPatch(path);
break;
case R.id.btn_new_function:
Toast.makeText(this, "这是新功能", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_new_gradle:
Toast.makeText(this, "这是Gradle打包的", Toast.LENGTH_SHORT).show();
break;
}
}
}