美洽

← 返回

移动应用 SDK for Android

在您阅读此文档之前,我们假定您已经具备了基础的 Android 应用开发经验,并能够理解相关基础概念。

如有疑问,欢迎加入 美洽 SDK 开发 QQ 群:295646206

仓库地址:GitHub Android

*请注意,如果你是旧版 SDK 更换到新版 SDK,我们的推送数据格式统一成了 JSON 格式,具体请参见离线消息推送

ScreenShot

美恰SDKDemo

SDK 工作流程

美洽 SDK 的工作流程如下图所示:

流程图

集成美洽 SDK

// -------------------- 以下三个库是必须依赖的 ----------------------------
compile 'com.meiqia:meiqiasdk:3.4.0@aar'
compile 'com.android.support:support-v4:23.1.1'
compile 'com.squareup.okhttp3:okhttp:3.5.0'
// -------------------- 以上三个库是必须依赖的 ----------------------------

// 目前支持常见的 4 种图片加载库,必须在下面四个图片加载库中选择一个添加依赖
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
//    compile 'com.github.bumptech.glide:glide:3.7.0'
//    compile 'com.squareup.picasso:picasso:2.5.2'
//    compile 'org.xutils:xutils:3.3.36'

1.拷贝 /eclipse/MeiqiaSdk 到工作空间并导入 Eclipse 中

2.选中你自己的工程的根目录 -> 右键 -> 选择 Properties -> 选中 Android -> 点击 Library 右边的的 Add 按钮 -> 选中 MeiqiaSdk -> 点击 OK

3.在你自己的工程的 AndroidManifest.xml 文件中添加以下权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

4.在你自己的工程的 AndroidManifest.xml 文件的 application 结点下加入以下代码

<!--聊天界面-->
<activity
    android:name="com.meiqia.meiqiasdk.activity.MQConversationActivity"
    android:configChanges="keyboardHidden|orientation"
    android:launchMode="singleTop"
    android:screenOrientation="portrait"
    android:theme="@style/MQTheme"
    android:windowSoftInputMode="stateHidden|adjustResize" />

<!--图片查看界面-->
<activity
    android:name="com.meiqia.meiqiasdk.activity.MQPhotoPreviewActivity"
    android:configChanges="keyboardHidden|orientation"
    android:launchMode="singleTop"
    android:screenOrientation="portrait"
    android:theme="@style/MQTheme"
    android:windowSoftInputMode="stateAlwaysHidden" />

<!--图片选择界面-->
<activity
    android:name="com.meiqia.meiqiasdk.activity.MQPhotoPickerActivity"
    android:configChanges="keyboardHidden|orientation"
    android:launchMode="singleTop"
    android:screenOrientation="portrait"
    android:theme="@style/MQTheme"
    android:windowSoftInputMode="stateAlwaysHidden" />
<!--图片选择预览界面-->
<activity
    android:name="com.meiqia.meiqiasdk.activity.MQPhotoPickerPreviewActivity"
    android:configChanges="keyboardHidden|orientation"
    android:launchMode="singleTop"
    android:screenOrientation="portrait"
    android:theme="@style/MQTheme"
    android:windowSoftInputMode="stateAlwaysHidden" />
<!--WebView 界面-->
<activity
    android:name=".activity.MQWebViewActivity"
    android:configChanges="keyboardHidden|orientation"
    android:launchMode="singleTop"
    android:screenOrientation="portrait"
    android:theme="@style/MQTheme"
    android:windowSoftInputMode="stateHidden|adjustResize" />
<!--询前表单-->
<activity
    android:name=".activity.MQInquiryFormActivity"
    android:configChanges="keyboardHidden|orientation"
    android:launchMode="singleTop"
    android:screenOrientation="portrait"
    android:theme="@style/MQTheme"
    android:windowSoftInputMode="stateHidden|adjustResize" />

<activity
    android:name=".activity.MQCollectInfoActivity"
    android:configChanges="keyboardHidden|orientation"
    android:launchMode="singleTop"
    android:screenOrientation="portrait"
    android:theme="@style/MQTheme"
    android:windowSoftInputMode="stateHidden|adjustResize" />

<service android:name="com.meiqia.core.MeiQiaService" />

5.如果你自己的工程中已经添加了 /eclipse/MeiqiaSdk/libs 中的 jar 包,拷贝你自己的工程中对应的 jar 包替换 /eclipse/MeiqiaSdk/libs 中的 jar 包

注意:报 Cannot find the class file for java.nio.file.OpenOption 错的解决方法:Project -> Properties -> Java Build Path -> Libraries -> Add Library -> JRE System Library -> Select Workspace Default (jdk 1.7*)
如果出现编译失败, 请将 Project Build Target 指定到 Android 6.0

使用美洽

MQConfig.init(this, "Your Appkey", new OnInitCallback() {
    @Override
    public void onSuccess(String clientId) {
        Toast.makeText(MainActivity.this, "init success", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onFailure(int code, String message) {
        Toast.makeText(MainActivity.this, "int failure", Toast.LENGTH_SHORT).show();
    }
});

如果您不知道 Appkey ,请使用美洽管理员帐号登录 美洽,在「设置」 -> 「SDK」 菜单中查看。如下图:

获取 Appkey

初始化成功后,就可以直接启动对话界面

Intent intent = new MQIntentBuilder(this).build();
startActivity(intent);

目前是两种模式:
(1) 完全对话模式
无机器人时:如果当前客服不在线,直接聊天界面输入就是留言,客服上线后能够直接回复,如果客服在线,则进入正常客服对话模式。
有机器人时:如果当前客服不在线时,直接聊天界面输入的话,还是由机器人回答,顾客点击留言就会跳转到表单。
(2) 单一表单模式
不管客服是否在线都会进入表单,顾客提交后,不会有聊天的界面。这种主要用于一些 App 只需要用户反馈,不需要直接回复的形式。

startActivity(new Intent(this, MQMessageFormActivity.class));

如果你的 App 需要兼容 Android M,需要处理权限问题。 参考 Demo

============== 下面是可选设置 ==============

绑定开发者用户 id 上线

Intent intent = new MQIntentBuilder(this)
        .setCustomizedId("开发者的 id") // 相同的 id 会被识别为同一个顾客
        .build();
startActivity(intent);

设置顾客信息

HashMap<String, String> clientInfo = new HashMap<>();
clientInfo.put("name", "富坚义博");
clientInfo.put("avatar", "https://s3.cn-north-1.amazonaws.com.cn/pics.meiqia.bucket/1dee88eabfbd7bd4");
clientInfo.put("gender", "");
clientInfo.put("tel", "1300000000");
clientInfo.put("技能1", "休刊");
Intent intent = new MQIntentBuilder(this)
        .setClientInfo(clientInfo)
        .build();
startActivity(intent);

修改顾客信息

HashMap<String, String> updateInfo = new HashMap<>();
updateInfo.put("name", "富坚义博 newName");
MQManager.getInstance(this).updateClientInfo(updateInfo, OnClientInfoCallback());

指定客服分配

Intent intent = new MQIntentBuilder(this)
        .setScheduledAgent(agentId) // agentId 可以从工作台查询
        .build();
startActivity(intent);

指定客服分组分配

Intent intent = new MQIntentBuilder(this)
        .setScheduledGroup(groupId) // groupId 可以从工作台查询
        .build();
startActivity(intent);

设置预发送消息

Intent intent = new MQIntentBuilder(this)
        .setPreSendTextMessage("我是预发送文字消息")
        .setPreSendImageMessage(new File("预发送图片的路径"))
        .build();
startActivity(intent);

设置监听 MQConversationActivity 生命周期的回调接口

MQConfig.setActivityLifecycleCallback(new MQSimpleActivityLifecycleCallback() {
});

API 接口介绍

获取 MQManager 实例后,

MQManager mqManager = MQManager.getInstacne(context);

就可以调用下面的接口:

初始化 SDK 成功后,会默认生成一个顾客,如果没有更改过顾客 id,将以默认生成的顾客上线。

/**
* 设置当前 Client 上线
*
* @param onlineCallback 回调
*/
setCurrentClientOnline(final OnClientOnlineCallback onlineCallback)

开发者可通过 获取当前顾客的 id 接口,取得顾客 id ,保存到开发者的服务端,以此来绑定美洽顾客和开发者用户系统。 如果开发者保存了美洽的顾客 id,可调用如下接口让其上线。调用此接口后,当前可用的顾客即为开发者传的顾客 id。

/**
 * 绑定美洽 id,并设置上线
 *
 * @param mqClientId     美洽 id
 * @param onlineCallback 回调接口
*/
setClientOnlineWithClientId(String mqClientId, final OnClientOnlineCallback onlineCallback)

MQConversationActivity.class 内部调用了此接口,所以可以直接通过 MQIntentBuilder 来构造 intent。

Example:

// 假设 meiqia_id 是美洽生成的顾客 id
Intent intent = new MQIntentBuilder(this)
        .setClientId(meiqia_id)
        .build();
startActivity(intent);

如果开发者不愿保存「美洽顾客 id」来绑定自己的用户系统,也可以将自己的用户 id当做参数,进行顾客的上线,美洽将会为开发者绑定一个顾客,下次开发者直接调用如下接口,就能让这个绑定的顾客上线。
调用此接口后,当前可用的顾客即为该自定义 id 对应的顾客 id。

特别注意:传给美洽的自定义 id 不能为自增长的,否则非常容易受到中间人攻击,此情况的开发者建议保存美洽顾客 id。

/**
* 绑定自定义 id,并设置上线
*
* @param customizedId   自定义 id
* @param onlineCallback 回调接口
*/
setClientOnlineWithCustomizedId(String customizedId, final OnClientOnlineCallback onlineCallback)

MQConversationActivity.class 内部调用了此接口,所以可以直接通过 MQIntentBuilder 来构造 intent。

Example:

// 假设 developer@dev.com 是开发者的用户 id
Intent intent = new MQIntentBuilder(this)
        .setCustomizedId("developer@dev.com") // 相同的 id 会被识别为同一个顾客
        .build();
startActivity(intent);

美洽默认会按照管理员设置的分配方式智能分配客服,但如果需要让来自 App 的顾客指定分配给某个客服或者某组客服。

/**
 * 指定客服或者分组,默认分配全企业
 *
 * @param agentId      指定客服的 id,不指定传 null
 * @param groupId      指定分组的 id,不指定传 null
 * @param scheduleRule 分配规则 MQScheduleRule.java
 */
setScheduledAgentOrGroupWithId(String agentId, String groupId, MQScheduleRule scheduleRule)

分配规则:

MQScheduleRule.REDIRECT_NONE // 指定分配客服失败,则进入留言
MQScheduleRule.REDIRECT_GROUP // 分配给组内的人,分配失败,则进入留言
MQScheduleRule.REDIRECT_ENTERPRISE // 分配给企业随机一个人,分配失败,则进入留言 (默认)

MQConversationActivity.class 内部调用了此接口,所以可以直接通过 MQIntentBuilder 来构造 intent。

Example:

Intent intent = new MQIntentBuilder(this)
        .setScheduledAgent(agentId) // agentId 可以从工作台查询
        .setScheduledGroup(groupId) // groupId 可以从工作台查询
        .setScheduleRule(MQScheduleRule.REDIRECT_ENTERPRISE) // 默认
        .build();
startActivity(intent);

注意:

获取 客服 / 分组 ID

设置顾客离线后,将停止监听客服发送的消息,开发者不会再监听到即时消息广播。

如果设置了顾客离线,并且在美洽工作台配置了推送服务器,则客服发送的消息将会发送给开发者的服务端。

美洽建议:如果退出界面后需要监听客服消息,不设置顾客离线,这样开发者仍能监听到收到消息的广播,以便提醒顾客有新消息。

/**
* 设置顾客离线
* 需要初始化成功后才能调用
* 如果设置了顾客离线,则客服发送的消息将会发送给开发者的推送服务器
*/
setClientOffline()
/**
 * 发送文字消息
 *
 * @param content               消息内容
 * @param onMessageSendCallback 消息状态回调
*/
sendMQTextMessage(String content, final OnMessageSendCallback onMessageSendCallback)
/**
 * 发送图片消息
 *
 * @param localPath             图片的本地路径
 * @param onMessageSendCallback 消息状态回调
 */
sendMQPhotoMessage(String localPath, final OnMessageSendCallback onMessageSendCallback)
/**
 * 发送语音消息
 *
 * @param localPath             语音的本地路径
 * @param onMessageSendCallback 消息状态回调
 */
sendMQVoiceMessage(String localPath, final OnMessageSendCallback onMessageSendCallback)
/**
 * 从服务器获取历史消息
 *
 * @param lastMessageCreateOn  获取该日期之前的消息
 * @param length               获取的消息长度
 * @param onGetMessageListCallback 回调
 */
getMQMessageFromService(final long lastMessageCreateOn, final int length, final OnGetMessageListCallback onGetMessageListCallback)
/**
 * 从服务器获取历史消息
 *
 * @param lastMessageCreateOn  获取该日期之前的消息
 * @param length               获取的消息长度
 * @param onGetMessageListCallback 回调
 */
getMQMessageFromDatabase(final long lastMessageCreateOn, final int length, final OnGetMessageListCallback onGetMessageListCallback)
/**
 * 设置用户的设备唯一标识
 *
 * @param token 唯一标识
 */
registerDeviceToken(String token, OkHttpUtils.OnRegisterDeviceTokenCallback onRegisterDeviceTokenCallback)

App 进入后台后,美洽推送给开发者服务端的消息数据格式中,会有 deviceToken 的字段。

美洽推送消息给开发者服务端的数据格式,可参考 推送消息数据结构

/**
 * 开发者自定义当前顾客的信息,用于展示给客服
 *
 * @param clientInfo           顾客信息
 * @param onClientInfoCallback 回调
 */
setClientInfo(Map<String, String> clientInfo, OnClientInfoCallback onClientInfoCallback)

功能效果展示:

设置顾客信息效果图

为了让客服能更准确帮助用户,开发者可上传不同用户的属性信息。示例如下:

Map<String, String> info = new HashMap<>();
info.put("name", "富坚义博");
info.put("avatar", "https://s3.cn-north-1.amazonaws.com.cn/pics.meiqia.bucket/1dee88eabfbd7bd4");
info.put("gender", "");
info.put("tel", "111111");
info.put("技能1", "休刊");
info.put("技能2", "外出取材");
info.put("技能3", "打麻将");
MQManager.getInstance(context).setClientInfo(info, new OnClientInfoCallback());

MQConversationActivity.class 内部调用了此接口,所以可以直接通过 MQIntentBuilder 来构造 intent。

HashMap<String, String> clientInfo = new HashMap<>();
clientInfo.put("name", "富坚义博");
clientInfo.put("avatar", "https://s3.cn-north-1.amazonaws.com.cn/pics.meiqia.bucket/1dee88eabfbd7bd4");
clientInfo.put("gender", "");
clientInfo.put("tel", "1300000000");
clientInfo.put("技能1", "休刊");
Intent intent = new MQIntentBuilder(this)
        .setClientInfo(clientInfo)
        .build();
startActivity(intent);

以下字段是美洽定义好的,开发者可通过上方提到的接口,直接对下方的字段进行设置:

Key 说明
name 真实姓名
gender 性别
age 年龄
tel 电话
weixin 微信
weibo 微博
address 地址
email 邮件
avatar 头像 URL
tags 标签,数组形式,且必须是企业中已经存在的标签
source 顾客来源
comment 备注
/**
 * 获取当前正在接待的客服信息
 *
 * @return 如果存在,返回当前客服信息;不存在,返回 null
 */
getCurrentAgent()
/**
 * 获取当前顾客的顾客 id,开发者可保存该顾客id,下次使用 setClientOnlineWithMQClientId 接口来让该顾客登陆美洽客服系统
 *
 * @return 当前顾客 id
 */
getCurrentClientId()
/**
 * 获取一个新的顾客
 *
 * @param onGetMQClientIdCallBack 回调
 */
createMQClient(OnGetMQClientIdCallBackOn onGetMQClientIdCallBack)

如果开发者想初始化一个新的顾客,可调用此接口。

该顾客没有任何历史记录及用户信息。

/**
 * 更新消息阅读状态
 *
 * @param messageId 消息id
 * @param isRead    将替换的状态
 */
updateMessage(long messageId, boolean isRead)
/**
 * 结束当前对话
 *
 * @param onEndConversationCallback 回调
 */
endCurrentConversation(OnEndConversationCallback onEndConversationCallback)
/**
 * 将用户正在输入的内容,提供给客服查看。该接口没有调用限制,但每1秒内只会向服务器发送一次数据
 *
 * @param content 正在输入的内容
 */
sendClientInputtingWithContent(String content)
/**
 * 切换当前顾客
 *
 * @param clientIdOrCustomizedId clientId 或者 customized
 * @param simpleCallback         回调
 */
MQManager.getInstance(context).setCurrentClient(String clientIdOrCustomizedId, SimpleCallback simpleCallback);

退出界面后收到的消息,都将算作未读消息。

/**
 * 获取当前 Client 的未读消息
 *
 * @param onGetMessageListCallback 回调
 */
MQManager.getInstance(context).getUnreadMessages(new OnGetMessageListCallback());
/**
 * 获取指定 ClientId 或者 customized 顾客的未读消息
 *
 * @param clientIdOrCustomizedId   clientId 或者 customized
 * @param onGetMessageListCallback 回调
 */
MQManager.getInstance(context).getUnreadMessages(String clientIdOrCustomizedId, new OnGetMessageListCallback());

在未开启 离线消息推送 的情况下,开发者可以通过注册一个 BroadcastReceiver ,监听广播

注意:必须通过 LocalBroadcastManager 注册 和 取消注册 BroadcastReceiver。

Example:

// 注册
LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, intentFilter);
// 取消注册
LocalBroadcastManager.getInstance(this).unregisterReceiver(messageReceiver);

BroadcastReceiver:

public class MessageReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
         // 获取 ACTION
         final String action = intent.getAction();
         // 接收新消息
         if (MQMessageManager.ACTION_NEW_MESSAGE_RECEIVED.equals(action)) {
             // 从 intent 获取消息 id
             String msgId = intent.getStringExtra("msgId");
             // 从 MCMessageManager 获取消息对象
             MQMessageManager messageManager = MQMessageManager.getInstance(context);
             MQMessage message = messageManager.getMQMessage(msgId);
             // do something
         }

         // 客服正在输入
         else if (MQMessageManager.ACTION_AGENT_INPUTTING.equals(action)) {
             // do something
         }

         // 客服转接
         else if (MQMessageManager.ACTION_AGENT_CHANGE_EVENT.equals(action)) {
             // 获取转接后的客服
             MQAgent mqAgent = messageManager.getCurrentAgent();
             // do something
         }
     }
 }
/**
 * 获取 SDK 版本号
 */
getMeiqiaSDKVersion()

离线消息推送

当前仅支持一种推送方案,即美洽服务端发送消息至开发者的服务端,开发者再推送消息到 App。

推送消息将会发送至开发者的服务器。

设置服务器地址,请使用美洽管理员帐号登录 美洽,在「设置」 -> 「SDK」中设置。

设置推送地址

/**
 * 设置用户的设备唯一标识
 *
 * @param token 唯一标识
 */
registerDeviceToken(String token, OkHttpUtils.OnRegisterDeviceTokenCallback onRegisterDeviceTokenCallback)

关闭美洽服务,美洽推送给开发者服务端的消息数据格式中,会有 deviceToken 的字段。

关闭服务后,将停止监听消息,此时美洽服务端将会推送消息给开发者提供的消息推送服务端,如下代码:

 MQManager.getInstance(context).closeMeiqiaService();

美洽建议:在 App 后台以后,关闭美洽服务。App 进入前台,如果需要监听客服消息,再开启美洽服务。

开启服务后,将重新监听消息,此时美洽服务端将不会推送消息给开发者提供的消息推送服务端,如下代码:

 MQManager.getInstance(context).openMeiqiaService();

美洽建议:在 App 后台以后,关闭美洽服务。App 进入前台,如果需要监听客服消息,再开启美洽服务。

当有消息需要推送时,美洽服务器会向开发者设置的服务器地址发送推送消息,方法类型为 POST,数据格式为 JSON

发送的请求格式介绍:

request.header.authorization 为数据签名。

request.body 为消息数据,数据结构为:

Key 说明
messageId 消息 id
content 消息内容
messageTime 发送时间
fromName 发送人姓名
deviceToken 发送对象设备的 deviceToken,格式为字符串
clientId 发送对象的顾客 id
customizedId 开发者传的自定义 id
contentType 消息类型 - text/photo/audio
deviceOS 设备系统
customizedData 开发者上传的自定义的属性

开发者可以根据请求中的签名,对推送消息进行数据验证,美洽提供了
Java、Python、Ruby、JavaScript、PHP 5种语言的计算签名的代码,具体请移步 美洽 SDK 3.0 推送的数据结构签名算法

*请注意,如果你是旧版 SDK 更换到新版 SDK,我们的推送数据格式统一成了 JSON 格式,具体请参见离线消息推送

自定义UI

自定义属性说明 说明 默认值
MQConfig.ui.titleGravity 居中对齐 MQTitleGravity.CENTER、居左对齐 MQTitleGravity.LEFT MQTitleGravity.CENTER
自定义属性名 说明
mq_activity_bg Activity 背景颜色资源
mq_activity_title_textColor 标题栏文字颜色资源
mq_chat_left_textColor 聊天界面左边气泡文字颜色资源
mq_chat_right_textColor 聊天界面右边气泡文字颜色资源
mq_chat_left_bubble 聊天界面左边气泡背景颜色资源
mq_chat_right_bubble 聊天界面右边气泡背景颜色资源
mq_ic_back.png 左上角返回箭头图片资源
自定义属性名 说明
MQConfig.ui.backArrowIconResId 标题栏返回箭头图片的资源 ID
MQConfig.ui.titleBackgroundResId 标题栏背景颜色的资源 ID
MQConfig.ui.titleTextColorResId 标题栏文字颜色的资源 ID
MQConfig.ui.leftChatBubbleColorResId 聊天界面左边气泡背景颜色的资源 ID
MQConfig.ui.rightChatBubbleColorResId 聊天界面右边气泡背景颜色的资源 ID
MQConfig.ui.leftChatTextColorResId 聊天界面左边气泡文字颜色的资源 ID
MQConfig.ui.rightChatTextColorResId 聊天界面右边气泡文字颜色的资源 ID

自定义业务

属性说明 说明 默认值
MQConfig.isVoiceSwitchOpen 是否开启语音功能 默认为 true
MQConfig.isSoundSwitchOpen 是否开启声音提示 默认为 true
MQConfig.isLoadMessagesFromNativeOpen 是否加载本地数据 默认为 false
MQConfig.isShowClientAvatar 是否显示客户头像 默认为 false

代码混淆

//      OkHttp相关

-keepattributes Signature
-keepattributes *Annotation*
-keep class com.squareup.okhttp3.** { *; }
-keep interface com.squareup.okhttp3.** { *; }
-dontwarn com.squareup.okhttp3.**


//      Okio相关

-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**

 //     UIL相关

-keep class com.nostra13.universalimageloader.** { *; }
-keepclassmembers class com.nostra13.universalimageloader.** {*;}
-dontwarn com.nostra13.universalimageloader.**

//      Glide相关

-keep class com.bumptech.glide.Glide { *; }
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}
-dontwarn com.bumptech.glide.**

 //     Picasso相关

-keep class com.squareup.picasso.Picasso { *; }
-dontwarn com.squareup.okhttp.**
-dontwarn com.squareup.picasso.**

 //     xUtils3相关

-keepattributes Signature,*Annotation*
-keep public class org.xutils.** {
    public protected *;
}
-keep public interface org.xutils.** {
    public protected *;
}
-keepclassmembers class * extends org.xutils.** {
    public protected *;
}
-keepclassmembers @org.xutils.db.annotation.* class * {*;}
-keepclassmembers @org.xutils.http.annotation.* class * {*;}
-keepclassmembers class * {
    @org.xutils.view.annotation.Event <methods>;
}
-dontwarn org.xutils.**