本文档为EMAS Android SDK快速集成手册。
一、 APP 工程准备
新建或者使用已有的Android工程。
在接入前,确保工程能编译通过,顺利运行后,再开始接入, 避免增加不必要的麻烦。
二、 集成准备
EMAS包括以下SDK能力,可选择性接入:
应用更新SDK:提供应用的完整APK更新能力、动态部署更新/DexPatch补丁更新能力(如接入了)
高可用SDK:提供客户端整个性能、稳定性监控分析,包括crash、性能、日志、埋点等等
WEEX-SDK:一套构建高性能、可扩展的原生应用跨平台开发方案
Atlas组件化框架:提供组件化架构、动态部署、DexPatch热修复能力
2.1 应用信息和SDK配置信息
集成前需获取应用的信息和SDK相关配置信息,SDK初始化需要这些信息才能正常的工作。
以下局部变量名均来自脚手架工程中的EmasInit.java文件,此处仅仅作为说明示例,本文后面会用到。可以将这些数据配置到一个json文件中放置到assets目录,初始化的时候通过读取该文件获取数据(参考脚手架工程里的DemoApplication.java),可以参考脚手架工程中assets目录中的aliyun-emas-services.json文件。
应用基本信息:AppKey和SecretKey,在控制台注册应用后获取。
mAppkey = 10000039
mAppSecret = c7795717b2306055f21fb33418c1d011
SDK配置信息:域名需接入方申请(POC环境的域名配置信息可参考POC Demo)。
统一网关域名: 示例:mMTOPDoman = “aserver.emas-ha.cn”
APP渠道号:开发者自定义。渠道号组成规则(渠道ID@应用英文名_应用os_版本号),示例:mChannelID = “1001@DemoApp_Android_1.0.0”。
高可用SDK长连通道域名:示例:mACCSDoman = “acs.emas-ha.cn”
高可用SDK数据上报域名:示例:mHAUniversalHost = “adash.emas-ha.cn”
高可用SDKOssBucket:示例:mHAOSSBucketName = “emas-ha-remote-log-poc”
高可用SDK公钥:示例:mHARSAPublicKey = “xxxx”
高可用SDK启动Activity:APP的启动Activity。示例:mStartActivity = “com.taobao.demo.WelcomActivity”
2.2 配置依赖下载MAVEN地址
修改文件位置:项目根目录的build.gradle(Demo位置:Android-EMAS-Demo/build.gradle)
修改文件内容:allprojects.repositories中配置maven仓库地址用于拉取EMAS相关SDK
示例:
allprojects{
repositories{
maven{url"http://nexus-ce.emas-poc.com/repository/maven-public/"//SDK中心仓库地址
credentials{
username="xxx"//SDK中心仓库账号密码
password="xxx"
}
}
maven{url"xxx"}//业务模块aar/awb snapshot仓库地址(体验时可填POC产物仓库snapshot地址)
maven{url"xxx"}//业务模块aar/awb release仓库地址(体验时可填POC产物仓库release地址)
}
}
2.3 接入EMAS插件
1、引入插件库
修改文件位置:工程根目录的build.gradle(Demo位置:Android-EMAS-Demo/build.gradle)
修改文件内容:在buildscript中
(1)配置maven仓库地址[maven {url “xxx”}]用于拉取EMAS插件
(2)加入classpath “com.taobao.android.gradle:emas-plugin:2.1.3”(注:如仍使用gradle4.0以下环境,emas插件请使用1.7.2-SNAPSHOT)示例:
buildscript{
repositories{
maven{
url"http://nexus-ce.emas-poc.com/repository/maven-public/"//EMAS SDK中心仓库地址
credentials{
username="xxx"//SDK中心仓库账号密码
password="xxx"
}}//SDK中心仓库地址
maven{url"http://maven.aliyun.com/nexus/content/repositories/google/"}
}
dependencies{
classpath"com.taobao.android.gradle:emas-plugin:2.1.3"
}
}
2、创建common.gradle
新建文件位置: 在app项目根目录新建common.gradle(Demo位置:Android-EMAS-Demo/app/common.gradle)
配置作用:用于从EMAS控制台获取环境信息
文件内容:
defemas_version_name=getEnvValue('EMAS_VERSION_NAME',"1.0")
defemas_version_code=getEnvValue('EMAS_VERSION_CODE',"1")
defemas_base_version=getEnvValue('EMAS_BASE_VERSION',"1")
//设置渠道包
defemas_channel_list=getEnvValue("EMAS_CHANNEL_LIST","")
if(emas_version_code){
android.defaultConfig.versionCode=emas_version_code.toInteger()
println("emas_version_code="+emas_version_code)
}
if(emas_base_version){
println("emas_base_version="+emas_base_version)
}
if(emas_version_name){
android.defaultConfig.versionName=emas_version_name
println("emas_version_name="+emas_version_name)
}
if(emas_channel_list){
println("emas_channel_list="+emas_channel_list)
}
StringgetEnvValue(key,defValue){
defval=System.getProperty(key);
if(null!=val){
returnval;
}
val=System.getenv(key);
if(null!=val){
returnval;
}
returndefValue;
}
3、应用common.gradle及EMAS插件
修改文件位置:app项目根目录的build.gradle(Demo位置:Android-EMAS-Demo/app/build.gradle)
(1)拷贝下文脚本到build.gradle
(2)拷贝完后,去掉工程中所有原有的 apply plugin: ‘com.android.application’
apply plugin:'com.taobao.android.emas'
applyfrom:'common.gradle'
configurations{
providedCompile
all*.excludegroup:'com.taobao.android',module:'tnet-jni'
all*.excludegroup:'com.taobao.android',module:'tlog_adapter'
all*.excludegroup:'com.aliyun.ams',module:'alicloud-android-utdid'
}
configurations.all{
resolutionStrategy{
cacheChangingModulesFor(0,'SECONDS')
cacheChangingModulesFor(0,'SECONDS')
}
}
task wrapper(type:Wrapper){
gradleVersion='3.3'
distributionUrl='http://emas-deploy.oss-cn-hangzhou.aliyuncs.com/gradle-3.3-all.zip'
}
4、在android标签中去掉系统生成的versionCode、versionName配置,加入dexOptions配置
配置作用:防止默认的version覆盖控制台传入的version(EMAS平台在做每次发布时会托管这两个参数达到自动更新的效果)
android{
...
//versionCode 1
//versionName "1.0"
dexOptions{
javaMaxHeapSize='2048m'
additionalParameters=["--no-strict"]
}
...
}
5、 按需配置lib目前平台类型,语言类型支持
android{
...
defaultConfig{
...
ndk{
abiFilters"x86","armeabi"//默认只启用x86,armeabi
}
resConfigs"en","fr"
...
}
...
}
6、 加入插件配置
新建文件位置: 在app项目根目录新建emasConfig.properties(Demo位置:Android-EMAS-Demo/app/emasConfig.properties)
配置作用:插件功能开关
文件内容:
atlas.tBuildConfig.classInject=false
atlas.enhanceConfigs.debug.enabled=false
atlas.enhanceConfigs.release.enabled=false
#构建配置
atlas.tBuildConfig.fastProguard=true
atlas.tBuildConfig.atlasMultiDex=true
atlas.tBuildConfig.mergeOverride=false
atlas.multiDexConfigs.debug.fastMultiDex=true
atlas.multiDexConfigs.release.fastMultiDex=true
#构建渠道包开关
atlas.atlasChannelConfigs.debug.enabled=false
atlas.atlasChannelConfigs.release.enabled=false
7、 加入Gradle官方配置
新建文件位置: 在app项目根目录新建gradle.properties(Demo位置:Android-EMAS-Demo/app/gradle.properties)
配置作用:gradle官方配置,优化构建效率及处理兼容性等
文件内容:
android.enableBuildCache=true
org.gradle.daemon=false
android.enableAapt2=false
2.4 添加SDK依赖
依赖说明
以下步骤会集成以下三个SDK:
1、应用更新SDK(UPDATE SDK)
2、高可用SDK(HA SDK)
3、跨平台SDK(WEEX SDK)
如只需部分能力,可在build.gradle脚本、EmasInit.java、AndroidMenifest.xml中删除掉对应gradle依赖/代码/配置即可。
集成方式
Gradle依赖:标准gradle方式依赖各SDK版本(首次接入推荐采用该方式)
通过EMAS研发平台管理依赖:在EMAS研发平台配置各SDK模块后,可通过平台来发布,更新各SDK模块到APP中。(首次接入不建议采用,对平台模块管理有足够理解后再陆续迁移到平台上)
Gradle依赖
``` dependencies {
/************基础库 START**********/
compile('com.alibaba:fastjson:1.1.54.android@jar'){transitivetrue}
compile('com.taobao.android:mtopsdk_allinone:3.0.8.2-open@jar'){transitivetrue}
compile('com.taobao.android:networksdk:3.3.7-open@jar'){transitivetrue}
compile('com.taobao.android:tnet4android:3.1.14.6@aar'){transitivetrue}
compile('com.taobao.android:utdid4all:1.1.5.3_proguard@jar'){transitivetrue}
/************基础库 END**********/
/************UPDATE START**********/
compile('com.taobao.android:update-datasource:1.0.1-open@jar'){transitivefalse}
compile('com.taobao.android:update-common:1.0.1-open@aar'){transitivefalse}
compile('com.taobao.android:update-manager:1.0.1-open@aar'){transitivefalse}
compile('com.taobao.android:update-adapter:1.0.1-open@jar'){transitivefalse}
compile('com.taobao.android:update-main:1.0.1-open@aar'){transitivefalse}
compile('com.taobao.android:downloader:2.0.2.12@jar'){transitivetrue}
/************UPDATE END**********/
/************高可用 SDK START**********/
compile('com.taobao.android:ut-analytics:1.1.0.1-open@aar'){transitivetrue}
compile('com.alibaba.ha:alihatbadapter:1.1.0.7-open@aar'){
transitivetrue
//exclude group:'com.taobao.android', module:'tlog_uploader_oss'
}
//compile ('com.taobao.android:tlog_uploader_ceph:1.1.0.7-open@aar')
//默认oss通道需要依赖oss的三方库
compile'com.squareup.okhttp3:okhttp:3.4.1@jar'
compile'com.squareup.okio:okio:1.9.0@jar'
compile'com.aliyun.dpa:oss-android-sdk:2.4.2@aar'
/************高可用 SDK END**********/
/************WEEX SDK START**********/
compile("com.taobao.android:weex_sdk:0.18.16.28"){transitivetrue}
compile('com.taobao.android:zcache:0.1.2-open'){transitivetrue}
compile('com.alibaba.mtl:dynamicConfig:0.1.0.10'){transitivetrue}
compile('com.alibaba.mtl:dynamicconfigadapter:0.1.0.10'){transitivetrue}
compile('com.android.support:recyclerview-v7:26.+'){transitivetrue}
compile'com.android.support:appcompat-v7:26.+'
//Weex调试工具,线上发布包无需引入
compile'com.taobao.android:weex_inspector:0.16.15'
compile'com.google.code.findbugs:jsr305:2.0.1'
/************WEEX SDK END**********/
/************WEEX 扩展 START**********/
//EMAS开放组件库
compile("com.emas.weex:emas-weex:1.0.1")
//开源图片库
compile'com.facebook.fresco:fresco:1.5.0'
//加载gif动图需添加此库
compile'com.facebook.fresco:animated-gif:1.5.0'
//加载webp动图需添加此库
compile'com.facebook.fresco:animated-webp:1.5.0'
//支持webp需添加此库
compile'com.facebook.fresco:webpsupport:1.5.0'
/************WEEX 扩展 END**********/
/************通道服务 SDK START**********/
compile('com.taobao.android:accs_sdk_taobao:3.3.3.1-open'){transitivetrue}
/************通道服务 SDK START**********/
}
```通过EMAS研发平台集成。
在研发平台新建模块(Maven坐标和版本信息参考下面gradle依赖信息), 再将模块关联到EMAS研发平台上的指定应用即可。参考研发平台使用手册模块管理。
三、 SDK初始化
1、拷贝以下配置到AndroidManifest.xml中对应的位置
android:name="com.taobao.accs.data.MsgDistributeService"
android:exported="false">
2、拷贝EmasInit.java类到工程(或者直接拷贝脚手架项目里面该文件)
packagecom.xxx.xxx;
importandroid.app.Application;
importandroid.content.Context;
importandroid.content.res.Resources;
importandroid.text.TextUtils;
importandroid.util.Log;
importcom.alibaba.dynamic.DynamicSdk;
importcom.alibaba.dynamicconfigadapter.DefaultDynamicSDKEngine;
importcom.alibaba.ha.adapter.AliHaAdapter;
importcom.alibaba.ha.adapter.AliHaConfig;
importcom.alibaba.ha.adapter.Plugin;
importcom.alibaba.ha.adapter.Sampling;
importcom.taobao.accs.ACCSClient;
importcom.taobao.accs.ACCSManager;
importcom.taobao.accs.AccsClientConfig;
importcom.taobao.accs.AccsException;
importcom.taobao.accs.IAppReceiver;
importcom.taobao.accs.common.Constants;
importcom.taobao.accs.utl.ALog;
importcom.taobao.update.adapter.UpdateAdapter;
importcom.taobao.update.apk.ApkUpdater;
importcom.taobao.update.common.Config;
importcom.taobao.update.common.framework.UpdateRuntime;
importcom.taobao.update.datasource.UpdateDataSource;
importcom.taobao.weex.InitConfig;
importcom.taobao.weex.WXSDKEngine;
importcom.taobao.zcache.ZCache;
importcom.taobao.zcache.config.ConfigOrigin;
importcom.taobao.zcache.config.EnvEnum;
importcom.taobao.zcache.config.ZCacheConfigManager;
importcom.taobao.zcache.utils.ILog;
importcom.taobao.zcache.utils.ZLog;
importorg.android.spdy.SpdyProtocol;
importjava.util.ArrayList;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
importanet.channel.SessionCenter;
importanet.channel.strategy.ConnEvent;
importanet.channel.strategy.ConnProtocol;
importanet.channel.strategy.IConnStrategy;
importanet.channel.strategy.IStrategyInstance;
importanet.channel.strategy.IStrategyListener;
importanet.channel.strategy.StrategyCenter;
importanet.channel.strategy.dispatch.HttpDispatcher;
importanetwork.channel.config.NetworkConfigCenter;
importanetwork.channel.http.NetworkSdkSetting;
importmtopsdk.common.util.TBSdkLog;
importmtopsdk.mtop.domain.EnvModeEnum;
importmtopsdk.mtop.global.SwitchConfig;
importmtopsdk.mtop.intf.Mtop;
importmtopsdk.mtop.intf.MtopEnablePropertyType;
importmtopsdk.mtop.intf.MtopSetting;
importmtopsdk.security.LocalInnerSignImpl;
/**
* Created by jason on 18/1/15.
*/
publicclassEmasInit{
publicstaticfinalintDEBUG=1;//测试环境
publicstaticfinalintRELEASE=2;//发布环境
/*配置信息*/
protectedStringmAppkey="20000062";//"10000066";//"10000039";//"10000078";//"60039748";
protectedStringmAppSecret="b8bc185ec225c3e784f1a0dddaaa6694";//"1426c10c5ce57d6cb29e016a816421a7";//"c7795717b2306055f21fb33418c1d011";//"2e00a7e9ab2048daabd4977170d37c4a";//"ab5ff148782b467bb0b310c4acd70abd"//"fe240d4b8f4b31283863cc9d707e2cb1"
protectedStringmCacheURL="http://publish-poc.emas-ha.cn/eweex/";
protectedStringmACCSDoman="accs-pre-k8s-poc.emas-poc.com";
protectedMapmIPStrategy;
protectedStringmMTOPDoman="aserver-pre-k8s-poc.emas-poc.com:30080";
protectedStringmHAUniversalHost="adash-pre-k8s-poc.emas-poc.com:32080";
protectedStringmHAOSSBucketName="emas-ha-remote-log-poc";
protectedStringmHARSAPublicKey;
protectedStringmStartActivity="com.taobao.demo.WelcomActivity";
protectedStringmChannelID="1001@DemoApp_Android_"+BuildConfig.VERSION_NAME;
protectedStringPUSH_TAG="POC";
protectedbooleanmUseHttp=false;
protectedintmEnv=RELEASE;
privateApplicationmApplication;
privatestaticfinalStringTAG="EmasInit";
/**
* 切换成单例
*/
privatestaticclassCreateInstance{
privatestaticEmasInitinstance=newEmasInit();
}
publicstaticEmasInitgetInstance(){
returnCreateInstance.instance;
}
privateEmasInit(){
//不能生成对象
}
//先设置mApplication
publicEmasInitsetmApplication(Applicationapplication){
this.mApplication=application;
//初始化
firstInit();
returnthis;
}
privatevoidfirstInit(){
Applicationapplication=mApplication;
StringBuilderbuilder=newStringBuilder();
try{
intid=application.getResources().getIdentifier("ttid","string",application.getPackageName());
if(id>0){
mChannelID=builder.append(application.getString(id))
.append("@")
.append(application.getResources().getString(application.getApplicationInfo().labelRes))
.append("_")
.append("Android")
.append("_")
.append(BuildConfig.VERSION_NAME).toString();
}
}catch(Resources.NotFoundExceptione){
Log.d(TAG,"no channel id in res"+e.toString());
}
}
/********************UPDATE SDK START **************************/
publicvoidinitUpdate(){
initMtop();
Configconfig=newConfig();
config.group=mAppkey+"@android";//AppInfoHelper.getGroup();
config.ttid=mChannelID;
config.isOutApk=false;
config.appName="EMAS Demo";
UpdateAdapterupdateAdapter=newUpdateAdapter();
UpdateDataSource.getInstance().init(mApplication,config.group,config.ttid,config.isOutApk,updateAdapter);
UpdateRuntime.init(mApplication,config.ttid,config.appName,config.group);
ApkUpdaterapkupdate=newApkUpdater();
UpdateDataSource.getInstance().startUpdate(true,false);
}
privatevoidinitMtop(){
if(mEnv==DEBUG){
TBSdkLog.setTLogEnabled(false);
TBSdkLog.setLogEnable(TBSdkLog.LogEnable.DebugEnable);
}
//关闭密文
if(mUseHttp){
NetworkConfigCenter.setSSLEnabled(false);
}
//[option]关闭MTOP请求长链,调用后Mtop请求直接调用NetworkSDK的HttpNetwork发请求
SwitchConfig.getInstance().setGlobalSpdySwitchOpen(false);
//关闭MTOPSDK NewDeviceID逻辑
MtopSetting.setEnableProperty(Mtop.Id.INNER,MtopEnablePropertyType.ENABLE_NEW_DEVICE_ID,false);
//设置自定义全局访问域名
MtopSetting.setMtopDomain(Mtop.Id.INNER,mMTOPDoman,mMTOPDoman,mMTOPDoman);
//设置自定义签名使用的appKey和appSecret
MtopSetting.setISignImpl(Mtop.Id.INNER,newLocalInnerSignImpl(mAppkey,mAppSecret));
MtopSetting.setAppVersion(Mtop.Id.INNER,BuildConfig.VERSION_NAME);
MtopmtopInstance=Mtop.instance(Mtop.Id.INNER,mApplication.getApplicationContext(),mChannelID);
}
/********************UPDATE SDK END **************************/
/********************WEEX SDK START **************************/
publicvoidinitWeex(){
try{
ImagePipelineConfigimageConfig=ImagePipelineConfig.newBuilder(mApplication)
.setDownsampleEnabled(true)
.build();
Fresco.initialize(mApplication,imageConfig);
EmasWeex.Configeconfig=newEmasWeex.Config.Builder()
.setAppkey(mAppkey)
.setAppVersion(BuildConfig.VERSION_NAME)
.setZcacheUrl(mCacheURL).build();
EmasWeex.getInstance().init(mApplication,econfig);
}catch(WXExceptionvar6){
var6.printStackTrace();
}
}
/********************WEEX SDK END **************************/
/********************HA SDK START **************************/
publicvoidinitHA(){
//开启
if(mEnv==DEBUG){
AliHaAdapter.getInstance().openDebug(true);
}
AliHaAdapter.getInstance().changeHost(mHAUniversalHost);
AliHaAdapter.getInstance().tLogService.changeRemoteDebugHost(mHAUniversalHost);
AliHaAdapter.getInstance().tLogService.changeRemoteDebugOssBucket(mHAOSSBucketName);
if(!TextUtils.isEmpty(mHARSAPublicKey)){
AliHaAdapter.getInstance().tLogService.changeRasPublishKey(mHARSAPublicKey);
}
initHACrashreporterAndUt();
AliHaAdapter.getInstance().openHttp(mUseHttp);
initAccs();
AliHaAdapter.getInstance().removePugin(Plugin.crashreporter);//tlog 依赖accs
AliHaAdapter.getInstance().removePugin(Plugin.ut);
AliHaAdapter.getInstance().telescopeService.setBootPath(newString[]{mStartActivity},System.currentTimeMillis());
AliHaAdapter.getInstance().start(buildAliHaConfig());
//AliHaAdapter.getInstance().crashService.addJavaCrashListener(new WeexCrashListener());
}
privateAliHaConfigbuildAliHaConfig(){
//ha初始化
AliHaConfigconfig=newAliHaConfig();
config.isAliyunos=false;
config.appKey=mAppkey;
config.userNick="you need set user name";
config.channel=mChannelID;
config.appVersion=BuildConfig.VERSION_NAME;
config.application=mApplication;
config.context=mApplication;
returnconfig;
}
privatevoidinitHACrashreporterAndUt(){
AliHaAdapter.getInstance().startWithPlugin(buildAliHaConfig(),Plugin.crashreporter);
AliHaAdapter.getInstance().startWithPlugin(buildAliHaConfig(),Plugin.ut);
AliHaAdapter.getInstance().utAppMonitor.changeSampling(Sampling.All);
}
/********************HA SDK END **************************/
/********************ACCS SDK START **************************/
publicfinalstaticStringTEST_SERVICE_ID="4272_mock";
privatefinalstaticMapSERVICES=newHashMap(){
privatestaticfinallongserialVersionUID=2527336442338823324L;
{
put(TEST_SERVICE_ID,"com.taobao.demo.accs.TestAccsService");
}
};
privatevoidinitAccs(){
if(mIPStrategy!=null&&mIPStrategy.size()>0){
initNetwork();
}
booleanisDebug=((mApplication.getApplicationInfo().flags&ApplicationInfo.FLAG_DEBUGGABLE)!=0);
if(isDebug){//debug版本, 打开日志开关, 方便排查问题
ALog.setUseTlog(false);
anet.channel.util.ALog.setUseTlog(false);
}
newThread(){//建议异步进行初始化
@Override
publicvoidrun(){
intenv=Constants.RELEASE;
intpubkey=SpdyProtocol.PUBKEY_PSEQ_EMAS;//SpdyProtocol.PUBKEY_SEQ_TEST
Stringappkey=mAppkey;//"4272"
Stringappsecret=mAppSecret;//"257461a8005f538382640d4894dd193a04d18e1b4a7a5ee214b6d660778d3943"
StringemasHost=mACCSDoman;
SharedPreferencessp=mApplication.getSharedPreferences("emas_accs",Context.MODE_PRIVATE);
Stringkey=sp.getString("appkey",null);
Stringsecret=sp.getString("appsecret",null);
appkey=TextUtils.isEmpty(key)?appkey:key;
appsecret=TextUtils.isEmpty(secret)?appsecret:secret;
try{
ACCSManager.setAppkey(mApplication.getApplicationContext(),mAppkey,env);//兼容老接口 如果有任意地方使用老接口,必须setAppkey
NetworkSdkSetting.init(mApplication.getApplicationContext());
//关闭AMDC请求
HttpDispatcher.getInstance().setEnable(false);
ACCSClient.setEnvironment(mApplication.getApplicationContext(),env);
AwcnConfig.setAccsSessionCreateForbiddenInBg(false);
AccsClientConfigclientConfig=newAccsClientConfig.Builder()
.setAppKey(appkey)
.setAppSecret(appsecret)
.setInappHost(emasHost)
.setInappPubKey(pubkey)
.setTag(AccsClientConfig.DEFAULT_CONFIGTAG)
.setConfigEnv(env)
.build();
ACCSClient.init(mApplication,clientConfig);
ACCSClient.getAccsClient(AccsClientConfig.DEFAULT_CONFIGTAG).bindApp(mChannelID,mAppReceiver);
}catch(AccsExceptione){
ALog.w(TAG,"initDefaultAccs",e);
}
}
}.start();
}
privateIAppReceivermAppReceiver=newIAppReceiver(){
privateStringTAG="mAppReceiver";
@Override
publicvoidonBindApp(interrorCode){
ALog.i(TAG,"onBindApp","errorCode",errorCode);
try{
ACCSClient.getAccsClient(AccsClientConfig.DEFAULT_CONFIGTAG).bindUser("123324234");
}catch(AccsExceptione){
e.printStackTrace();
}
}
@Override
publicvoidonUnbindApp(interrorCode){
ALog.i(TAG,"onUnbindApp","errorCode",errorCode);
}
@Override
publicvoidonBindUser(StringuserId,interrorCode){
ALog.i(TAG,"onBindUser","errorCode",errorCode);
}
@Override
publicvoidonUnbindUser(interrorCode){
ALog.i(TAG,"onUnbindUser","errorCode",errorCode);
}
@Override
publicvoidonSendData(StringdataId,interrorCode){
ALog.i(TAG,"onSendData");
}
@Override
publicvoidonData(StringuserId,StringdataId,byte[]data){
ALog.i(TAG,"onData");
}
@Override
publicStringgetService(StringserviceId){
Stringservice=SERVICES.get(serviceId);
returnservice;
}
@Override
publicMapgetAllServices(){
returnSERVICES;
}
};
privatevoidinitNetwork(){
SessionCenter.init(mApplication);
finalIStrategyInstanceinstance=StrategyCenter.getInstance();
StrategyCenter.setInstance(newIStrategyInstance(){
@Override
publicvoidinitialize(Contextcontext){
instance.initialize(context);
}
@Override
publicvoidswitchEnv(){
instance.switchEnv();
}
@Override
publicvoidsaveData(){
instance.saveData();
}
@Override
publicStringgetFormalizeUrl(StringrawUrlString){
returninstance.getFormalizeUrl(rawUrlString);
}
@Override
publicListgetConnStrategyListByHost(Stringhost){
Stringstrategy=mIPStrategy.get(host);
if(TextUtils.isEmpty(strategy)){
returninstance.getConnStrategyListByHost(host);
}
finalString[]ipPort=strategy.split(":");
Listlist=newArrayList();
IConnStrategyconnStrategy=newIConnStrategy(){
@Override
publicStringgetIp(){
returnipPort[0];
}
@Override
publicintgetIpType(){
return0;
}
@Override
publicintgetIpSource(){
return0;
}
@Override
publicintgetPort(){
returnInteger.parseInt(ipPort[1]);
}
@Override
publicConnProtocolgetProtocol(){
returnConnProtocol.valueOf("http2","0rtt","emas",false);
}
@Override
publicintgetConnectionTimeout(){
return10000;
}
@Override
publicintgetReadTimeout(){
return10000;
}
@Override
publicintgetRetryTimes(){
return1;
}
@Override
publicintgetHeartbeat(){
return0;
}
};
list.add(connStrategy);
returnlist;
}
@Override
publicStringgetSchemeByHost(Stringhost){
returninstance.getSchemeByHost(host);
}
@Override
publicStringgetSchemeByHost(Stringhost,StringdftScheme){
returninstance.getSchemeByHost(host,dftScheme);
}
@Override
publicStringgetCNameByHost(Stringhost){
returninstance.getCNameByHost(host);
}
@Override
publicStringgetClientIp(){
returninstance.getClientIp();
}
@Override
publicvoidnotifyConnEvent(Stringhost,IConnStrategyconnStrategy,ConnEventconnEvent){
instance.notifyConnEvent(host,connStrategy,connEvent);
}
@Override
publicStringgetUnitByHost(Strings){
returnnull;
}
@Override
publicvoidforceRefreshStrategy(Stringhost){
instance.forceRefreshStrategy(host);
}
@Override
publicvoidregisterListener(IStrategyListenerlistener){
instance.registerListener(listener);
}
@Override
publicvoidunregisterListener(IStrategyListenerlistener){
instance.unregisterListener(listener);
}
});
}
/********************ACCS SDK END **************************/
}
3、将应用及SDK配置信息替换掉(或者通过上文提到的通过读取assets中json文件的方式获取,具体可以参考脚手架代码)
示例代码:
privateStringmAppkey="10000032";
privateStringmAppSecret="11e929a1bc36382fcfe613a2aae5cf02";
privateStringmZcachePrefix="http://publish-poc.emas-ha.cn/eweex/";
privateStringmAccsHost="emaspoc-acs.emas-ha.cn";
privateMapmIPStrategy;//一般无需设置
privateStringmMtopHost="emaspoc-aserver.emas-ha.cn";
privateStringmAdashHost="emaspoc-adash.emas-ha.cn";
privateStringmHAOssBucket="emas-ha-remote-log-poc";
privateStringmHAPubKey;
privateStringmStartActivity="com.taobao.demo.WelcomActivity";
privateStringmTTid="1001@
App_Android_"+BuildConfig.VERSION_NAME;
privatebooleanmUseHttp=false;
4、按需调用初始化接口(需要在Application执行)
EmasInitemas=EmasInit.getInstance().setmApplication(this);
//初始化高可用
emas.initHA();
//初始化应用更新
emas.initUpdate();
//初始化Weex
emas.initWeex();
四、使用EMAS-WEEX
EMAS-WEEX是EMAS官方提供的组件库,提供对weex官方sdk的实用封装以及各种实用、通用组件,目的在于降低EMAS用户对于WEEX的接入、使用成本。
以上章节中已经在gradle依赖和初始化中对EMAS-WEEX做了相应的依赖和初始化操作,这里讲解一些开发过程中使用EMAS-WEEX的使用要点,推荐用户使用EMAS-WEEX而不是weex官方sdk。
WeexPageFragment
除了weex官方sdk的Activity用法外,EMAS-WEEX提供了fragment的方式供渲染weex界面,建议直接继承自WeexPageFragment使用。
可以使用WeexPageFragment的newInstanceWithUrl/newInstanceWithTemplate方法渲染一个weex fragment,支持远程和本地文件渲染,渲染后将绘制到最后一参数所指定的view中
WeexPageFragmentfragment;
if(jsSource.startsWith("http")){
fragment=(WeexPageFragment)WeexPageFragment
.newInstanceWithUrl(this,WeexFragment.class,jsSource,getRenderUrl(jsSource),R.id.frame_root_layout);
}else{
fragment=(WeexPageFragment)WeexPageFragment.newInstanceWithTemplate(this,WeexFragment.class,
WXFileUtils.loadAsset(jsSource,mContext),jsSource,
null,null,R.id.frame_root_layout);
}
fragment.setDynamicUrlEnable(true);
降级
WeexPageFragment提供了默认的降级处理机制,当weex脚本因为各种问题出错且。以被降级时,将通过webview来渲染界面提供兜底策略,避免crash发生。只需要调用setEnableDowngrade方法打开开关即可。
publicclassWeexFragmentextendsWeexPageFragment{
@Override
publicvoidonCreate(@NullableBundlesavedInstanceState){
super.onCreate(savedInstanceState);
setEnableDowngrade(true);//打开weex跨平台降级处理
}
}
注意,只有渲染线上url方式的weex界面,降级服务才是可用的,同时需要url服从以下格式:
http://h5.taobao.com?_wx_tpl=http://dotwe.org/raw/dist/df16904d7d5a693b231df97c7c575cc4.bundle.wx
_wx_tpl标记后是需要渲染的weex脚本地址(后缀名js/wx等都可以),_wx_tpl标记前是发生错误时需要降级到的H5地址。
WEEX DEVTOOL
EMAS-WEEX集成了官方WEEX DEVTOOL SDK,可以通过WeexInspectorManager.startInspector开启。
使用示例:
privatevoidhandleScanCodeResult(Stringresult){
Uriuri=Uri.parse(result);
if(uri==null){
Log.e(TAG,"scan result null, return");
}else{
if(WeexInspectorManager.startInspector(uri,mContext)){
Toast.makeText(this,"连接调试模式",Toast.LENGTH_SHORT).show();
}else{
// 展示weex界面
}
}
}
您可以将扫码后的结果传入到handleScanCodeResult方法中做处理。
WEEX DEVTOOL使用方式可以参考 官方文档
五、开启ATLAS(可选)
Atlas是阿里巴巴集团内部广泛使用的APP组件化框架,通过实现组件化来帮助业务独立开发和上线,并提供动态部署能力使业务具备实时更新和发布的特性。
4.1 添加依赖通过EMAS研发平台集成。
在研发平台新建模块(Maven坐标和版本信息参考下面gradle依赖信息), 再将模块关联到EMAS研发平台上的指定应用即可。参考研发平台使用手册模块管理。
Gradle本地集成
修改文件位置:app项目根目录的build.gradle(Demo位置:Android-EMAS-Demo/app/build.gradle)
修改文件内容:configurations中排除mltidex依赖, dependencies中添加atlas相关依赖
示例:
configurations{
...
all*.excludegroup:'com.android.support',module:'multidex'
...
}/************ATLAS START**********/
compile('com.taobao.android:atlas_core:5.0.8.7-rc2-all@aar'){transitivetrue}/****注:如使用gradle4.0以下环境atlas_core请仍使用5.0.8.0版本****/
compile("com.taobao.android:open-update-aar:1.0.1-open@aar"){transitivetrue}
compile("com.taobao.android:downloader:2.0.2.12@jar"){transitivetrue}
4.2 ALTAS配置
1、修改emasConfig.properties
修改文件位置:app根目录的emasConfig.properties(Demo位置:Android-EMAS-Demo/app/emasConfig.properties)
修改文件内容:修改atlas.tBuildConfig.classInject为false, 其他配置直接加入
[]中为需填充的变量,参考示例。
atlas.atlasEnabled=true
atlas.tBuildConfig.autoStartBundles=xxx
atlas.patchConfigs.debug.appSignName=[appkey]@android
atlas.patchConfigs.release.appSignName=[appkey]@android
atlas.tBuildConfig.appCoordinate=[groupId]:[artifactId]
atlas.atlasChannelConfigs.debug.enabled=false
atlas.atlasChannelConfigs.release.enabled=true
示例:
//开启atlas
atlas.atlasEnabled=true
//自启动的bundle列表,值是Bundle的packageName
atlas.tBuildConfig.autoStartBundles=com.taobao.firstbundle
//应用唯一标识
atlas.patchConfigs.debug.appSignName=10000011@android
atlas.patchConfigs.release.appSignName=10000011@android
//动态部署需要AP包(包含基线apk完整信息的包),定义AP包的发布坐标(确保唯一性)
atlas.tBuildConfig.appCoordinate=com.alibaba.emas:AP
atlas.atlasChannelConfigs.debug.enabled=false
atlas.atlasChannelConfigs.release.enabled=true
2、修改build.gradle
修改文件位置:app项目根目录的build.gradle(Demo位置:Android-EMAS-Demo/app/build.gradle)
修改文件内容:拷贝以下片段到build.gradle,注意替换xxx字段
group: 前文所述AP包的
坐标groupId
repositories url: 发布AP基线包maven仓库地址
username/password: maven仓库账号密码
group='xxx.xxx.xxx'
version=android.defaultConfig.versionName+'-SNAPSHOT'
apply plugin:'maven'
apply plugin:'maven-publish'
publishing{
publications{
maven(MavenPublication){
if(project.gradle.startParameter.toString().contains("assembleDebug")){
artifact"${project.buildDir}/outputs/apk/${project.name}-debug.ap"
artifactId"AP-debug"
}else{
artifact"${project.buildDir}/outputs/apk/${project.name}-release.ap"
artifactId"AP-release"
}
}
}
repositories{
if(version.endsWith("-SNAPSHOT")){
maven{
url"xxx"//基线包发布仓库snapshot地址(体验时可填POC产物仓库snapshot地址)
credentials{
username="xxx"
password="xxx"
}
}
}else{
maven{
url"xxx"//基线包发布仓库release地址(体验时可填POC产物仓库release地址)
credentials{
username="xxx"
password="xxx"
}
}
}
}
}
3、修改update sdk初始化方式
修改文件位置:源码目录EmasInit类(Demo位置:Android-EMAS-Demo/app/src/main/java/com.taobao.demo.EmasInit.java)
修改文件内容:替换initUpdate()方法, import对应头文件。
publicvoidinitUpdate(){
initMtop();
Configconfig=newConfig();
config.group=mAppkey+"@android";//AppInfoHelper.getGroup();
config.ttid=mChannelID;
config.isOutApk=false;
config.appName="EMAS Demo";
UpdateManager.getInstance().init(config,
newClassNotFoundInterceptorCallback(){
@Override
publicIntentreturnIntent(Intentintent){
Log.e("APP","returnIntent"+intent.toString());
returnnull;
}
},true);
}
六、新建ATLAS Bundle(可选)ATLAS BUNLDE 源于OSGI规范里面bundle(组件)的概念, 与GOOGLE AAR包的结构类似,可以理解为一个完整的业务模块。
较为独立的业务模块建议转化为Bundle, 基础库(如网路库、图片库等)无需转化,保持为AAR或者JAR库形式。
为了更好实现模块间的并行开发及平台自动构建Bundle模块,Bundle代码需要放在单独的git地址,即不与APP工程同在一个Git project。
5.1 Bundle工程准备
如果已有Android Library工程(AAR/JAR工程),可直接使用。
没有则:
(1)创建Android Project工程
(2)再创建Android Library Module,模块代码放在该Module中
基于Android Studio的Android Library工程目录结构如下:
5.2 引入ATLAS插件
确保5.1准备的工程能编译通过
修改文件位置: 工程根目录build.gradle文件
修改文件内容: 在buildscript中加入以下内容:
buildscript{
repositories{
maven{url SDK_REPOSITORY_URL
credentials{
username=SDK_REPOSITORY_USERNAME
password=SDK_REPOSITORY_PASSWORD
}
}
maven{url"http://maven.aliyun.com/nexus/content/groups/public/"}
maven{url"http://maven.aliyun.com/nexus/content/repositories/google/"}
google()
}
dependencies{
classpath"com.taobao.android:atlasplugin:3.0.1-rc68-3"
}
}
5.3 gradle.properties配置
修改文件位置: 工程根目录gradle.properties文件,如没有则新建
修改文件内容:配置仓库地址及账号
其中 MAVEN_GROUP/MAVEN_ARTIFACTID为Maven仓库的发布坐。MAVEN_GROUP 一般取Bundle包名,例如”com.emas.test”MAVEN_ARTIFACTID 一般取Bundle名字,保证唯一性即可 例如”mybundle”
#### SDK中心仓库
SDK_REPOSITORY_URL=http://nexus-ce.emas-poc.com/repository/maven-public/
SDK_REPOSITORY_USERNAME=developer
SDK_REPOSITORY_PASSWORD=fT&iXg$16jIh##yL
#### 产物仓库
CUSTOM_REPOSITORY_HOST=nexus.emas-ha.cn:8081
CUSTOM_REPOSITORY_USERNAME=developer
CUSTOM_REPOSITORY_PASSWORD=RSs6KfAdXJOrA6rx
#### 发布坐标
MAVEN_GROUP=xxx.xxx.xxx
MAVEN_ARTIFACTID=xxx
5.4 修改build.gradle
修改文件位置: module目录的build.gradle文件(如上图的:mylibrary/build.gradle)
修改文件内容:atlas bundle配置及发布脚本。
拷贝以下内容build.gradle:
apply plugin:'com.taobao.atlas'
apply plugin:'maven-publish'
atlas.bundleConfig.awbBundle=true
defemas_version_name=getEnvValue('EMAS_VERSION_NAME',"1.0.0-SNAPSHOT")
StringgetEnvValue(key,defValue){
defval=System.getProperty(key);
if(null!=val){
returnval;
}
val=System.getenv(key);
if(null!=val){
returnval;
}
returndefValue;
}
group=MAVEN_GROUP
version=emas_version_name
task sourcesJar(type:Jar){
from('src/main/java'){
include'**'
}
classifier='sources'
}
publishing{
publications{
maven(MavenPublication){
if(project.gradle.startParameter.toString().contains("assembleDebug")){
artifact"${project.buildDir}/outputs/awb/${project.name}-debug.awb"
artifactId MAVEN_ARTIFACTID
}else{
artifact"${project.buildDir}/outputs/awb/${project.name}-release.awb"
artifactId MAVEN_ARTIFACTID
}
artifact sourcesJar
pom.withXml{
defdependenciesNode=asNode().appendNode('dependencies')
configurations.compile.allDependencies.each{
if(it.group!=null&&(it.name!=null||"unspecified"==it.name)&&it.version!=null){
defdependencyNode=dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId',it.group)
dependencyNode.appendNode('artifactId',it.name)
dependencyNode.appendNode('version',it.version)
}
}
}
}
}
repositories{
if(version.endsWith("-SNAPSHOT")){
maven{
url"http://"+CUSTOM_REPOSITORY_HOST+"/repository/maven-snapshots/"
credentials{
username=CUSTOM_REPOSITORY_USERNAME
password=CUSTOM_REPOSITORY_PASSWORD
}
}
}else{
maven{
url"http://"+CUSTOM_REPOSITORY_HOST+"/repository/maven-releases"
credentials{
username=CUSTOM_REPOSITORY_USERNAME
password=CUSTOM_REPOSITORY_PASSWORD
}
}
}
}
}
5.5 Bundle构建及发布
本地工程构建及发布指令:./gradlew assembleRelease publish
七、混淆配置
混淆配置参考Demo progurad.cfg文件
Demo git地址:
账号/密码:POCExperience/jkHHliks#al
八、Q&A问题1
/Users/jason/android/TempTestDemo/app/src/main/AndroidManifest.xml:6:9-35Error:
Attributeapplication@allowBackup value=(true)fromAndroidManifest.xml:6:9-35
isalso present at[com.taobao.android:open-update-aar:5.6.5-emax-SNAPSHOT]AndroidManifest.xml:13:9-36value=(false).
Suggestion:add'tools:replace="android:allowBackup"'toelement atAndroidManifest.xml:5:5-19:19tooverride.
解决方法:
在AndroidManifest.xml里manifest标签里加入xmlns:tools=”http://schemas.android.com/tools“application标签里加入tools:replace=”android:allowBackup”
问题2:
接入atlas后怎么确认altas正常接入?
解决方法:
(1)debug包有BridgeApplication 日志
(2)反编译apk, menifest里面application被替换为了android.taobao.atlas.startup.AtlasBridgeApplication
问题3:
UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.RuntimeException:Exceptionparsing classes
at com.android.dx.command.dexer.Main.processClass(Main.java:781)
at com.android.dx.command.dexer.Main.processFileBytes(Main.java:747)
at com.android.dx.command.dexer.Main.access$1200(Main.java:88)
at com.android.dx.command.dexer.Main$FileBytesConsumer.processFileBytes(Main.java:1689)
at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
at com.android.dx.command.dexer.Main.processOne(Main.java:695)
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:592)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:321)
at com.android.dx.command.dexer.Main.run(Main.java:292)
at com.android.builder.internal.compiler.DexWrapper.run(DexWrapper.java:54)
at com.android.builder.core.DexByteCodeConverter.lambda$dexInProcess$0(DexByteCodeConverter.java:174)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
解决方案:
gradle脚本andorid标签中加入以下配置
android{
xxx
dexOptions{
javaMaxHeapSize='2048m'
additionalParameters=["--no-strict"]}
}
九、 高可用SDK接入
本部分包含崩溃分析、远程日志、性能分析的接入。
1、添加依赖
1.1、Maven仓库依赖接入
在app模块的build.gradle的dependencies节点内添加:
compile('com.aliyun.ams:alicloud-android-ha-adapter:1.1.3.3-open')
//集成崩溃分析
compile('com.aliyun.ams:alicloud-android-ha-crashreporter:1.2.3-open')
//集成远程日志
compile('com.aliyun.ams:alicloud-android-tlog:1.1.2.4-open')
//集成性能分析
compile('com.aliyun.ams:alicloud-android-apm:1.0.7.9-open')
2、接入服务
在自定义Application类的onCreate里面启动服务:
publicclassMyApplicationextendsApplication{
@Override
publicvoidonCreate(){
initHa();
}
privatevoidinitHa(){
AliHaConfigconfig=newAliHaConfig();
config.appKey="xxxxxxxx";//appkey
config.appVersion="x.xx";//应用的版本号信箱
config.appSecret="xxxxxxxxxxxx";//appsecret
config.channel="mqc_test";//应用的渠道号标记,自定义
config.userNick=null;
config.application=this;
config.context=getApplicationContext();
config.isAliyunos=false;//是否为yunos
config.rsaPublicKey="xxxxxxx";//tlog公钥, 在控制台下载 aliyun-emas-services.json文件,文件内的appmonitor.tlog.rsaSecret字段即为公钥信息(文件下载方式:在 EMAS控制台-> 应用管理 找到对应的应用,点击应用所在区块右上角菜单内的“配置下载”),必填
AliHaAdapter.getInstance().addPlugin(Plugin.tlog);
AliHaAdapter.getInstance().addPlugin(Plugin.apm);
AliHaAdapter.getInstance().addPlugin(Plugin.crashreporter);
//设置adash网关
AliHaAdapter.getInstance().changeHost(mHAUniversalHost);
//设置tlog网关
TLogService.changeHost(mHAUniversalHost);
//设置oss bucket name
TLogService.changeRemoteDebugOssBucket(mHAOSSBucketName);
AliHaAdapter.getInstance().openDebug(true);
AliHaAdapter.getInstance().start(config);
//配置上传通道,由集成oss/ceph的sdk决定
TLogInitializer.getInstance().setLogUploader(newTLogUploader());
}
}
3、混淆配置
如果开启了混淆,需要根据您接入的sdk情况增加以下配置到您的混淆配置文件中:
#keep crashreporter
-keepclasscom.alibaba.motu.crashreporter.MotuCrashReporter{*;}
-keepclasscom.alibaba.motu.crashreporter.ReporterConfigure{*;}
-keepclasscom.alibaba.motu.crashreporter.utrestapi.UTRestReq{*;}
-keepinterfacecom.alibaba.motu.crashreporter.IUTCrashCaughtListener{*;}
-keepinterfacecom.alibaba.motu.crashreporter.ICrashReportSendListener{*;}
-keepinterfacecom.alibaba.motu.crashreporter.ICrashReportDataListener{*;}
-keepinterfacecom.ut.mini.crashhandler.*{*;}
-keepclasscom.uc.crashsdk.**{*;}
-keepclasscom.alibaba.motu.crashreporter.YouKuCrashReporter{public*;}
-keepattributesExceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
#--------------------------------------------------------------------------------------------------------
#keep tlog
-keepinterfacecom.taobao.tao.log.ITLogController{*;}
-keepclasscom.taobao.tao.log.upload.*{*;}
-keepclasscom.taobao.tao.log.message.*{*;}
-keepclasscom.taobao.tao.log.LogLevel{*;}
-keepclasscom.taobao.tao.log.TLog{*;}
-keepclasscom.taobao.tao.log.TLogConstant{*;}
-keepclasscom.taobao.tao.log.TLogController{*;}
-keepclasscom.taobao.tao.log.TLogInitializer{public*;}
-keepclasscom.taobao.tao.log.TLogUtils{public*;}
-keepclasscom.taobao.tao.log.TLogNative{*;}
-keepclasscom.taobao.tao.log.TLogNative$*{*;}
-keepclasscom.taobao.tao.log.CommandDataCenter{*;}
-keepclasscom.taobao.tao.log.task.PullTask{*;}
-keepclasscom.taobao.tao.log.task.UploadFileTask{*;}
-keepclasscom.taobao.tao.log.upload.LogFileUploadManager{public*;}
-keepclasscom.taobao.tao.log.monitor.**{*;}
#兼容godeye
-keepclasscom.taobao.tao.log.godeye.core.module.*{*;}
-keepclasscom.taobao.tao.log.godeye.GodeyeInitializer{*;}
-keepclasscom.taobao.tao.log.godeye.GodeyeConfig{*;}
-keepclasscom.taobao.tao.log.godeye.core.control.Godeye{*;}
-keepinterfacecom.taobao.tao.log.godeye.core.GodEyeAppListener{*;}
-keepinterfacecom.taobao.tao.log.godeye.core.GodEyeReponse{*;}
-keepinterfacecom.taobao.tao.log.godeye.api.file.FileUploadListener{*;}
-keepattributesExceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
#--------------------------------------------------------------------------------------------------------
#keep apm
-keepclasscom.taobao.monitor.APMLauncher{*;}
-keepclasscom.taobao.monitor.impl.logger.Logger{*;}
-keepclasscom.taobao.monitor.impl.logger.IDataLogger{*;}
-keepclasscom.taobao.monitor.impl.data.AbsWebView{*;}
-keepclasscom.taobao.monitor.impl.data.GlobalStats{*;}
-keepclasscom.taobao.monitor.impl.common.Global{*;}
-keepclasscom.taobao.monitor.impl.data.WebViewProxy{*;}
-keepclasscom.taobao.monitor.impl.logger.Logger{*;}
-keepclasscom.taobao.monitor.impl.processor.pageload.IProcedureManager{*;}
-keepclasscom.taobao.monitor.impl.processor.pageload.ProcedureManagerSetter{*;}
-keepclasscom.taobao.monitor.impl.util.TimeUtils{*;}
-keepclasscom.taobao.monitor.impl.util.TopicUtils{*;}
-keepclasscom.taobao.monitor.impl.common.DynamicConstants{*;}
-keepclasscom.taobao.application.common.data.DeviceHelper{*;}
-keepclasscom.taobao.application.common.impl.AppPreferencesImpl{*;}
-keepclasscom.taobao.monitor.impl.processor.launcher.PageList{*;}
-keepclasscom.taobao.monitor.impl.processor.fragmentload.FragmentInterceptorProxy{*;}
-keepclasscom.taobao.monitor.impl.processor.fragmentload.IFragmentInterceptor{*;}
-keepclasscom.taobao.monitor.impl.logger.DataLoggerUtils{*;}
-keepinterfacecom.taobao.monitor.impl.data.IWebView{*;}
-keepinterfacecom.taobao.monitor.impl.processor.IProcessor{*;}
-keepinterfacecom.taobao.monitor.impl.processor.IProcessorFactory{*;}
-keepinterfacecom.taobao.monitor.impl.logger.IDataLogger{*;}
-keepinterfacecom.taobao.monitor.impl.trace.IDispatcher{*;}
-keepattributesExceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod