Reprint please indicate the source: blog.csdn.net/qq_33408235… I am a github:github.com/niezhiyang
1. Overview of hot repair
To put it bluntly, hot repair means “patching”. For example, when your company launches an app, users respond that there is a major bug and urgent repair is needed. As usual, programmers work overtime to fix bugs, test them, repackage them, and release them. The problem is high cost and low efficiency. And that’s where thermal repair comes in. Replace buggy code with bug-free code downloaded from the web through a pre-defined interface. This saves much trouble, user experience is good.
2. Overview of Tinker (this introduction is taken from the official website)
Tinker is wechat’s official Hot patch solution for Android. It supports dynamic delivery of code, So library and resources, So that apps can be updated without the need for reinstallation. Of course, you can also use Tinker to update your plugin.
It mainly includes the following parts:
- Gradle compiler plugins:
tinker-patch-gradle-plugin
- Core SDK library:
tinker-android-lib
- Command line versions for non-Gradle compiling users:
tinker-patch-cli.jar
2.1 Comparison between Tinker and Ali’s AndFix, Meituan’s Robust and QZone
Tinker | QZone | AndFix | Robust |
---|---|---|---|
Class to replace | yes | yes | no |
So to replace | yes | no | no |
Resources to replace | yes | yes | no |
Full platform support | yes | yes | yes |
Effective immediately | no | no | yes |
Performance loss | smaller | larger | smaller |
Patch pack size | smaller | larger | general |
The development of transparent | yes | yes | no |
The complexity of the | The lower | The lower | complex |
Gradle support | yes | no | no |
Rom volume | larger | smaller | smaller |
The success rate | higher | higher | general |
In summary:
- As a native solution, AndFix first faces stability and compatibility problems. More importantly, it cannot realize class replacement, which requires a lot of extra development costs.
- Robust compatibility and success rate are high, but like AndFix, it cannot add variables and classes and can only be used as bugFix schemes.
- Qzone solution can release product functions, but its main problems are the performance problems of Dalvik caused by piling, and the rapid increase of patch packages in order to solve the memory address problem under Art.
Especially since Android N, it has not been easy to fix the various solutions on the market due to the inline policy changes of mixed compilation. Tinker hotfix not only supports class, So, and resource replacement, it also supports 2.x to 7.x platform. Tinker can not only be used as a Bugfix, but also as a replacement for feature publishing. Tinker already runs on wechat’s hundreds of millions of Android devices, so why don’t you use it?
2.2 Known problems of Tinker
Due to principle and system limitations, Tinker has the following known problems:
- Tinker does not support the modification of androidmanifest.xml, Tinker does not support the addition of four components; // If you want to
- Due to Google Play’s developer terms, it is not recommended to dynamically update code in the GP channel;
- On Android N, the patch has a slight impact on app startup time;
- Some Samsung Android-21 models are not supported, and will be actively thrown when loading patches
"TinkerRuntimeException:checkDexInstall failed"
; - Modifying remoteView is not supported for resource replacement. Examples are the Transition animation, the Notification icon, and the desktop icon
3. Tinker Access Guide
In fact, the official website has said almost, just want to practice, and the pit to everyone to fill up, there are two access methods
- Gradle access (recommended by the official website and also introduced by this article)
- Command line access (not covered here)
3.1 Adding a Dependency
- In build.gradle in the root directory, add
tinker-patch-gradle-plugin
The dependence of
dependencies {
classpath 'com. Android. Tools. Build: gradle: 2.3.3'
classpath ('com. Tencent. Tinker: tinker - patch - gradle - plugin: 1.8.0 comes with')/ / pluggin tinker
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}Copy the code
- Add dependencies in your app’s build.gradle
dependencies {
// Optional, used to generate the Application class
provided('com. Tencent. Tinker: tinker - android - anno: 1.8.0 comes with')
// Tinker's core library
compile('com. Tencent. Tinker: tinker - android - lib: 1.8.0 comes with')
// The subcontract library can be used below
compile 'com. Android. Support: multidex: 1.0.1'
}Copy the code
- Add a plugin dependency to the top of your app’s build.gradle
/ / the apply tinker plug-in
apply plugin: 'com.tencent.tinker.patch'Copy the code
- An error will be reported after sync Now
tinkerId is not set!!!
(Don’t worry.)
3.2 Configuration parameters and introduction of Build. gradle in APP
3.2.1 Explain the meaning of parameters
We call the original APK package the benchmark APK package. TinkerPatch directly uses the benchmark APK package to make differences with the newly compiled APK package to obtain the final patch package. Gradle configuration parameters are explained as follows:
parameter | The default value | describe |
---|---|---|
tinkerPatch |
Configuration items related to global information | |
tinkerEnable | true | Whether to enable tinker. |
oldApk | null | The path of the benchmark APK package must be entered, otherwise an error will be reported. |
newApk | null | This parameter is optional. It is used to compile the patch APK path. If the path is valid, the new installation package is not compiled, and oldApk and newApk are directly compiled. |
outputFolder null | Optional, set the compilation output path. The default inbuild/outputs/tinkerPatch In the |
|
ignoreWarning | false | If the following occurs and ignoreWarning is false, we break compilation. These situations may lead to risks for the compiled patch package: 1. MinSdkVersion is less than 14, but dexMode A value of “raw”;2. Four new components (Activity, BroadcastReceiver…) appear in the newly compiled installation package. ; 3. Classes defined in dex.loader for loading patches are not in main dex. 4. The class defined in dex.loader for loading patches is modified. 5. Resources.arsc changed, but not compiled with applyResourceMapping. |
useSign | true | During the run, we need to verify that the signature of the benchmark APK package is consistent with that of the patch package, and that we need to sign it for you. |
buildConfig |
Compile the relevant configuration items | |
applyMapping | null | Optional parameter; When compiling the new APK, we want to reduce the size of the patch pack by keeping the proGuard obfuscation of the old APK. This is just a recommendation,Not setting applyMapping will also not affect any Assemble compilation . |
applyResourceMapping | null | Optional parameter; When compiling the new APK, we want to pass the old APKR.txt Files remain ResId allocated so that not onlyYou can reduce the size of the patch pack And at the same timeAvoid abnormal remote View due to ResId change . |
tinkerId | null | At run time, we need to verify that the tinkerId of the benchmark APK package is equal to the tinkerId of the patch package. This is to determine which base packages the patch package will run on. Generally we can use git version number, versionName, and so on. |
keepDexApply | false | If we have more than one dex, we may have more changes when compiling the patch due to class movement. If you open thekeepDexApply Mode, the patch pack will be compiled according to the class distribution of the base pack. |
isProtectedApp | false | Whether to use hardened mode and only patch the changed classes.Note that this mode can only be used in hardened applications. |
dex |
Dex Indicates configuration items related to dex | |
dexMode | jar | Can only be ‘raw’ or ‘JAR’. For ‘raw’ mode, we will keep typing dex. For the ‘jar’ mode, we will recompress the input dex into a JAR. If your minSdkVersion is less than 14, you must select ‘JAR’ mode, which saves more storage space but takes longer to verify md5 than ‘RAW’ mode. By default we do not verify MD5, generally select JAR mode. |
pattern | [] | Dex paths need to be processed. Asterisk (*) and? Wildcard characters must be separated by a ‘/’. The path is relative to the installation package, such as assets/… |
loader | [] | This is important because it defines which classes will be used when loading the patch pack. These classes cannot be modified by Tinker and must be placed in the Main dex. The classes to be defined here are: 1. Your own Application class; 2. Tinker used to load the patch package part of the library classes, namely com. Tencent. Tinker. Loader. *; 3. If you have a custom TinkerLoader, add it and all classes it references to the Loader. 4. Other classes you don’t want to change, such as the BaseBuildInfo class in Sample.It is important to note that the direct reference classes of these classes also need to be added to the Loader. Or you need to make this class non-preVerify. 5. For versions later than 1.7.6, parameters 1 and 2 are automatically filled in. |
lib |
Lib configuration items | |
pattern | [] | Need to handle lib path, support *,? Wildcard characters must be separated by a ‘/’. Consistent with dex.pattern, the path is relative to the installation package, such as assets/… |
res |
Res configuration items | |
pattern | [] | The res path needs to be processed. *,? Wildcard characters must be separated by a ‘/’. Consistent with dex.pattern, the path is relative to the installation package, such as assets/… .It is important to note that only resources that match pattern are placed in the composite resource bundle. |
ignoreChange | [] | Support *,? Wildcard characters must be separated by a ‘/’. If the pattern of ignoreChange is specified, additions, deletions, and modifications to the file are ignored during compilation.At its most extreme, ignoreChange is similar to the pattern above, in that it completely ignores all resource changes. |
largeModSize | 100 | For the modified resource, if it is larger than largeModSize, we will use the BSdiff algorithm. This reduces the size of the patch pack, but increases the complexity of composition. The default size is 100kb |
packageConfig |
This command is used to generate the package_meta. TXT file in the patch package | |
configField | TINKER_ID, NEW_TINKER_ID | ConfigField (” key “, “value”), by default we automatically read the tinkerId from the Manifest of the base installation package and the new installation package and write the configField automatically. Here, you can define the rest of the information, at run time by TinkerLoadResult. GetPackageConfigByName get corresponding numerical values. However, it is recommended to do this directly by modifying the code, such as BuildConfig. |
sevenZip |
7zip Path configuration item. This parameter must be set to true | |
zipArtifact | null | Such as “com. Tencent. Mm: SevenZip: 1.1.10”, automatically based on attribute to obtain the corresponding machine 7 za file operation, it is recommended to use. |
path | 7za | 7ZA path in the system, for example, /usr/local/bin/7za. The path setting overrides zipArtifact, if neither is set, 7za will be used to try. |
3.2.2 According to mybuild.gradleOr the websitebuild.gradleConfigure build.gradle. In fact, mine is the same as the one on the official website
Build. Gradle: Build. Gradle: build. Gradle: build
3.2.3 Making the signature file corresponds to build.gradle
signingConfigs { release { try { storeFile file(".. /demojks.jks") // .. StorePassword '123456' keyAlias' Demojks' keyPassword '123456'} Catch (ex) {throw new InvalidUserDataException(ex.toString()) } } debug { storeFile file(".. /demojks.jks") storePassword '123456' keyAlias 'demojks' keyPassword '123456' } }Copy the code
3.3 Simulation of online APK production (that is, apK released to the market)
MyApplicationLike extends ApplicationLike, which is not an application, but rewrites the onCreate method as follows: Note that there is an annotation in this class @DefaultLifecycle that the application inside is a custom application, remember to use it in the manifest file
@DefaultLifeCycle(application = ".MyApplication".// This class is really a custom application. Don't forget to use it in androidmanifest.xml
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag =true)
public class MyApplicationLike extends ApplicationLike {
public MyApplicationLike(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);
TinkerInstaller.install(this);
}
@Override
public void onCreate() {
super.onCreate(); }}Copy the code
Deliberately miswrite code in mainactivity. Java, such as a button that will crash when clicked
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // loadPatch loadPatch(); } private void loadPatch() { String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/patch.apk"; File file = new File(path); If (the file. The exists ()) {/ / load the patch TinkerInstaller. OnReceiveUpgradePatch (getApplicationContext (), the path). }else {toast.maketext (this, "patch does not exist ", toast.length_short).show(); } } public void test(View view) { int i=1/0; // The button will crash when clicked.Copy the code
3,3.1 run:
Tinker does not support Instant Run. Go to File – > Settings – > Search for Instant Run and close it
3.3.2 Both Demo. apk and patch APK must be release versions. Change Build Variant to Release version, as shown below
3.3.3 Look at the position below after running, this APK is the APK that you have packaged and published online
3.4 Patch Package ApK Package
- You definitely need to fix the online bug before packing, change the MainActivity when clicking button
public void test(View view) {
// int i=1/0;
Toast.makeText(this."I'm fixed. I'm not broken.", Toast.LENGTH_SHORT).show();
}Copy the code
- Add tinkerOldApkPath in app build.gradle, TinkerApplyMappingPath tinkerApplyResourcePath, such as online apk just now, who is something of the figure 3.3.3, tinkerBuildFlavorDirectory I don’t know what it is. I changed it, too, okay
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-release-1026-17-04-59.apk"
//proguard mapping file to build patch apk
tinkerApplyMappingPath = "${bakPath}/app-release-1026-17-04-59-mapping.txt"
//resource R.txt to build patch apk, must input if there is resource changed
tinkerApplyResourcePath = "${bakPath}/app-release-1026-17-04-59-R.txt"
//only use for build all flavor, if not, just ignore this field
tinkerBuildFlavorDirectory = "${bakPath}/app-release-1026-17-04-59"
}Copy the code
- Open Gradle as shown below and double-click tinkerPatchRelease to compile the patch we want
The patch APK location is shown in the following figure
“Patch_singned_7zip. apk” is the patch package, change the name to patch.apk(corresponding to the previous loading), put it in the root directory of SD card, run the APK on the line again, find it does not crash, the bug is fixed, as shown below