一、SDK的引入
1、引入SDK包中的SDK-CMNS.jar。注:如何下载SDK?
2、引入安全保镖,包括:SecurityBodySDK.jar, SecurityGuardSDK.jar, libsgmain.so, libsgsecuritybody.so。
3、拷贝资源文件,包括:安全图片(yw_1222_aicc.jpg),以及两张SDK的图片资源文件( alisdk_cmns_ic_notification.png,alisdk_cmns_ic_large.png)。
4、在AndroidManifest.xml中增加以下内容:
增加权限
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<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" />
注:在Android5.0或者更高版本,android.permission.READ_PHONE_STATE需要动态申请,申请方式为:
class MainActivity {
@Override
public void onCreate() {
...
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.READ_PHONE_STATE}, 0);
}
}
增加receiver
<receiver android:name="com.aliyun.push.api.PushMessageReceiver">
<intent-filter>
<action android:name="com.aliyun.cmns.intent.SEND_NOTIFICATION"/>
<action android:name="com.aliyun.cmns.intent.CANCEL_NOTIFICATION"/>
<action android:name="com.aliyun.cmns.intent.PASSTHROUGH"/>
<action android:name="com.aliyun.cmns.intent.NOTIFICATION_CLICKED"/>
<action android:name="com.aliyun.cmns.intent.NOTIFICATION_REMOVE"/>
</intent-filter>
</receiver>
注:安全保镖,安全图片,资源文件,assets文件,AndroidManifest.xml内容,均需要按照上面方法正确操作,否则将引起CMNS接入失败。
二、SDK初始化
接口原型:
/** CMNS SDK初始化
* @param applicationContext Context 应用Context
* @param listener PushAsyncListener 初始化完成后的回调
*/
void init(Context applicationContext, PushAsyncInitListener listener);
/** CMNS SDK初始化
* @param applicationContext Context 应用Context
* @param authCode String 安全图片的authCode,如图片名称为yw_1222_abc.jpg,则authCode为abc
* @param listener PushAsyncListener 初始化完成后的回调
*/
void init(Context applicationContext, String authCode, PushAsyncInitListener listener);
/** CMNS SDK初始化
* @param applicationContext Context 应用Context
* @param appKeyIndex int 安全图片中cmns的key的位置,SDK中默认位置为1
* @param pAppkey String 业务处理的AppKey
* @param authCode String 安全图片的authCode,如图片名称为yw_1222_abc.jpg,则authCode为abc
* @param listener PushAsyncListener 初始化完成后的回调
*/
void init(Context applicationContext, int appKeyIndex, String pAppKey, String authCode, PushAsyncInitListener listener);
其中PushAsyncInitListener
如下:
public interface PushAsyncInitListener {
/** PushAsyncListener
* @param errorCode int 返回码,0表示成功,错误码详见com.aliyun.aicc.cmns.PushErrorCode
*/
void onInit(int errorCode);
}
调用示例:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
PushClient.getInstance().init(this, new PushAsyncInitListener() {
@Override
public void onInit(final int errorCode) {
Log.d(TAG, "init " + errorCode == PushErrorCode.ERROR_OK ? "success" : "failed");
}
});
}
}
三、获取长连接状态
描述:获取CMNS长连接通道状态(是否在线)。
接口原型:
/**
* @return isConnected boolean 设备是否在线
*/
boolean isConnected();
调用示例:
boolean isConnnected = PushClient.getInstance().isConnecte();
Log.d("Push", "connect status: " + isConnected ? "connected" : "disconnected");
注:在某些情况下,比如网络不好、SSL证书验证失败等情况下,CMNS通道会不存在,调用sendMessage
,sendMessageWithReply
等接口,需要判断CMNS服务的连接状态,如果CMNS通道不存在,需要接入方选择丢弃不发或者等待通道恢复延迟发送。
四、设置CMNS连接状态改变事件监听
描述: 设置CMNS连接状态改变事件监听,可以获取到长连接状态实时变化通知接口原型:
/**
* @param listener PushConnectionStateChangeListener 连接状态改变事件监听
*/
void setConnectionStateChangeListener(PushConnectionStateChangeListener listener);
其中PushConnectionStateChangeListener
为:
public interface PushConnectionStateChangeListener {
// 当连接成功时回调
void onConnect();
// 当断开连接时回调
void onDisconnect();
}
调用示例:
PushClient.getInstance().setConnectionStateChangeListener(new PushConnectionStateChangeListener() {
@Override
public void onConnect() {
Log.d(TAG, "push connect");
}
@Override
public void onDisconnect() {
Log.d(TAG, "push disconnect");
}
});
五、客户端设置消息监听
描述:设置消息监听可以收到CMNS服务端下发的透传消息、以及通知消息的点击回调
接口原型:
/**
* @param listener PushMessageListener 消息回调
*/
void setMessageListener(PushMessageListener listener);
其中相关类与方法如下:
interface PushMessageListener {
/** 当收到CMNS服务端透传的消息时回调
* @param mid long 当前消息的消息id,建议将此消息id打印出来,方便调试
* @param payload String 透传的消息主体
*/
void onPassThroughMessage(long mid, String payload);
/** 当用户点击通知中心消息时回调
* @param mid long 当前消息的消息id,建议将此消息id打印出来,方便调试
* @param payload String 透传的消息主体
*/
void onNotificationClicked(long mid, String payload);
}
调用示例:
PushClient.getInstance().setMessageListener(new PushMessageListener() {
// 透传消息回调,PushMessage为CMNS服务端下发的回调消息。
@Override
public void onPassThroughMessage(long mid, String payload) {
Log.d(TAG, "pass mid: " + mid + "\tmsg: " + payload);
}
// 通知中心点击事件,在用户手动点击通知中心消息后回调,PushMessage为CMNS服务端下发的通知消息。
@Override
public void onNotificationClicked(long mid, String payload) {
Log.d(TAG, "click mid: " + mid + "\tmsg: " + payload);
}
});
注:setMessageListenr
接口需要在Application.onCreate
中设置,因为推送消息会通过com.aliyun.cmns.CMNSMessageReceiver
唤醒应用,如果不在Application.onCreate
里面调用,唤醒状态无法设置消息监听者,应用就无法感知消息回调。
六、非可靠消息上行
描述:上传消息到应用appKey关联的mns消息队列上,此接口适用于不关注成功性的接口,比如上报位置信息等(CMNS底层也会多次重传,尽量保证上传成功率,只是没有成功与否的返回)。
接口原型:
/**
* @param tag String 上行消息的tag,服务端用来过滤
* @param msg String 上行的消息
*/
void sendMessage(String tag, String msg);
调用示例:
PushClient.getInstance().sendMessage("place", "在杭州");
七、可靠消息上行
描述:上传消息到应用appKey关联的mns消息队列上,此接口会在服务端将消息放到mns后返回,会告知客户端消息是否成功上传。
接口原型:
/**
* @param tag String 上行消息的tag,服务端做过滤消息用
* @param msg String 上行的消息
* @param listener PushSendMessageListener 上行消息的回调
*/
void sendMessageWithReply(String tag, String msg, PushSendMessageListener listener);
接口需要的相关类与方法如下:
interface PushSendMessageListener {
/** 上行消息回调
* @param errorCode int 上行消息错误码,详见PushErrorCode.java
*/
void onSendMessage(int errorCode);
}
调用示例:
PushClient.getInstance().sendMessageWithReply("place", "在杭州", new PushSendMessageListener() {
@Override
public void onSendMessage(final int errorCode) {
Log.d(TAG, "onSendMessage: " + errorCode);
}
});
注:如果网络状态不佳,上行消息可能会超时(可能数据已经上传到服务端,但是客户端没有收到回包),如果选择重发,CMNS服务端可能会收到多条一样的数据,此时需要应用服务端到mns中取数据时做数据去重。
八、RPC 远程服务调用
描述:调用业务方服务端注册在CMNS服务端的HTTP接口。
接口原型:
/** 获取服务端远程服务
* @param interfaceClass 远程服务的接口名
*/
<T> T getRemoteRpcService(Class<T> interfaceClass);
接口需要的相关类与方法如下:
// Rpc调用回调
public interface PushRpcListener {
/**
* @param errorCode int rpc调用返回的错误码
* @param response String rpc调用返回的response
*/
void onReply(int errorCode, String response);
}
// Rpc设置
public @interface PushRpcSetting {
/** 协议超时时间设置
* return int 当前协议的超时时间,默认为30s
*/
int timeout() default 30000;
}
调用示例:
public interface TestRemoteRpcService {
@PushRcpSetting (timeout = 3000)
void addInt(int a, int b, int c, PushRpcListener listener);
void addString(String a, String b, PushRpcListener listener);
}
void callRemoteFunc() {
TestRemoteRpcService rpcService = PushClient.getInstance().getRemoteRpcService(TestRemoteRpcService.class);
rpcService.addInt(1, 2, 3, new PushRpcListener() {
@Override
public void onReply(int errorCode, String response) {
Log.d(TAG, "addInt result, errorCode: " + errorCode + ", response: " + response);
}
});
rpcService.addString("a", "b", new PushRpcListener() {
@Override
public void onReply(int errorCode, String response) {
Log.d(TAG, "addInt result, errorCode: " + errorCode + ", response: " + response);
}
});
}
注:
目前支持的类型有int、long、float、double、boolean、byte[]等基本类型,支持Int、Long、Float、Double、Boolean、String等基本类。
调用的接口必须是业务服务端已经存在的接口,如果接口不存在则会报相关错误。
rpc调用必须在长连接通道存在的时候调用。
客户端调用的接口与服务端暴露的接口,通过appKey关联,如果服务端暴露了名为
addInt(String a, String b)
的接口,客户端则按照调用示例调用addInt
方法即可。如果对超时时间无特殊要求,不建议修改超时时间,在客户端网络比较弱的情况下,CMNS底层会多次重试,尽量保证请求成功,如果时间太短,则会容易引起失败。
客户端声明的RPC interface的每一个方法,最后一个参数必须写PushRpcListener,否则会引起调用失败。
九、客户端通知消息处理
通过CMNS服务端推送 “通知消息” ,会带有五个Intent对象:Action
,Component
,Data
,Type
,Category
,点击通知中心的点击消息,会通过这五个Intent对象去打开相应的应用页面。
Action(动作) Action规定了Intent要完成的动作,在打开页面的过程中,需要在目标组件的AndroidManifest.xml中声明过滤器规则,将Action加入其中。 默认action是
Intent.ACTION_VIEW
,即android.intent.action.View
,也可以根据具体需求设置其他Action。Action设置方法如下:<activity android:name="MessageClickActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
Component(组件) 如果应用服务端传给CMNS服务端的字段中带有Component这个属性的话,将直接使用它指定的组件。指定了这个属性以后,Intent的其它所有属性都是可选的。 收到带有Component的消息,SDK会取其中的class和package字段放到Intent中,去完成相应的操作。
Category(类别) 如果应用服务端传给CMNS服务端的字段中带有Category这个属性的话,SDK会在Intent中添加多个类别,那就要求被匹配的组件必须同时满足这多个类别,才能匹配成功。
默认category是android.intent.category.DEFAULT,也可以根据具体需求设置其他Category。配置方法如下:
<activity android:name="MessageClickActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.yunos.push.category.DEFAULT" /> </intent-filter> </activity>
Data(数据) Data字段指定了一个Uri,表示需要访问的数据。通常情况下,我们可以使用Action加Data两个属性来描述一个意图。 Data与Action、Category声明方式相同,也是描述在
<intent-filter>
中,设置方法如下:<activity android:name="MessageClickActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http" android:host="yunos.com"/> </intent-filter> </activity>
Type(数据类型) 如果Intent对象中既包含Uri又包含Type,那么,在
<intent-filter>
中也必须二者都包含才能通过测试。 Type属性用于明确指定Data属性的数据类型或MIME类型,但是通常来说,当Intent不指定Data属性时,Type属性才会起作用,否则Android系统将会根据Data属性值来分析数据的类型,所以无需指定Type属性。配置方法如下:
<activity android:name="MessageClickActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:type="audio/mpeg" android:scheme="http" android:host="yunos.com" /> </intent-filter> </activity>
这五个Intent对象可以通过CMNS接口参数pkgContent进行设置,通过这五个对象组合,可以发送以下几种类型的消息:
通知消息-H5消息(只带uri) 点击通知中心里面的H5消息,会使用系统默认的浏览器打开HTTP链接,如果uri不是HTTP格式,系统会默认匹配uri,使用系统默认应用去打开uri,如
uri=taobao://m.taobao.com
,会跳转到淘宝首页。通知消息-打开首页(带package+action) 点击通知中心的消息,会打开应用的默认Activity,需要应用在
AndroidManifest.xml
中Activiy的<intent-filter>
中添加action
。通知消息-应用内消息(带action+package+uri) 点击通知中心里面的应用内消息,会根据
AndroidManifest.xml
中的配置去打开相应的界面,即需要应用在AndroidManifest.xml
中,给相应Activity的<intent-filter>
属性增加action
和data
,详见上面Data
字段的介绍。透传消息(带package+action,其中action=应用包名) 透传消息不会显示在通知中心,会通过SDK的回调接口告知应用,可以在后台运行。
十、常见错误码
// 公共错误码
ERROR_OK = 0;
ERROR_INITIALIZE_LOGGING_FAILED = 1000;
ERROR_CREATE_THREADS_FAILED = 1001;
ERROR_NETWORK_ERROR = -1000;
ERROR_BAD_PARA = -999;
ERROR_SESSION_ERROR = -998;
ERROR_SERVER_RETURN_EMPTY = -997;
ERROR_CANCELLED = -996;
ERROR_RESPONSE_CODE_ERROR = -995;
// AndroidSDK独有 410xx
ERROR_SERVICE_IS_NOT_INITIALIZED = 41002;
ERROR_SYSTEM_SERVICE_IS_NOT_CONNECTED = 41003;
ERROR_SYSTEM_SERVICE_IS_NOT_AVALIABLE = 41004;
ERROR_SDK_SERVICE_IS_NOT_AVALIABLE = 41005;
ERROR_SECURITYBOX_INITIALIZE_FAILED = 41006;
ERROR_SECURITYBOX_GETAPPKEY_ERROR = 41007;
ERROR_UNEXPECT_RUN_ERROR = 41008;
ERROR_NO_FULL_PACKAGE = 41009;
ERROR_NO_SUCH_API = 41010;
ERROR_SERVICE_SHUTDOWN = 41011;
// 本地定义, database获取的失败
ERROR_SQL_ERROR = 50000;
// 本地定义,服务器返回超时
ERROR_TIMEOUT = 50001;
// native error code
ERROR_OOM = 60000;
ERROR_INVALID_SOCKET = 60001;
ERROR_DISCONNECT_BY_USER = 60002;
ERROR_CMNS_NOT_CONNECTED = 60003;
ERROR_NETWORK_NOT_REACHABLE = 60004;
ERROR_UNKNOW = 60005;
// connect error code
ERROR_CONNECT_ERROR_BASE = 70000;
ERROR_UNACCEPTABLE_PROTOCOL_VERSION = 70001; // 版本错误
ERROR_IDENTIFIER_REJECTED = 70002;
ERROR_SERVER_UNAVAILABLE = 70003;
ERROR_INVALID_USER_OR_PASSWORD = 70004;
ERROR_NOT_AUTHORIZED_EXPIRED = 70005; // 账号Token过期
ERROR_NOT_AUTHORIZED_ILLEGAL = 70006; // 账号Token非法
ERROR_ACCOUNT_CENTER_ERR = 70007; // 账号中心异常
ERROR_NO_ACCOUNT_CENTER_FOR_APPKEY = 70008; // 账号中心未配置
ERROR_DUPLICATE_IMEI_OR_UUID = 70009; // IMEI或者UUID重复
ERROR_SID_CENTER_ERR = 70010; // SID 中心异常
ERROR_CLIENTID_IMEI_ILLEGAL = 70011; // IMIE非法
ERROR_CLIENTID_LENGTH_ILLEGAL = 70012;
ERROR_CLIENTID_ISNULL = 70013; // ClientID为空
ERROR_PASSWORD_ISNULL = 70014; // Password为空
ERROR_USERNAME_ISNULL = 70015; // UserName为空
ERROR_USERNAME_ILLEGAL = 70016; // UserName非法
ERROR_USERNAME_LENGTH_ILLEGAL = 70017; // UserName长度非法
ERROR_UUID_ILLEGAL = 70018; // UUID非法
ERROR_UUID_CENTER_ERR = 70019; // UUID中心异常
ERROR_APPKEY_ILLEGAL = 70020;
ERROR_DB_REDIS_ERR = 70021;
ERROR_EMPTY_DEVICE_INFO = 70022;
ERROR_CERT_ILLEGAL = 70023;
ERROR_INVALID_CONNECTION = 70024;
ERROR_CONNECTION_NOT_CONACK = 70025;
ERROR_CLOSED_BY_CLIENT = 70026;
ERROR_CLOSED_BY_SINGLESIGNON = 70027;
ERROR_CLIENT_NO_RESPONE = 70028;
ERROR_PACKAGENAME_ILLEGAL = 70029;
ERROR_SECRET_NOT_EXIST = 70030;
ERROR_SIGN_TIMER_ILLEGAL = 70031;
ERROR_SIGN_ILLEGAL = 70032;
// response error code
ERROR_RESPONSE_ERROR_BASE = 80000;
ERROR_INVALID_JSON = 80001;
ERROR_APPKEY_ERROR = 80010;
ERROR_SECRET_ILLEGAL = 80011;
ERROR_NO_UPLOAD_PERMISSION = 80012;
ERROR_PAPPKEY_ILLEGAL = 80013;
ERROR_PAPPKEY_INVALID = 80014;
ERROR_TIMESTAMP_ILLEGAL = 80020;
ERROR_TAG_LENGTH_ILLEGAL = 80030;
ERROR_TAG_COUNT_ILLEGAL = 80031;
ERROR_RPC_SUCCESS = 90200;
ERROR_RPC_PARAMETER_EMPTY = 90201;
ERROR_RPC_PARAMETER_INVALID = 90202;
ERROR_RPC_APP_SERVER_NOT_EXIST = 90203;
ERROR_RPC_APP_CLIENT_NOT_EXIST = 90204;
ERROR_RPC_DEVICE_NOT_EXIST = 90205;
ERROR_RPC_DEVICE_NOT_ONLINE = 90206;
ERROR_RPC_METHOD_NOT_EXIST = 90207;
ERROR_RPC_PARAM_COUNT_NOT_MATCH = 90208;
ERROR_RPC_CLIENT_SIGN_ERROR = 90209;
ERROR_RPC_CLIENT_TIMEOUT = 90210;
ERROR_RPC_CLIENT_UNSUPPORT_PARAM_TYPE = 90211;
ERROR_RPC_SYSTEM_ERROR = 90212;
ERROR_RPC_SERVER_SIGN_ERROR = 90213;
ERROR_RPC_SERVER_DECRYPT_ERROR = 90214;
ERROR_RPC_SERVER_TIMESTAMP_INVALID = 90215;
ERROR_RPC_SERVER_DESERIALIZE_ERROR = 90216;
ERROR_RPC_SERVICE_METHOD_EXIST = 90217;
ERROR_RPC_NO_PRIVILEGE = 90406;
ERROR_RPC_SERVER_REQUEST_TIMEOUT = 90407;
ERROR_RPC_SERVER_SYSTEM_ERROR = 90500;