Android端接入SDK

本文介绍云发布服务Android端接入SDK的详细操作。

在接入阿里云移动SDK之前,请明确您已在阿里云移动研发平台 ( EMAS ) 上建立相关产品,并获得对应的appkey和appSecret。

重要

  • 为避免在日志中泄漏参数appkey/appsecret或App运行过程中产生的数据,建议线上版本关闭SDK调试日志。

  • 由于所有用户使用统一的SDK接入,在接入过程中需要在代码中设置appkey/appsecret参数,而此类参数与计量计费密切相关,为防止恶意反编译获取参数造成信息泄漏,建议您开启混淆,并进行App加固后再发布上线。

一、Android SDK接入

1、添加依赖Maven仓库依赖接入

在项目build.gradle中添加阿里云Maven仓库地址:

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

app模块的build.gradle的dependencies节点内添加:

implementation 'com.taobao.android:update-main:1.2.0-open'

2、接入服务

在自定义Application类的onCreate里面启动服务:

 private void initUpdate() {
        //以下引号部分需要客户根据自己的应用进行配置()
        Config config = new Config();
        //填写appkey
        config.group = appkey + "@android";
        //渠道号,非多渠道打包时可以不填。 
        config.ttid = ttid;
        config.isOutApk = false;
        //app name
        config.appName = appName;

        //设置自定义弹窗
        setCustomDialog(this, true);
        //初始化
        UpdateRuntime.init(this, config.ttid, config.appName, config.group);
        UpdateDataSource.getInstance().init(this, config.group, config.ttid, config.isOutApk,
                appkey, appsecret, ttid, new UpdateAdapter());
        //设置是否开启缓存,true开启,false关闭
        UpdateDataSource.getInstance().setEnableCache(true);
        //设置缓存有效时间,单位ms,eg: 缓存有效时间12h
        UpdateDataSource.getInstance().setCacheValidTime(12 * 60 * 60 * 1000);
        //true:同步, false:异步
        UpdateDataSource.getInstance().startUpdate(false);
    }

    /* 设置自定义弹窗
     * @param context Context
     * @param customDialog 是否自定义弹窗
     */
    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) {

            }
        });
        //设置apk下载进度回调
        apkUpdate.setApkDownloadListener(new ApkDownloadListener() {
            @Override
            public void onPreDownload() {
                //下载开始
            }

            @Override
            public void onDownloadProgress(int progress) {
                //下载进行中
            }

            @Override
            public void onStartFileMd5Valid(String filePath, String fileSize) {
                //开启文件md5校验
            }

            @Override
            public void onFinishFileMd5Valid(boolean success) {
                //完成文件校验
            }

            @Override
            public void onDownloadFinish(String url, String filePath) {
                //下载完成
            }

            @Override
            public void onDownloadError(String url, int errorCode, String msg) {
                //下载出错
            }
        });
        if (customDialog) {
            //自定义有更新信息时的Dialog
            apkUpdate.setUpdateNotifyListener(new UpdateNotifyListener() {
                @Override
                public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
                    builder.setTitle("新版本升级");
                    builder.setMessage(customUpdateInfo.getInfo() + "\n版本:" + customUpdateInfo.getVersion() + "\n安装包大小:" + customUpdateInfo.getSize() + "\n是否强制升级:" + 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();
                }
            });
            //如果是强制更新,用户点击更新Dialog的取消按钮时会弹出这个Dialog;如果不是强制更新,则不会弹出这个Dialog
            apkUpdate.setCancelUpdateNotifyListener(new UpdateNotifyListener() {
                @Override
                public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
                    builder.setTitle("强制升级取消提示");
                    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();
                }
            });
            //apk下载完成后,安装Dialog
            apkUpdate.setInstallUpdateNotifyListener(new UpdateNotifyListener() {
                @Override
                public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
                    builder.setTitle("个性化安装");
                    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();
                }
            });
            //自定义更新结果提示
            apkUpdate.setUpdateResultListener(new UpdateResultListener() {
                @Override
                public void onFinish(int i, int i1, String s) {

                }
            });
        } else {
            //接口设为null,则弹窗默认使用SDK内置的Dialog
            apkUpdate.setUpdateNotifyListener(null);
            apkUpdate.setCancelUpdateNotifyListener(null);
            apkUpdate.setInstallUpdateNotifyListener(null);
        }

    }

AndroidManifest.xml里面指定自定义Application:

<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>

AndroidManifest.xml里面Application标签内添加安装应用的权限,如示例:

<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" />

AndroidManifest.xml里面Application标签内注册FileProvider,如示例:

<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>

res目录下,创建apk_paths.xml文件,内容如下:

<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、接口说明

3.1 前后台查询更新

  • 手动触发更新接口。 每次都会请求接口,不会使用缓存。

    //参数指定同步/异步,true是同步,false是异步
    UpdateDataSource.getInstance().startManualUpdate(false) 
  • 自动触发更新接口。如果开启了缓存且在缓存有效期内,则优先使用缓存。

    //参数指定同步/异步,true是同步,false是异步
    UpdateDataSource.getInstance().startUpdate(false)

3.2 缓存功能说明

重要

如果是调用UpdateDataSource.getInstance().startManualUpdate()方法,不会使用缓存。

开启缓存功能后,每次请求更新接口前会先检查本地是否有之前接口Response的缓存,如果有且在缓存有效时间内,则直接使用本地缓存的接口Response。

//设置是否开启缓存,true开启,false关闭
UpdateDataSource.getInstance().setEnableCache(true);

缓存有效时间可以自定义设置。

重要

如果不设置缓存有效时间,或设置为小于0的数字且开启了缓存功能,默认缓存有效时间为24h。

//自定义设置缓存有效时间,单位ms
UpdateDataSource.getInstance().setCacheValidTime(12 * 60 * 60 * 1000);
重要

开启缓存后,缓存时长不宜过长,过长可能会导致设备长时间无法接收到最新的更新/灰度指令。

3.3 自定义升级提示弹窗

apkUpdate.setUpdateNotifyListener(new UpdateNotifyListener() {
    @Override
    public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("新版本升级");
        builder.setMessage(customUpdateInfo.getInfo() + "\n版本:" + customUpdateInfo.getVersion() + "\n安装包大小:" + customUpdateInfo.getSize() + "\n是否强制升级:" + 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 自定义取消强制升级的提示弹窗

apkUpdate.setCancelUpdateNotifyListener(new UpdateNotifyListener() {
    @Override
    public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("强制升级取消提示");
        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 自定义下载完成后的安装提示弹窗

apkUpdate.setInstallUpdateNotifyListener(new UpdateNotifyListener() {
    @Override
    public void onNotify(Activity activity, CustomUpdateInfo customUpdateInfo, final UserAction userAction) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("个性化安装");
        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 自定义更新结果提示

apkUpdate.setUpdateResultListener(new UpdateResultListener() {
    @Override
    public void onFinish(int mode, int errorCode, String errMsg) {
        //不保证ui线程
    }
});

3.7 自定义下载进度回调

apkUpdate.setApkDownloadListener(new ApkDownloadListener() {
    @Override
    public void onPreDownload() {
        //下载开始
    }

    @Override
    public void onDownloadProgress(int progress) {
        //下载进行中
    }

    @Override
    public void onStartFileMd5Valid(String filePath, String fileSize) {
        //开启文件md5校验
    }

    @Override
    public void onFinishFileMd5Valid(boolean success) {
        //完成文件校验
    }

    @Override
    public void onDownloadFinish(String url, String filePath) {
        //下载完成
    }

    @Override
    public void onDownloadError(String url, int errorCode, String msg) {
        //下载出错
    }
});
  • 如果本次下载是从0开始且过程顺利,回调的调用路径为onPreDownload() -> onDownloadProgress() -> onDownloadFinish()/onDownloadError(),其余两个关于文件校验的方法并不会被调用。

  • 如果本次下载是断点续传,则下载完成后会进行文件完整性校验,会回调onStartFileMd5Valid() -> onFinishFileMd5Valid()

3.8 自定义日志接口

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

    }

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

    }

    @Override
    public void e(String smsg {

    }

    @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) {

    }
});

二、混淆配置

如果开启了混淆,需要增加以下配置到您的混淆配置文件中:

#-------------------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{*;}

三、测试验证

在您完成上述步骤之后,您可以通过EMAS控制台新建产品,构建发布并进行测试。