Stable SDK integration

更新时间:
复制 MD 格式

This topic describes how to integrate the Mobile Hotfix software development kit (SDK).

Prerequisites

Limits

  • Supports Android 4.3 and later. If you use self-developed devices and systems, you must disable system-level JIT before integration.

  • You must use ProGuard for code obfuscation.

  • The hotpatching SDK supports fixes for only Java code, resource files, and .so files.

Integration steps

  1. Add project dependencies

    1. Android Studio integration method

      Use a Gradle remote repository dependency. Open the project, find your app's `build.gradle` file, and add the following configurations:

      Add the Maven repository address:

      repositories {
         maven {
             url "http://maven.aliyun.com/nexus/content/repositories/releases"
         }
      }

      Add the Gradle coordinate version dependency:

      android {
          ......
          defaultConfig {
              applicationId "com.xxx.xxx" // Package name
              ......
              ndk {
                  // Select the .so libraries for the corresponding CPU types.
                  // Hotpatching supports five types.
                  abiFilters 'arm64-v8a', 'armeabi', 'armeabi-v7a', 'x86', 'x86_64'
              }
              ......
          }
          ......
      }
      dependencies {
          ......
              compile 'com.aliyun.ams:alicloud-android-hotfix:3.4.2'
          ......
      }

      If you cannot access the repository, you can use local dependencies.

      Important
      • When you use Android Studio to package and generate an APK, you must disable Instant Run.

      • If you use a Gradle plugin version later than 4.2, resource optimization may be automatically enabled. This can obfuscate resource names and cause the patch generation process to stop responding at the "Start building patch..." step and fail to parse the APK package. To resolve this issue, add `android.enableResourceOptimizations=false` to `gradle.properties`. Then, regenerate the baseline package and the fix package before you generate the patch.

      • If you enable code obfuscation, you must disable R8 to prevent the generated patch from being too large. To do this, add `android.enableR8=false` to `gradle.properties`. Then, regenerate the baseline package and the fix package before you generate the patch.

      • If a UTDID conflict occurs during SDK integration, see Alibaba Cloud SDK UTDID Conflict Solution.

    2. Eclipse integration method

      1. Download OneSDK.zip. Unzip the file and open the `libs` folder. You must add dependencies for all .aar and .jar files in the `libs` folder.

      2. To add dependencies for .aar files, unzip the .aar file. Copy the .so files from the `jni` folder to your project's `jni` folder. In Eclipse, the `jni` folder is typically the `libs` folder. Copy the .jar file to the project's `libs` folder. Merge the content of the `AndroidManifest.xml` file into your project's `AndroidManifest.xml` file.

      3. Copy all .jar files to the project's `libs` folder.

      Important

      If a duplicate UTDID class exception occurs during compilation, see Alibaba Cloud SDK UTDID Conflict Solution.

  2. Add application permissions

    The Sophix SDK uses the following permissions. If you use Maven or .aar dependencies, you do not need to configure them manually. The configurations are specified in `AndroidManifest.xml`.

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    SDK permission

    Required

    Description

    INTERNET

    Yes

    Allows network requests. Used to download patches.

    ACCESS_NETWORK_STATE

    Yes

    Gets carrier and network type information. Used to collect statistics on patch loading status across different networks.

    ACCESS_WIFI_STATE

    Yes

    Gets carrier and network type information. Used to collect statistics on patch loading status across different networks.

    READ_EXTERNAL_STORAGE

    No

    Read permission for external storage. Required by the debugging tool to load local patches from an SD card.

    Note

    The READ_EXTERNAL_STORAGE permission is a Dangerous Permission. It is required only for the debugging tool to retrieve external patches and does not affect the loading of patches published online. When debugging, handle runtime permission requests for Android 6.0 and later as needed.

  3. Configure the AndroidManifest.xml file

    In the AndroidManifest.xml file, add the following configurations under the application node:

    <meta-data
    android:name="com.taobao.android.hotfix.IDSECRET"
    android:value="App ID" />
    <meta-data
    android:name="com.taobao.android.hotfix.APPSECRET"
    android:value="App Secret" />
    <meta-data
    android:name="com.taobao.android.hotfix.RSASECRET"
    android:value="RSA key" />

    Replace the `value` placeholders with the App ID, App Secret, and RSA key that you can obtain from the Mobile Hotfix service on the platform. For more information about how to obtain the App Secret and RSA key, see View application information. For security, we recommend that you use the `setSecretMetaData` method to set these values. For more information, see the method description in SDK API. If you cannot find the corresponding parameters, see "Download the SDK" in EMAS Quick Start to obtain the application configuration information.

    Note
    • In addition, hotpatching does not currently support reading JSON files from the unified EMAS plugin.

    • IDSECRET, APPSECRET, and RSASECRET will be used for billing. Store them securely.

    • To avoid leaking the IDSECRET, APPSECRET, RSASECRET parameters or data generated during app runtime in logs, we recommend that you disable SDK debug logs in the production version.

    • Because you must set the IDSECRET, APPSECRET, and RSASECRET parameters in your code, we recommend that you enable obfuscation before you publish your application. This helps prevent information leaks from malicious decompilation.

  4. Obfuscation configuration

    # Used for the baseline package to generate mapping.txt
    -printmapping mapping.txt
    # The generated mapping.txt is in the app/build/outputs/mapping/release path. Move it to the /app path.
    # Used for the fixed project to ensure consistent obfuscation results
    #-applymapping mapping.txt
    #hotfix
    -keep class com.taobao.sophix.**{*;}
    -keep class com.ta.utdid2.device.**{*;}
    # Prevent inlining
    -dontoptimize
    Important

    When you enable obfuscation, use the mapping file from the baseline package to generate the fix package. This ensures consistent obfuscation results.

  5. Obfuscation with ProGuard

    If you enable code obfuscation, you must disable R8 and use ProGuard instead. Otherwise, patch generation may fail. The specific steps depend on your Android Gradle Plugin version:

    1. Android Gradle Plugin earlier than 7.0

      In the `gradle.properties` file in the project's root directory, add the following configuration.

      android.enableR8=false
    2. Android Gradle Plugin 7.0 and later

      1. In the `build.gradle` file in the project's root directory, add the following ProGuard Gradle Plugin configuration.

        buildscript {
            repositories {
                // For the Android Gradle plugin.
                google()
                // For the ProGuard Gradle Plugin.
                mavenCentral()
            }
            dependencies {
                // The Android Gradle plugin.
                classpath("com.android.tools.build:gradle:x.y.z")
                // The ProGuard Gradle plugin.
                classpath("com.guardsquare:proguard-gradle:7.1.+")
            }
        }
      2. In the `build.gradle` file in the `app` folder, apply the ProGuard Gradle Plugin.

        apply plugin: 'com.guardsquare.proguard'
      3. Then, disable R8 obfuscation.

        android {
           buildTypes {
               release {
                   // Disable R8.
                   minifyEnabled false
               }
           }
        }
      4. Finally, configure ProGuard obfuscation.

        android {
           ...
        }
        
        proguard {
           configurations {
               release {
                   defaultConfiguration 'proguard-android.txt'
                   configuration 'proguard-rules.pro'
               }
               debug {
                   defaultConfiguration 'proguard-android-debug.txt'
                   configuration 'proguard-rules.pro'
               }
           }
        }
  6. Initialization

    Call the initialization method as early as possible. You must initialize the SDK at the beginning of Application.attachBaseContext(), after `super.attachBaseContext` and `Multidex.install` if you use Multidex. Do not use other custom classes before initialization, because this is very likely to cause a crash. You can query the server for available patches at a later point. We do not recommend initializing in Application.onCreate(), because if the application includes a ContentProvider, Sophix initialization may be delayed and cause issues.

    The latest version of Sophix introduces a new initialization method.

    The original initialization method still works. However, the new method provides more comprehensive support for feature fixes and offers the following advantages:

    • Initialization is completely isolated from the application's original business logic. This allows the original Application class to be patched and reduces the patch preloading time.

    • The new method is more compatible with Android 8.0 and later.

    Specifically, you need to add the following class:

    package com.my.pkg;
    import android.app.Application;
    import android.content.Context;
    import android.support.annotation.Keep;
    import android.util.Log;
    import com.taobao.sophix.PatchStatus;
    import com.taobao.sophix.SophixApplication;
    import com.taobao.sophix.SophixEntry;
    import com.taobao.sophix.SophixManager;
    import com.taobao.sophix.listener.PatchLoadStatusListener;
    import com.my.pkg.MyRealApplication;
    /**
     * Sophix entry class. This class is used only to initialize Sophix and should not contain any business logic.
     * This class must inherit from SophixApplication. You do not need to implement the onCreate method.
     * This class must be completely isolated and should not have any logic that calls or is called by other classes in the project.
     * In AndroidManifest, set the application to this class. In SophixEntry, set it to the original Application class.
     * Do not re-initialize Sophix in the original Application class. Make sure the original Application class is not obfuscated.
     * If you have other custom modifications, contact the official support team for proper handling.
     */
    public class SophixStubApplication extends SophixApplication {
        private final String TAG = "SophixStubApplication";
        // Here, SophixEntry should specify the real Application class, and you must ensure that the RealApplicationStub class name is not obfuscated.
        @Keep
        @SophixEntry(MyRealApplication.class)
        static class RealApplicationStub {}
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
    //         To use MultiDex, call it here.
    //         MultiDex.install(this);
            initSophix();
        }
        private void initSophix() {
            String appVersion = "0.0.0";
            try {
                appVersion = this.getPackageManager()
                                 .getPackageInfo(this.getPackageName(), 0)
                                 .versionName;
            } catch (Exception e) {
            }
            final SophixManager instance = SophixManager.getInstance();
            instance.setContext(this)
                    .setUsingEnhance(true) // Adapts to reinforcement mode. Add this method if the app uses reinforcement.
                    .setAppVersion(appVersion)
                    .setSecretMetaData(null, null, null)
                    .setEnableDebug(true)
                    .setEnableFullLog()
                    .setTags(Arrays.asList("Gray")) // Set tags as needed.
                    .setPatchLoadStatusStub(new PatchLoadStatusListener() {
                        @Override
                        public void onLoad(final int mode, final int code, final String info, final int handlePatchVersion) {
                            if (code == PatchStatus.CODE_LOAD_SUCCESS) {
                                Log.i(TAG, "sophix load patch success!");
                            } else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
                                // To restart the app in the background, we recommend that you use SharePreference to save the state here.
                                Log.i(TAG, "sophix preload patch success. restart app to make effect.");
                            }
                        }
                    }).initialize();
        }
    }

    The key point is:

        @Keep
        @SophixEntry(MyRealApplication.class)
        static class RealApplicationStub {}

    SophixEntry must specify the project's original Application class, which is specified by `android:name` in the `` tag. In this example, it is represented by `MyRealApplication`. Ensure that the RealApplicationStub class name is not obfuscated. You can choose your own class name and package name for SophixStubApplication.

    Important

    The SophixStubApplication class cannot contain any other logic. You can use SharedPreferences to save the state.

    Here, Keep is a class from the `android.support` package. Its purpose is to prevent the name of this inner static class from being obfuscated, because Sophix uses reflection to retrieve the SophixEntry of this class. If your project does not have a dependency on `android.support`, you must manually specify in ProGuard that RealApplicationStub should not be obfuscated, as described below.

    Then, add the following content to the ProGuard file:

    -keepclassmembers class com.my.pkg.MyRealApplication {
        public <init>();
    }
    -keep class com.my.pkg.SophixStubApplication$RealApplicationStub

    This prevents the constructor of the real Application class from being obfuscated by ProGuard.

    Finally, change the application in `AndroidManifest` to this new SophixStubApplication class:

        <application
            android:name="com.my.pkg.SophixStubApplication"
            ... ...>
            ... ...

    This completes the integration of the new initialization method.

  7. Pull patches

    After initialization is complete, you can query the server and pull a patch.

    SophixManager.getInstance().queryAndLoadNewPatch();
    Important
    • The queryAndLoadNewPatch method is used to request patch packages published in the console. It initiates a network request, so it must be called after the user agrees to the privacy policy.

    • However, do not call this method in `attachBaseContext` because it will not have network permissions. We recommend that you call it at any point in the main process after the user agrees to the privacy policy.

  8. Verify the integration

    View the logs in Logcat to verify that the feature works. Filter by the tag `Sophix` and check for the following key log entries.

    # Start pulling the patch
    Sophix.NetworkManager I   query start
    # Successfully pulled the patch
    Sophix.NetworkManager I   query download success
    # Start preloading the patch
    Sophix.ColdDexManager I   preloadDex start sophix-merged.zip
    # Successfully preloaded the patch
    Sophix.ColdDexManager I   preloadDex end time consumed(ms): 36064
    # Successfully loaded the patch after a cold start
    Sophix.PatchManager   I   loadPatch success hasDexPatched: true hasResPatched: true hasSOPatched: false

If you encounter integration issues, first refer to the FAQ and Common status codes. If you cannot resolve the issue using the documentation, contact us.

Learning materials

Note

If you have other unofficial materials, contact us to join our DingTalk group. Share your articles with us, and we will select high-quality articles to feature here.