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:

  1. Gradle compiler plugins:tinker-patch-gradle-plugin
  2. Core SDK library:tinker-android-lib
  3. 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:

  1. 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.
  2. Robust compatibility and success rate are high, but like AndFix, it cannot add variables and classes and can only be used as bugFix schemes.
  3. 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:

  1. Tinker does not support the modification of androidmanifest.xml, Tinker does not support the addition of four components; // If you want to
  2. Due to Google Play’s developer terms, it is not recommended to dynamically update code in the GP channel;
  3. On Android N, the patch has a slight impact on app startup time;
  4. Some Samsung Android-21 models are not supported, and will be actively thrown when loading patches"TinkerRuntimeException:checkDexInstall failed";
  5. 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, addtinker-patch-gradle-pluginThe 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 NowtinkerId 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/tinkerPatchIn 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, butdexModeA 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.txtFiles remain ResId allocated so that not onlyYou can reduce the size of the patch packAnd 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 thekeepDexApplyMode, 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

Source locationgithub