Integrate the Android SDK

更新时间:
复制 MD 格式

This topic describes how to integrate the Android software development kit (SDK) for the Cloud Release service.

Before you integrate the Alibaba Cloud mobile SDK, make sure that you have created a product on the Alibaba Cloud Multiexperience Development Platform (EMAS) and obtained the corresponding appkey and appSecret.

Important
  • Disable SDK debug logs for the online version to prevent parameters such as appkey and appsecret or data generated during app runtime from being leaked in logs.

  • The SDK requires you to set the appkey and appsecret parameters in your code. These parameters are used for metering and billing. To prevent malicious decompilation and information leakage, you must enable obfuscation and app reinforcement before you publish your application.

1. Android SDK integration

1. Add a dependency: Maven repository dependency

  • Maven repository address:

    • Configuration for Gradle versions earlier than 7.0: In your root-level Gradle file (<project>/build.gradle), add the Maven repository address to repositories in the allprojects block.

      allprojects {
        repositories {
          maven {
            url 'https://maven.aliyun.com/nexus/content/repositories/releases/'
          }
        }
      }
    • Configuration for Gradle 7.0 and later: In your root-level (project-level) Gradle file (<project>/settings.gradle), add the Maven repository address to repositories in dependencyResolutionManagement.

      dependencyResolutionManagement {
        repositories {
          maven {
            url 'https://maven.aliyun.com/nexus/content/repositories/releases/'
          }
        }
      }
  • Dependencies:

    • Add the following to the dependencies block of the build.gradle file for the app module:

          implementation ('com.taobao.android:update-main:1.3.0-open')
          implementation ('com.taobao.android:update-common:1.3.0-open')
          implementation ('com.taobao.android:update-datasource:1.3.0-open')
          implementation ('com.taobao.android:update-adapter:1.3.0-open')

2. Accessing the service

Start the service in the onCreate method of your custom Application class:

 private void initUpdate() {
        // 1. Initialize UpdateDataSource.
        UpdateDataSource.getInstance().init(this, Utils.sAppkey, Utils.sAppSecret, Utils.sChannelID);

        // 2. Initialize UpdateRuntime.
        UpdateRuntime.init();

        // 3. Set a custom dialog box.
        SharedPreferences sp = getSharedPreferences("settings", Context.MODE_PRIVATE);
        final boolean customDialog = sp.getBoolean("custom_dialog", false);
        MainActivity.setCustomDialog(this, customDialog);
        
        // Other settings.
        // Set whether to enable the cache. true: enabled, false: disabled.
        UpdateDataSource.getInstance().setEnableCache(true);
        // Set the cache validity period in milliseconds (ms). For example, a 12-hour cache validity period.
        UpdateDataSource.getInstance().setCacheValidTime(12 * 60 * 60 * 1000);
        // true: sync, false: async.
        UpdateDataSource.getInstance().startUpdate(false);
    }

    /* Set a custom dialog box.
     * @param context Context
     * @param customDialog Specifies whether to use a custom dialog box.
     */
    private void setCustomDialog(final Context context, boolean customDialog) {
        ApkUpdater apkUpdate = new ApkUpdater(context);
        apkUpdate.setUpdateLog(new IUpdateLog() {
            @Override
            public void d(String s) {

            }

            @Override
            public void d(String s, Throwable throwable) {

            }

            @Override
            public void e(String s) {

            }

            @Override
            public void e(String s, Throwable throwable) {

            }

            @Override
            public void i(String s) {

            }

            @Override
            public void i(String s, Throwable throwable) {

            }

            @Override
            public void w(String s) {

            }

            @Override
            public void w(String s, Throwable throwable) {

            }

            @Override
            public void v(String s) {

            }

            @Override
            public void v(String s, Throwable throwable) {

            }
        });
        // Set the APK download progress callback.
        apkUpdate.setApkDownloadListener(new ApkDownloadListener() {
            @Override
            public void onPreDownload() {
                // The download starts.
            }

            @Override
            public void onDownloadProgress(int progress) {
                // The download is in progress.
            }

            @Override
            public void onStartFileMd5Valid(String filePath, String fileSize) {
                // The file MD5 validation starts.
            }

            @Override
            public void onFinishFileMd5Valid(boolean success) {
                // The file validation is complete.
            }

            @Override
            public void onDownloadFinish(String url, String filePath) {
                // The download is complete.
            }

            @Override
            public void onDownloadError(String url, int errorCode, String msg) {
                // A download error occurred.
            }
        });
        if (customDialog) {
            // Customize the dialog box that appears when an update is available.
            apkUpdate.setUpdateNotifyListener(new UpdateNotifyListener() {
                @Override
                public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
                    builder.setTitle("New Version Available");
                    builder.setMessage(customUpdateInfo.getInfo() + "\nVersion: " + customUpdateInfo.getVersion() + "\nPackage size: " + customUpdateInfo.getSize() + "\nForce update: " + customUpdateInfo.isForceUpdate());
                    builder.setCancelable(false);
                    builder.setPositiveButton(userAction.getConfirmText(), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            userAction.onConfirm();
                            dialogInterface.dismiss();
                        }
                    });
                    builder.setNegativeButton(userAction.getCancelText(), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            userAction.onCancel();
                            dialogInterface.dismiss();
                        }
                    });
                    builder.create().show();
                }
            });
            // If the update is mandatory, this dialog box appears when the user clicks the cancel button in the update dialog box. This dialog box does not appear for optional updates.
            apkUpdate.setCancelUpdateNotifyListener(new UpdateNotifyListener() {
                @Override
                public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
                    builder.setTitle("Mandatory Update Cancellation");
                    builder.setMessage(customUpdateInfo.getInfo());
                    builder.setCancelable(false);
                    builder.setPositiveButton(userAction.getConfirmText(), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            userAction.onConfirm();
                            dialogInterface.dismiss();
                        }
                    });
                    builder.setNegativeButton(userAction.getCancelText(), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            userAction.onCancel();
                            dialogInterface.dismiss();
                        }
                    });
                    builder.create().show();
                }
            });
            // The installation dialog box that appears after the APK download is complete.
            apkUpdate.setInstallUpdateNotifyListener(new UpdateNotifyListener() {
                @Override
                public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
                    builder.setTitle("Custom Installation");
                    builder.setMessage(customUpdateInfo.getInfo());
                    builder.setCancelable(false);
                    builder.setPositiveButton(userAction.getConfirmText(), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            userAction.onConfirm();
                            dialogInterface.dismiss();
                        }
                    });
                    builder.setNegativeButton(userAction.getCancelText(), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            userAction.onCancel();
                            dialogInterface.dismiss();
                        }
                    });
                    builder.create().show();
                }
            });
            // Customize the update result notification.
            apkUpdate.setUpdateResultListener(new UpdateResultListener() {
                @Override
                public void onFinish(int i, int i1, String s) {

                }
            });
        } else {
            // If the interface is set to null, the SDK's built-in dialog box is used by default.
            apkUpdate.setUpdateNotifyListener(null);
            apkUpdate.setCancelUpdateNotifyListener(null);
            apkUpdate.setInstallUpdateNotifyListener(null);
        }

    }

Specify the custom Application in the AndroidManifest.xml file:

<application
    android:name=".MyApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme" >
</application>

Add the permission to install applications within the application tag in the AndroidManifest.xml file. For example:

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

Register FileProvider within the application tag in the AndroidManifest.xml file. For example:

<provider
    android:name="com.taobao.update.provider.UpdateProvider"
    android:authorities="${applicationId}.update.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/apk_paths" />
</provider>

In the res folder, create an apk_paths.xml file with the following content:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-cache-path name="external_apk_update" path="apkupdate/" />
    <cache-path name="apk_update" path="apkupdate/" />
    <root-path name="root_apk_update" path ="storage/"/>
</paths>

3. API reference

3.1 Foreground and background update checks

  • Manually trigger the update API. This method calls the API for each request and does not use the cache.

    // The parameter specifies sync or async. true: sync, false: async.
    UpdateDataSource.getInstance().startManualUpdate(false) 
  • Automatically trigger the update API. If caching is enabled and the cache is still valid, the response is served from the cache.

    // The parameter specifies sync or async. true: sync, false: async.
    UpdateDataSource.getInstance().startUpdate(false)

3.2 Cache feature

Important

The cache is not used if you call the UpdateDataSource.getInstance().startManualUpdate() method.

After you enable the cache feature, the system checks for a local cached response from the previous API call before calling the update API. If a valid cache exists, the local cached response is returned directly.

// Set whether to enable the cache. true: enabled, false: disabled.
UpdateDataSource.getInstance().setEnableCache(true);

You can customize the cache validity period.

Important

If you enable the cache feature but do not set a cache validity period, or if you set the period to a value less than 0, the default cache validity period of 24 hours is used.

// Customize the cache validity period in milliseconds (ms).
UpdateDataSource.getInstance().setCacheValidTime(12 * 60 * 60 * 1000);
Important

After you enable the cache, avoid setting a long cache duration. A long duration may prevent the device from receiving the latest update or grayscale release instructions in a timely manner.

3.3 Customizing the update notification dialog box

ApkUpdater apkupdate = new ApkUpdater();
apkUpdate.setUpdateNotifyListener(new UpdateNotifyListener() {
    @Override
    public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("New Version Available");
        builder.setMessage(customUpdateInfo.getInfo() + "\nVersion: " + customUpdateInfo.getVersion() + "\nPackage size: " + customUpdateInfo.getSize() + "\nForce update: " + customUpdateInfo.isForceUpdate());
        builder.setCancelable(false);
        builder.setPositiveButton(userAction.getConfirmText(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                userAction.onConfirm();
                dialogInterface.dismiss();
            }
        });
        builder.setNegativeButton(userAction.getCancelText(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                userAction.onCancel();
                dialogInterface.dismiss();
            }
        });
        builder.create().show();
    }
});

3.4 Customize the cancellation dialog box for mandatory updates

ApkUpdater apkupdate = new ApkUpdater();
apkUpdate.setCancelUpdateNotifyListener(new UpdateNotifyListener() {
    @Override
    public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("Mandatory Update Cancellation");
        builder.setMessage(customUpdateInfo.getInfo());
        builder.setCancelable(false);
        builder.setPositiveButton(userAction.getConfirmText(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                userAction.onConfirm();
                dialogInterface.dismiss();
            }
        });
        builder.setNegativeButton(userAction.getCancelText(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                userAction.onCancel();
                dialogInterface.dismiss();
            }
        });
        builder.create().show();
    }
});

3.5 Customizing the installation dialog box

ApkUpdater apkupdate = new ApkUpdater();
apkUpdate.setInstallUpdateNotifyListener(new UpdateNotifyListener() {
    @Override
    public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("Custom Installation");
        builder.setMessage(customUpdateInfo.getInfo());
        builder.setCancelable(false);
        builder.setPositiveButton(userAction.getConfirmText(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                userAction.onConfirm();
                dialogInterface.dismiss();
            }
        });
        builder.setNegativeButton(userAction.getCancelText(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                userAction.onCancel();
                dialogInterface.dismiss();
            }
        });
        builder.create().show();
    }
});

3.6 Customize update notifications

apkUpdate.setUpdateResultListener(new UpdateResultListener() {
    @Override
    public void onFinish(int mode, int errorCode, String errMsg) {
        // This does not guarantee the UI thread.
    }
});

3.7 Customizing the download progress callback

apkUpdate.setApkDownloadListener(new ApkDownloadListener() {
    @Override
    public void onPreDownload() {
        // The download starts.
    }

    @Override
    public void onDownloadProgress(int progress) {
        // The download is in progress.
    }

    @Override
    public void onStartFileMd5Valid(String filePath, String fileSize) {
        // The file MD5 validation starts.
    }

    @Override
    public void onFinishFileMd5Valid(boolean success) {
        // The file validation is complete.
    }

    @Override
    public void onDownloadFinish(String url, String filePath) {
        // The download is complete.
    }

    @Override
    public void onDownloadError(String url, int errorCode, String msg) {
        // A download error occurred.
    }
});
  • If a download starts from the beginning and completes successfully, the callback path is onPreDownload() -> onDownloadProgress() -> onDownloadFinish()/onDownloadError(). The other two file validation methods are not called.

  • If a download resumes from a breakpoint, a file integrity check is performed after the download is complete. The callback path is onStartFileMd5Valid() -> onFinishFileMd5Valid().

3.8 Customizing the log interface

apkupdate.setUpdateLog(new IUpdateLog() {
    @Override
    public void d(String msg) {

    }

    @Override
    public void d(String msg, Throwable throwable) {

    }

    @Override
    public void e(String msg) {

    }

    @Override
    public void e(String msg, Throwable throwable) {

    }

    @Override
    public void i(String msg) {

    }

    @Override
    public void i(String msg, Throwable throwable) {

    }

    @Override
    public void w(String msg) {

    }

    @Override
    public void w(String msg, Throwable throwable) {

    }

    @Override
    public void v(String msg) {

    }

    @Override
    public void v(String msg, Throwable throwable) {

    }
});

2. Obfuscation configuration

If you enable obfuscation, add the following configuration to your obfuscation file:

#-------------------update-main------------------------------
-keepclassmembers class * {
    @com.google.inject.Inject <init>(...);
}
# There's no way to keep all @Observes methods, so use the On*Event convention to identify event handlers
-keepclassmembers class * {
    void *(**On*Event);
}
-keepclassmembers class ** {
    public <init>(android.content.Context);
}
-keepclassmembernames class **.R$* {*;}
-keepclassmembernames class **.R {*;}
-keepclassmembers class **{
    public static final <fields>;
}

-keep  class com.taobao.update.apk.MainUpdateData{*;}
-keep  class com.taobao.update.apk.ApkUpdater{*;}

#-------------------update-common------------------------------
-keep class com.taobao.update.common.framework.**{*;}
-keep class com.taobao.update.common.utils.**{*;}
-keep  class com.taobao.update.common.dialog.**{*;}
-keep  class com.taobao.update.common.Config{*;}
-keep class com.taobao.update.common.dialog.CustomUpdateInfo{
    public <methods>;
}
-keep interface com.taobao.update.common.dialog.UpdateNotifyListener{*;}

-dontwarn mtopsdk.mtop.intf.Mtop

3. Test and validation

After you complete the preceding steps, you can create a product, build a release, and run tests in the EMAS console.