RobustForSdk

Demonstrate how to use meituan’s Robust for code hot update in SDK development

At first I wanted to do a hot update to the SDK, but my heart said no -_-. After seeing the famous Tinker, Sophix and Robust, BASED on the application scenario and timeliness of SDK, I choose Robust. The following is the introduction of the whole process of SDK to access Robust ~

Access to the process

1.Robust cannot be directly applied in the SDK project

Firstly, complete the configuration by referring to the Robust access guide, which will not be described here

The Module is written in the form of an application

    apply plugin: 'com.android.application'// Auto-patch-plugin plugin plugin com.android.application //apply plugin:'auto-patch-plugin'
    apply plugin: 'robust'Copy the code

It is obvious that the SDK development is the apply plugin: ‘com.android.library’, if compiled, it will give an error similar to the following

Failed to notify project evaluation listener.

Transforms with scopes ‘[SUB_PROJECTS, SUB_PROJECTS_LOCAL_DEPS, EXTERNAL_LIBRARIES]’ cannot be applied to library projects.

Could not find property ‘applicationVariants’ on com.android.build.gradle.LibraryExtension_Decorated@6a5c2d2d.

So we need to think about how to generate jar packages processed by Robust?

SDK/build/outputs/apk/develop/Release/apk/apk/develop/Release/apk/build/outputs/apk/develop/Release

QQ screenshots 20171026151701. PNG

In this case, the resource files and code files produced by APK during the packaging process should be generated in a directory \ SDK \build, so we found the corresponding code JAR files by *.jar search. Gradle-3.0.0&robust-0.4.71 follows the path \ SDK \ Build \intermediates\transforms\ ProGuard \develop\release\0.jar; Gradle-2.3.3 & robust-0.4.7 follows the path \ SDK \ Build \intermediates\ Transforms \ ProGuard \develop\release\jars\3\1f\main.jar, Using the viewing tool, we can see that the Robust has inserted the code required by hot repair in the compilation process

QQ screenshots 20171026152420. PNG

Then other resource files, list files, aar after decompression of the reference standard can be found in the \ SDK \build directory can be found in the other corresponding path, here is no longer described, refer to the SDK directory build.gradle jar package task can be

QQ screenshots 20171026153115. PNG

2. Jar package processing and AAR package

From the above analysis we know the path of jar packages and various resources, so we can step into the process of gradle packaging APK, process the files generated during packaging APK, and then merge them into aar packages to output the specified directory. The following is a multi-channel packaging treatment, explain how to deal with

Configure two variables in gradle.properties to facilitate the switch of SDK packaging mode. The host Module can use different dependency modes according to the variables when developing dependencies

# Application mode, Robust needs to be Application to insert code and patch
isAppModule=true
Enable this to patch in Application mode
isPatchModule=falseCopy the code

Configuration of the Apply Plugin

// Apply plugin indicates that the project will use the specified plugin, the SDK corresponding to com.android.libraryif (isAppModule.toBoolean()) {
    # Application mode, use robust
    apply plugin: 'com.android.application'
    if (isPatchModule.toBoolean()) {
        //制作补丁时将这个打开,auto-patch-plugin紧跟着com.android.application
        apply plugin: 'auto-patch-plugin'
    }
    apply plugin: 'robust'
} else {
    apply plugin: 'com.android.library'
}Copy the code

Configure two channels

// Test options develop {dimension"test"} // Default normal {dimension"test"}}Copy the code

Configure the path specified by the manifest file, because application and Library manifest files are clearly different

    sourceSets {main {// specify the jni file source folder libs jnilibs. srcDirs = ['libs'] // Application and Library manifest files are handled differentlyif (isAppModule.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'}}}Copy the code

Dependent configuration

Dependencies {compileOnly fileTree(dir:'libs', include: ['*.jar'])

    androidTestImplementation('com. Android. Support. Test. Espresso: espresso - core: 2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    testImplementation 'junit: junit: 4.12'// Robust is entered into jar package during patch production, and the host is not required to compile, so the SDK hot repair has no awareness of implementation for the host'com. At meituan. Robust: robust: 0.4.71'// Remote dependency on aar does not support provided or compileOnly, so you need to filter implementation when jar your task'com. Orhanobut: logger: 2.1.1'// same with implementation dependency logger'cn. Bmob. Android: bmob - SDK: 3.5.8'// secret.jar jar package output by secret module, directly into SDK module JAR package, use API instead of implementation because app module has a direct use of secret in the method, so expose this dependency API files('libs/secret.jar')}Copy the code

The custom task is packaged to involve the project in the process of making the release version of the APK package

Pattern p = pattern.compile (// The assemble(channels) release command is called for the project to release the apK package, so we can use regular matching to match all channels' release process."^assemble(.*)Release\$"WhenTaskAdded {task ->if(! Isappmodule.toboolean ()) {// Application mode is not handledreturnAssemble (channel)Release assemble task Matcher m = p. Matcher (task.name)if{String flavor = m.group(1);if(flavor.length() > 1) {// Channel naming fix, lowercase, For example, assembleDevelopRelease flavor = flavor.substring(0, 1).tolowerCase () + flavor.substring(1)} And generate aar package task.dolast {delete {// delete the last generated file directory, The directory is \ SDK \robustjar\(channel)\release Delete projectDir.tostring () + file.separator +'robustjar' + File.separator + flavor + File.separator + "release"} // The parent directory where the required resources are packaged, \ SDK \build\intermediates String intermediatesPath = buildDir.toString() + file.separator +"intermediates"// Gradle-3.0.0&robust-0.4.71 corresponds to a path \ SDK \ Build \intermediates\transforms\ ProGuard \(channel)\release\0.jar String robustJarPath = intermediatesPath + File.separator +"transforms" + File.separator + "proguard" + File.separator + flavor + File.separator + "release" + File.separator + "0.jar"// Gradle-2.3.3 & robust-0.4.7 corresponds to a path \ SDK \build\intermediates\transforms\ Proguard \(channel)\release\jars\ 3f \main.jar // String robustJarPath = intermediatesPath + File.separator +"transforms" + File.separator + "proguard" + File.separator + flavor + File.separator + "release" + File.separator + "jars" + File.separator + "3" + File.separator + "1f" + File.separator + "main.jar"// The path of the resource File, \ SDK \build\intermediates\assets\(channel)\ Release String assetsPath = intermediatesPath + file.separator +"assets" + File.separator + flavor + File.separator + "release"// Rely on the local jar package path, \ SDK \build\intermediates\jniLibs\(channel)\ Release String libsPath = intermediatesPath + file.separator +"jniLibs" + File.separator + flavor + File.separator + "release"

            // res资源文件的路径,\sdk\build\intermediates\res\merged\(渠道)\release,经测试发现此目录下生成的.9图片会失效,因此弃置,换另外方式处理
            // String resPath = intermediatesPath + File.separator + "res" + File.separator + "merged" + File.separator + flavor + File.separator + "release"// Due to the above problems, I directly use the res path \ SDK \ SRC \main\res of the project, so the resource files dependent on the third party cannot be integrated, but I am based on generating jar packages and resources that only contain their own code, and the rest depend on the host. String resPath = projectDir.toString() + file.separator +"src" + File.separator + "main" + File.separator + "res"// Resource ID path, \ SDK \build\intermediates\symbols\(channel)\ Release String resIdPath = intermediatesPath + file.separator +"symbols" + File.separator + flavor + File.separator + "release"// Manifest file path, \ SDK \build\intermediates\manifests\full\(channel)\release, String manifestPath = intermediatesPath + file.separator +"manifests" + File.separator + "full" + File.separator + flavor + File.separator + "release"// Target path after the above File is integrated, \ SDK \robustjar\(channel)\release\ Origin String destination = projectdir.toString () + file.separator + /*'outputs' + File.separator +*/ 'robustjar' + File.separator + flavor + File.separator + 'release' + File.separator + 'origin'// It looks like the aidl folder is useless, The package generates com.cm based on the definition code such as G:\\sms-hotfix\\SmsParsingForRcs-Library\ Library\ SRC \main\ aidl\ com\ cmic\ IMyAidlInterface. Aidl // String aidlPath = builddir.toString () + file.separator +"generated" + File.separator + "source" + File.separator + "aidl" + File.separator + flavor + File.separator + "release"

            File file = file(robustJarPath)
            if (file.exists()) {
                println 'The channel is:' + flavor + '; Start copying the jar package 'robust'Copy {// Copy to assets directory from(assetsPath) {into'assets'} // from(libsPath) {// into'libs'
                    //      include '**/*.jar'
                    //      exclude {
                    //          // println it.path+";"+ it.isdirectory () // it.isdirectory () //} //} //.so to jni directory from(libsPath) {into'jni'
                        include '**/*/*.so'} // From (resPath) {// Exclude the MainActivity layout file, because the output is jar package, MainActivity is only added to exclude the APK package task'/layout/activity_main.xml'Exclude {// exclude the empty folder it.isDirectory() && it.getFile().listFiles().length == 0} into'res'} // from(aidlPath) {// into'aidl'// copy the resource ID file from resIdPath to directory \ SDK \robustjar\(channel)\release\origin into destination} copy {// Copy the obfuscation rules for the host, here I am in Android {defaultConfig {consumerProguardFiles'lib-proguard-rules.pro'}}, configured with a confusing rules def files. = android defaultConfig. ConsumerProguardFilesif(files ! = null && files.size() > 0) { def file1 = files.get(0); // println'Obfuscate file path:'+file1.path from file1.path into destination // Copy obfuscation rules and rename rename(file1.name,'proguard.txt'TXT and methodsMap. Robust file copy {// Confuse the mapping file path, \ SDK \build\outputs\mapping\(channel)\release\mapping.txt from(builddir.tostring () + file.separator +'outputs' + File.separator + 'mapping' + File.separator + flavor + File.separator + 'release') {
                        include 'mapping.txt'} // Copy to directory \ SDK \robustjar\(channel)\release into projectDir.toString() + file.separator +'robustjar' + File.separator + flavor + File.separator + 'release'} copy {robust generated methodsMap file path, \sdk\build\outputs\robust\methodsMap.robust from(buildDir.toString() + File.separator +'outputs' + File.separator + 'robust') {
                        include 'methodsMap.robust'} // Copy to directory \ SDK \robustjar\(channel)\release into projectDir.toString() + file.separator +'robustjar' + File.separator + flavor + File.separator + 'release'} // If no aiDL directory exists, create an aiDL empty directory createDir(destination + file.separator +"aidl") // createDir(destination + file.separator +"assets") // createDir(destination + file.separator +"jni") // createDir(destination + file.separator +"libs") // createDir(destination + file.separator +"res") // Replace the contents of the manifest application node with the contents of the activity node, such as < Application Android :allowBackup= below"true"
                    tools:replace="android:label"
                    android:label="sdk"
                    android:supportsRtl="true"
                    android:icon="@android:drawable/ic_dialog_info"
                    android:theme="@android:style/Theme.Black"
                    >
                    <activity android:name=".MainActivity">
                        <intent-filter>
                            <action android:name="android.intent.action.MAIN"/>
                            <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> < Application Android :allowBackup="true"
                    tools:replace="android:label"
                    android:label="sdk"
                    android:supportsRtl="true">
                </application>

                def oldStr = ["
      
       "
      [\\s\\s]*?>."
      
       "
      [\\s\\s]*?>]
                def newStr = ["<application\n" + " android:allowBackup=\"true\"\n" + " android:supportsRtl=\"true\">".""] // Handle \ SDK \build\intermediates\manifests\full\(channel)\release\ Androidmanifest.xml String strBuffer = fileReader(manifestPath + File.separator +"AndroidManifest.xml", oldStr, newStr) // output to \ SDK \robustjar\(channel)\release\ Origin \ Androidmanifest.xml fileWrite(destination + file.separator +"AndroidManifest.xml", strBuffer)

                println 'Output robust insertion JAR package successful! 'Tasks.findbyname (); // Execute the task that generates the jar.'jar_'+ flavor).execute(); // Execute tasks with aar files, which include \ SDK \robustjar\develop\release\origin.'aar_' + flavor).execute()

            }
        }
    }
}Copy the code

The other two tasks that the above task needs to execute

// Generate tasks for jar packages and AAR packages according to the channelfor(String flavor: android. ProductFlavors. Names) {/ / to iterate through all channels, and generate the corresponding channel dozen jars of task, named jar_ (channels) tasks. The create (name:'jar_' + flavor, type: Jar) {

        println "Current channel is:"// The jar package is named classes.jar baseName'classes'

        String intermediatesPath = buildDir.toString() + File.separator + "intermediates"// Gradle-2.3.3 & robust-0.4.7 corresponds to a path \ SDK \build\intermediates\transforms\ Proguard \(channel)\release\jars\ 3f \main.jar // String robustJarPath = intermediatesPath + File.separator +"transforms" + File.separator + "proguard" + File.separator + flavor + File.separator + "release" + File.separator + "jars" + File.separator + "3" + File.separator + "1f" + File.separator + "main.jar"// Gradle-3.0.0&robust-0.4.71 corresponds to a path \ SDK \ Build \intermediates\transforms\ ProGuard \(channel)\release\0.jar String robustJarPath = intermediatesPath + File.separator +"transforms" + File.separator + "proguard" + File.separator + flavor + File.separator + "release" + File.separator + "0.jar"Def zipFile = new File(robustJarPath) // Decompress the jar package FileTree jarTree = zipTree(zipFile) from jarTree Separator + \ SDK \robustjar\(channel)\release\ Origin File destDir = File (projectdir.tostring () + file.separator +'robustjar' + File.separator + flavor + File.separator + 'release' + File.separator + 'origin') // Set the output pathsetDestinationDir destDir include {// Only pack the classes we need it.path.startswith ('com/oubowu/sdk') || it.path.startsWith('com/meituan/robust') || it.path.startsWith('com/oubowu/secret')
        }

        exclude {
            // println "Executive exclusion:"+ it.path // exclude R related class file, exclude mainactivity.class file it.path.startswith ('com/oubowu/sdk/R$') || it.path.startsWith('com/oubowu/sdk/R.class') || it.path.startsWith('com/oubowu/sdk/MainActivity.class')
        }

        println 'Zip jar package completed !!!!!!!! '} // Create a task named aar_(channel) tasks.create(name:'aar_' + flavor, type: Zip) {// The output path of aar package is \ SDK \robustjar\(channel)\ Release \aar File destDir = File (projectdir.tostring () + file.separator +'robustjar' + File.separator + flavor + File.separator + 'release' + File.separator + 'aar'// The aar package is named library-(channel)-release.aar archiveName'library-' + flavor + '-release.aar'// The source path is \ SDK \robustjar\(channel)\release\ Origin from projectDir.tostring () + file.separator +'robustjar' + File.separator + flavor + File.separator + 'release' + File.separator + 'origin'DestinationDir destDir println // Set the compressed output path destinationDir destDir println'Compression AAR package complete !!!!!!!! '}}Copy the code

Now that our custom packaged task is written, execute the assembleDevelopRelease task to generate the JAR and AAR packages we want

QQ screenshots 20171101111237. PNG

The project address RobustForSdk can be downloaded and run for easy understanding.

I’ll also explain how I handled patch delivery and secure patch application. Stay tuned! Peace!!!!!!

3. Policies for delivering and loading patches

Refer to the official example PatchManipulateImp. Java to customize our deliver load policy

Patch policy. PNG

As shown in the preceding flowchart, check whether a patch has been delivered locally. If a local patch has been loaded first, the local patch cannot be repaired because there is no network or the patch fails to be delivered to the network. Then go to request issued by patch list information network, the SDK version as issued based on the patch, then patch high repair contains low version of the code, I consider is convenient to handle, don’t need to load multiple patches, corresponding strategy is a large version of the project contract as the closure version of a release branch, follow-up this version online when bugs are based on the branch, Merge the fixed code until the next release goes live.

The specific code is as follows:

/ / create a fixed thread thread pool, requests for local patch load and network used in serial patches and then load the logic ExecutorService fixedThreadPool = Executors. NewFixedThreadPool (1); / / read the name of the local save the last patch loading final String pName = context. GetSharedPreferences ("com.oubowu.sdk.sp", Context.MODE_PRIVATE).getString("pName"."");
        if(! PName. IsEmpty ()) {/ / threads to create local patch load PatchExecutor patchExecutor1 = new PatchExecutor (context) getApplicationContext (), new PatchManipulateImp(true, pName),...) ; Fixedthreadpool. execute(patchExecutor1); } / / create the patch network request and load the thread PatchExecutor patchExecutor2 = new PatchExecutor (context. GetApplicationContext (), new PatchManipulateImp(false, pName), ...) ; fixedThreadPool.execute(patchExecutor2);Copy the code

Patch Processing

Public class PatchManipulateImp extends PatchMANIPULIMP {private Boolean mOnlyLocal = specifies whether to do only local patchestrue; // Local patch name private String mSavePatchName; public PatchManipulateImp(boolean onlyLocal, String savePatchName) { mOnlyLocal = onlyLocal; mSavePatchName = savePatchName; } /*** * connect to the network,get the latest patches * @param context ** @return
     */
    @Override
    protected List<Patch> fetchPatchList(final Context context) {

        final List<Patch> patches = new ArrayList<>();

        if(mOnlyLocal) {// Just to make local judgmentsif(! Msavepatchname.isempty ()) {// If the name is not empty, a locally saved patch exists, and addPatchInfo(context, mSavePatchName, patches) is added. }returnpatches; Final CountDownLatch mCountDownLatch = new CountDownLatch(1); / / Bmob initialization Bmob. The initialize (context. GetApplicationContext (),"52e558b89195c84cd761afbeabc3df52"); BmobQuery<com.oubowu.sdk.Patch> query = new BmobQuery<>(); // Query the online patch for this SDK version with sdkVersion query.addWhereequalto ("sdkVersion", BuildConfig.VERSION_NAME); // Query. Order ("-patchVersion");
        // query.setLimit(1);
        query.findObjects(new FindListener<com.oubowu.sdk.Patch>() {
            @Override
            public void done(List<com.oubowu.sdk.Patch> list, BmobException e) {
                if(e ! = null) {logger. e(LLDB etMessage()); mCountDownLatch.countDown(); }else {
                    if(list ! = null && list.size() > 0) {final com.oubowu.sdk.Patch p = list.get(0); Logger.e(p.toString()); final String filename = p.getPatchUrl().getFilename(); // If the patch name saved by SP is different from that of the latest patch, download and apply the patch. Or the patch has the same name but is not available locally. Download and apply the patchif(! filename.equals(mSavePatchName) || ! (new File(context.getFilesDir(), mSavePatchName).exists())) { File saveFile = new File(context.getFilesDir(), filename);if(! Savefile.exists () {// If not saved locally, download the patch p.gepatchurl (). Download (saveFile, new)DownloadFileListener() {
                                    @Override
                                    public void done(String s, BmobException e) {
                                        if(e ! = null) { mCountDownLatch.countDown(); }else {
                                            Logger.e("Download successful," + s);
                                            context.getSharedPreferences("com.oubowu.sdk.sp", Context.MODE_PRIVATE).edit().putString("pName", filename).apply();
                                            addPatchInfo(context, filename, patches);
                                            mCountDownLatch.countDown();
                                        }
                                    }

                                    @Override
                                    public void onProgress(Integer integer, long l) {
                                    }
                                });
                            } else{/ / local has been saved, save the name, use the context directly. GetSharedPreferences ("com.oubowu.sdk.sp", Context.MODE_PRIVATE).edit().putString("pName", filename).apply(); addPatchInfo(context, filename, patches); mCountDownLatch.countDown(); }}}else{/ / the SDK version not patch mCountDownLatch. CountDown (); }}}}); Try {// block waiting for the network request to end McOuntdownlatch.await (); } catch (InterruptedException e) { e.printStackTrace(); }returnpatches; } /** * Add patch information ** @param context * @param fileName * @param patches */ private void addPatchInfo(context context, String fileName, List<Patch> patches) {// Decrypt the delivered encrypted Patch and return the decrypted file path. String dPatchPath = ndkHelper. p(context, fileName,false);
        File dPatchFile = new File(dPatchPath);
        if(dPatchfile.exists ()) {// If the decrypted file exists, add it to the Patch list Patch = new Patch(); patch.setName(dPatchFile.getName().replace(".jar".""));
            patch.setLocalPath(dPatchFile.getPath().replace(".jar".""));
            patch.setPatchesInfoImplClassFullName("com.oubowu.sdk.lib.PatchManipulateImp.PatchesInfoImpl");
            patches.add(patch);
        }
    }

    /**
     * @param context
     * @param patch
     * @return you can verify your patches here
     */
    @Override
    protected boolean verifyPatch(Context context, Patch patch) {
        //do"Your verification, put the real patch to patch" // place in your app's private directory patch.settemppath (context.getcachedir () + file.separator +"robust" + File.separator + patch.getName());
        //in the sample we just copy the file
        try {
            copy(patch.getLocalPath(), patch.getTempPath());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("copy source patch to local patch error, no patch execute in path "+ patch.getTempPath()); } // Delete the decrypted local patch patch.delete(patch.getLocalPath());return true; }... }Copy the code

4. Generate and use patches

In sdktest. Java I wrote a method that throws NumberFormatException

    public static void callBugMethod(Context context) {

        String strFromCPlus = NdkHelper.getStrFromCPlus();

        Logger.e(strFromCPlus);

        int i = Integer.parseInt(strFromCPlus);

    }Copy the code

Then fix the callBugMethod method and add a static inner class

    @Modify
    public static void callBugMethod(Context context) {

        String strFromCPlus = NdkHelper.getStrFromCPlus();

        Logger.e(strFromCPlus);

        try {
            int i = Integer.parseInt(strFromCPlus);
        } catch (NumberFormatException e) {
            e.printStackTrace();
            Logger.e("I fixed the null pointer using Robust hot update!!");
        }

        MyClass.call();

    }

    @Add
    public static class MyClass {
        public static void call() {
            Logger.e("I've added a static inner class using a Robust hot update."); }}Copy the code

Set \RobustForSdk\gradle.properties isPatchModule to true, Place mapping.txt and methodsMap. Robust under \ SDK \robustjar\develop\ Release in \ SDK \robust

    # Application mode, Robust needs to be Application to insert code and patch
    isAppModule=true
    Enable this to patch in Application mode
    isPatchModule=trueCopy the code

Run assembleDevelopRelease. If the following message is displayed, the patch generation is successful

* What went wrong:
Execution failed for task ':sdk:transformClassesWithAutoPatchTransformForDevelopRelease'.
> auto patch end successfullyCopy the code

QQ screenshots 20171102112715. PNG

In order to ensure security, patches need to be encrypted and then uploaded to the Bmob background. In order to facilitate operation, I wrote a Window program with MFC, which can be viewed as AesWindowsApplication

PNG encryption and decryption tool

Upload to Bmob background

Bmob background. PNG

Aar is used in the app module to initialize the SDK on the main page mainActivity. class, and sdktest. init will execute the patch request and load logic

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTvHello = (TextView) findViewById(R.id.tv_hello);

        findViewById(R.id.bt_sdk).setOnClickListener(this);

        checkPermissionAndCallSdk();

    }

    private void checkPermissionAndCallSdk() {
        boolean checkPermission = MPermissionUtils.getInstance()
                .checkPermission(this, REQUEST_PERMISSION_SUCCESS, Manifest.permission.READ_PHONE_STATE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.READ_EXTERNAL_STORAGE);
        if(checkPermission) { SdkTest.init(this); }}Copy the code

Bug method of calling SDK in click event when APP is first launched and disconnected

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_sdk:
                SdkTest.callBugMethod(this);
                mTvHello.setText(NdkHelper.getStrFromCPlus());
                break;
            default:
                break; }}Copy the code

Logcat prints the exception thrown by the following string conversion integer

    FATAL EXCEPTION: main
    Process: com.oubowu.robustforsdk, PID: 18306
    java.lang.NumberFormatException: Invalid int: "Hello from C++"Copy the code

Open the network and start the APP for the second time. If the network request is normal, the patch version we use is printed out as 3, corresponding to SDK version 2.6.0. /data/data/(package name)/files; And it worked

11-03 09:48:43. 893, 19711-19711 / com oubowu. Robustforsdk E/PRETTY_LOGGER: ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 11-03 09:48:43. 893, 19711-19711 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ Thread: The main 11-03 09:48:43. 893. 19711-19711 / com oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:48:43. 893, 19711-19711 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ From the Code (264) PolicyQuery. Java: 11-03 09:48:43. 893, 19711-19711 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ aThe $1. Done (PatchManipulateImp. Java: 95) 11-03 09:48:43. 893, 19711-19711 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:48:43. 893, 19711-19711 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ Patch {sdkVersion ='server', patchVersion='3'. patchUrl=http://bmob-cdn-14435.b0.upaiyun.com/2017/10/16/37e20fe240ae64a2803b3d5ed7047be9.jar} Com. Oubowu. SDK. Patch @ 42588-03 09:48:43 d90 11. 893, 19711-19711 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 11-03 09:51:35. 513, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 11-03 09:51:35. 513, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ Thread: The main 11-03 09:51:35. 513. 20875-20875 / com oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:51:35. 523, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ of onPostExecute (BmobFileDownloader. Java: 2095) 11-03 09:51:35. 523, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ aThe $1The $1. Done (PatchManipulateImp. Java: 109) 11-03 09:51:35. 523, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:51:35. 523, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ downloaded successfully, / data/data/com. Oubowu. Robustforsdk/files/e - patch server - 4. The jar - 03 09:51:35. 11, 523, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 11-03 09:51:35. 533, 20875-20875 / com. Oubowu. Robustforsdk E/secret: / data/data/com. Oubowu. Robustforsdk/files/e - patch server - 4. The jar - 03 09:51:35. 11, 533, 20875-20875 / com. Oubowu. Robustforsdk E/secret: /data/data/com.oubowu.robustforsdk/files/d-e- patch - server - 4. The jar - 03 09:51:35. 11, 623, 20875-20891 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 11-03 09:51:35. 623, 20875-20891 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ Thread: 11 - thread pool - 1-1-03 09:51:35. 623, 20875-20891 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:51:35. 623, 20875-20891 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ PatchExecutor. ApplyPatchList (PatchExecutor. Java: 71) 11-03 09:51:35. 623, 20875-20891 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ SdkTest$3OnPatchApplied (SdkTest. Java: 97) 11-03 09:51:35. 623, 20875-20891 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:51:35. 623, 20875-20891 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ PatchExecutor 11-03 09:51:35. 623. 20875-20891 / com oubowu. Robustforsdk E/PRETTY_LOGGER: └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─Copy the code

By clicking on the event, you can see from the log that the fixed code has been used

11-03 09:53:46. 213, 20875-20875 / com oubowu. Robustforsdk W/System. Err: Java. Lang. A NumberFormatException: Invalid int:"Hello from C++"11-03 09:53:46. 213, 20875-20875 / com oubowu. Robustforsdk W/System. Err: The at Java. Lang. Integer. InvalidInt (Integer. Java: 137) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err: The at Java. Lang. Integer. The parse (Integer. Java: 374) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err: The at Java. Lang. Integer. ParseInt (Integer. Java: 365) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err: The at Java. Lang. Integer. ParseInt (Integer. Java: 331) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err: At com. Oubowu. SDK. Lib. PatchManipulateImp. SdkTestPatch. CallBugMethod (SdkTestPatch. Java: 163) 11-03 09:53:46. 213 20875-20875/com.oubowu.robustforsdk W/System.err: At com. Oubowu. SDK. Lib. PatchManipulateImp. SdkTestPatchControl. AccessDispatch (PatchTemplate. Java) 11-03 09:53:46. 213 20875-20875/com.oubowu.robustforsdk W/System.err: At com. At meituan. Robust. PatchProxy. AccessDispatch (PatchProxy. Java: 61) 11-03 09:53:46. 213 20875-20875/com.oubowu.robustforsdk W/System.err: At com. Oubowu. SDK. SdkTest. CallBugMethod (SdkTest. Java) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System.err: At com. Oubowu. Robustforsdk. MainActivity. OnClick (MainActivity. Java: 46) 11-03 09:53:46. 213 20875-20875/com.oubowu.robustforsdk W/System.err: An android. View. The view. PerformClick (the Java: 4444) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err: at android.view.View$PerformClick. The run (the Java: 18457) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err: . An android OS. Handler. HandleCallback (Handler. Java: 733) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System.err: . An android OS. Handler. DispatchMessage (Handler. Java: 95) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System.err: . An android OS. Stars. Loop (136). Which Java: 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err: . An android app. ActivityThread. Main (ActivityThread. Java: 5113) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System.err: At Java. Lang. Reflect. Method. InvokeNative (Native Method) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System.err: The at Java. Lang. Reflect. Method. Invoke (515) Method. The Java: 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err:  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller. The run (ZygoteInit. Java: 796) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System. Err: At com. Android. Internal. OS. ZygoteInit. Main (ZygoteInit. Java: 612) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk W/System.err: At dalvik. System. NativeStart. Main (Native Method) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ Thread: The main 11-03 09:53:46. 213. 20875-20875 / com oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ SdkTestPatchControl. AccessDispatch (PatchTemplate. Java: 1) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ SdkTestPatch. CallBugMethod (SdkTestPatch. Java: 166) 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ I use Robust hot update null Pointers to repair!!!!!! 11-03 09:53:46. 213, 20875-20875 / com oubowu. Robustforsdk E/PRETTY_LOGGER: └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 11-03 09:53:46. 213, 20875-20875 / com. Oubowu. Robustforsdk D/robust: invoke the static method is No: 23 e 11-03 09:53:46. 213. 20875-20875 / com oubowu. Robustforsdk e/PRETTY_LOGGER: ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 11-03 09:53:46. 223, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ Thread: The main 11-03 09:53:46. 223. 20875-20875 / com oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:53:46. 223, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ SdkTestPatch. CallBugMethod (SdkTestPatch. Java: 169) 11-03 09:53:46. 223, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ SdkTest$MyClass. Call (176) SdkTest. Java: 11-03 09:53:46. 223, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: ├ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ 11-03 09:53:46. 223, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: │ I use Robust hot update adds a static inner class 11-03 09:53:46. 223, 20875-20875 / com. Oubowu. Robustforsdk E/PRETTY_LOGGER: └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─Copy the code

The above is my idea and method to practice SDK hotfix, hoping to bring some useful to readers. If you feel good, you can give it to the projectRobustForSdkA star, yours is definitely the biggest encouragement for me!