快速入门

一、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通道会不存在,调用sendMessagesendMessageWithReply等接口,需要判断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);
        }
    });
}

注:

  1. 目前支持的类型有int、long、float、double、boolean、byte[]等基本类型,支持Int、Long、Float、Double、Boolean、String等基本类。

  2. 调用的接口必须是业务服务端已经存在的接口,如果接口不存在则会报相关错误。

  3. rpc调用必须在长连接通道存在的时候调用。

  4. 客户端调用的接口与服务端暴露的接口,通过appKey关联,如果服务端暴露了名为addInt(String a, String b)的接口,客户端则按照调用示例调用addInt方法即可。

  5. 如果对超时时间无特殊要求,不建议修改超时时间,在客户端网络比较弱的情况下,CMNS底层会多次重试,尽量保证请求成功,如果时间太短,则会容易引起失败。

  6. 客户端声明的RPC interface的每一个方法,最后一个参数必须写PushRpcListener,否则会引起调用失败。

九、客户端通知消息处理

通过CMNS服务端推送 “通知消息” ,会带有五个Intent对象:ActionComponentDataTypeCategory,点击通知中心的点击消息,会通过这五个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>属性增加actiondata,详见上面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;