Tinker使用
16lz
2021-01-23
Tinker使用
Tinker简介
Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码、So库以及资源,让应用能够在不需要重新安装的情况下实现更新。
tinker官方介绍
使用步骤
1.在项目的build.gradle文件中,添加以下代码:
dependencies { classpath "com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}"}
在gradle.properties文件中配置TINKER_VERSION:
TINKER_VERSION = 1.7.9
2.在app的build.gradle文件中,添加以下代码:
dependencies { provided("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") compile('com.android.support:multidex:1.0.1')}def gitSha() { try { String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim() if (gitRev == null) { throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'") } return gitRev } catch (Exception e) { throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'") }}def bakPath = file("${buildDir}/bakApk/")ext { //for some reason, you may want to ignore tinkerBuild, such as instant run debug build? tinkerEnabled = true //for normal build //old apk file to build patch apk tinkerOldApkPath = "${bakPath}/app-debug-1018-17-32-47.apk" //proguard mapping file to build patch apk tinkerApplyMappingPath = "${bakPath}/app-debug-1018-17-32-47-mapping.txt" //resource R.txt to build patch apk, must input if there is resource changed tinkerApplyResourcePath = "${bakPath}/app-debug-1018-17-32-47-R.txt" //only use for build all flavor, if not, just ignore this field tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"}def getOldApkPath() { return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath}def getApplyMappingPath() { return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath}def getApplyResourceMappingPath() { return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath}def getTinkerIdValue() { return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()}def buildWithTinker() { return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled}def getTinkerBuildFlavorDirectory() { return ext.tinkerBuildFlavorDirectory}if (buildWithTinker()) { apply plugin: 'com.tencent.tinker.patch' tinkerPatch { oldApk = getOldApkPath() ignoreWarning = true useSign = true tinkerEnable = buildWithTinker() buildConfig { applyMapping = getApplyMappingPath() applyResourceMapping = getApplyResourceMappingPath() tinkerId = getTinkerIdValue() keepDexApply = false isProtectedApp = false } dex { dexMode = "jar" pattern = ["classes*.dex", "assets/secondary-dex-?.jar"] loader = [ //use sample, let BaseBuildInfo unchangeable with tinker "tinker.sample.android.app.BaseBuildInfo" ] } lib { pattern = ["lib/*/*.so"] } res { pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] ignoreChange = ["assets/sample_meta.txt"] largeModSize = 100 } packageConfig { configField("patchMessage", "tinker is sample to use") configField("platform", "all") configField("patchVersion", "1.0") } //or you can add config filed outside, or get meta value from old apk //project.tinkerPatch.packageConfig.configField("test1", project.tinkerPatch.packageConfig.getMetaDataFromOldApk("Test")) //project.tinkerPatch.packageConfig.configField("test2", "sample") sevenZip { zipArtifact = "com.tencent.mm:SevenZip:1.1.10"// path = "/usr/local/bin/7za" }}List flavors = new ArrayList<>();project.android.productFlavors.each {flavor -> flavors.add(flavor.name)}boolean hasFlavors = flavors.size() > 0def date = new Date().format("MMdd-HH-mm-ss")android.applicationVariants.all { variant -> 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.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" } } } }}}
注意:
- defaultConfig中配置multiDexEnabled true- ignoreWarning = true而不是false- tinkerId赋一个值或者使用versionName
3.创建MyApplicationLinke,代码如下:
@SuppressWarnings("unused")@DefaultLifeCycle(application = "com.example.tinkerdemo.MyApplication", flags = ShareConstants.TINKER_ENABLE_ALL, loadVerifyFlag = false)public class MyApplicationLinker extends DefaultApplicationLike { public MyApplicationLinker(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) { super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public void onBaseContextAttached(Context base) { super.onBaseContextAttached(base); //you must install multiDex whatever tinker is installed! MultiDex.install(base); //installTinker after load multiDex //or you can put com.tencent.tinker.** to main dex TinkerInstaller.install(this); // Tinker tinker = Tinker.with(getApplication()); }}
在类的注解中有application = “com.example.tinkerdemo.MyApplication”,意思是帮你生成Application,注意包名。生成的MyApplication的位置如图:
4.修复部分的代码:
public class MainActivity extends AppCompatActivity {private TextView mTextView;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.text); mTextView.setText("这是有问题的APK"); //点击TextView,开始修复 mTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //指定补丁包位置,加载补丁包信息;test为补丁包名称,可以定制 TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/test"); } });}}
5.运行打包后,生成如图所示的文件:
6.修改代码,修改app中build.gradle中老版本的信息,即将上图中bakApk中的apk的名称app-debug-0510-15-43-08复制给build.gradle中的tinkerOldApkPath = “${bakPath}/app-debug-0510-15-43-08.apk”
7.使用命令生成新的apk和差异包
在app的目录下,运行gradle tinkerPatchdebug命令。
在app/build/output/tinkerPath/目录下生成差异包,如图所示:
移动端通过后台下载差异包,然后在后台服务中修复。
tinker原理
更多相关文章
- [Android]代码实现ColorStateList及StateListDrawable
- Android 完全退出应用程序实现代码
- android 6.0后usb otg设备不显示在文件管理器中
- 将Activity打包成jar文件
- Eclipse中跟踪调试Android Framework源代码