本文介绍如何进行 SDK 初始化,建立设备与云端的连接。

设备认证

SDK支持的设备认证方案如下:

设备密钥认证方式

设备密钥认证方式指设备在连接物联网平台时需要使用平台颁发的ProductKey、DeviceName、DeviceSecret进行设备认证,每个设备的设备密钥不能一样,否则会出现一个设备上线导致另外一个设备下线的情况。

注:有时候设备密钥也被称为三元组。

开发者需要实现初始化失败的设备连接平台重试逻辑,如果设备连接物联网平台成功,SDK发现MQTT连接断开时会自动尝试连接物联网平台。

初始化代码如下所示:

/**
 * 设置设备三元组信息
 */
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = productKey;// 产品类型
deviceInfo.deviceName = deviceName;// 设备名称
deviceInfo.deviceSecret = deviceSecret;// 设备密钥

/**
 * 设置设备当前的初始状态值,属性需要和云端创建的物模型属性一致
 * 如果这里什么属性都不填,物模型就没有当前设备相关属性的初始值。
 * 用户调用物模型上报接口之后,物模型会有相关数据缓存。
 */
Map<String, ValueWrapper> propertyValues = new HashMap<>();
// 示例
// propertyValues.put("LightSwitch", new ValueWrapper.BooleanValueWrapper(0));

IoTMqttClientConfig clientConfig = new IoTMqttClientConfig(productKey, deviceName, deviceSecret);

LinkKitInitParams params = new LinkKitInitParams();
params.deviceInfo = deviceInfo;
params.propertyValues = propertyValues;
params.mqttClientConfig = clientConfig;
/**
 * 设备初始化建联
 * onError 初始化建联失败,需要用户重试初始化。如因网络问题导致初始化失败。
 * onInitDone 初始化成功
 */
LinkKit.getInstance().init(context, params, new ILinkKitConnectListener() {
    @Override
    public void onError(AError error) {
        // 初始化失败 error包含初始化错误信息 
    }

    @Override
    public void onInitDone(Object data) {
        // 初始化成功 data 作为预留参数
    }
});

动态注册

由于每个设备的DeviceName、DeviceSecret都是不同的,有的产品在产线上为每个设备烧写不同的设备密钥存在难度,为了简化厂商产线的设备密钥烧写难度,阿里云物联网平台提供了动态注册的方案。

动态注册指设备在出厂前烧写了ProductKey、ProductSecret,并使用设备已有的某个唯一标识用作DeviceName,通常为设备的MAC地址或者SN。由于ProductKey、ProductSecret对同一个产品的所有设备来说都是相同的,而设备即使不连接阿里云也需要烧写MAC地址或者SN,所以厂商可以将ProductKey、ProductSecret固化在设备的固件中,这就意味着设备厂商在产线上无需烧写设备密钥。

动态注册将会从云端获取设备的DeviceSecret,然后设备调用SDK初始化使用上面的设备密钥认证方式进行设备认证。设备使用设备密钥连接物联网平台成功之后,出于安全考虑平台不允许设备再次通过动态注册获取DeviceSecret,所以设备需要将收到的DeviceSecret持久化存储,确保设备进行OTA升级、配置清除之后仍然存在

下面是动态注册的代码示例:

//  #######  一型一密动态注册接口开始 ######
/**
 * 注意:动态注册成功,设备上线之后,不能再次执行动态注册,云端会返回已注册错误信息。
 *   因此用户在编程时首先需要判断设备是否已获取过deviceSecret,没有获取过的情况下再
 *   调用动态注册接口去获取deviceSecret
 */
DeviceInfo myDeviceInfo = new DeviceInfo();
myDeviceInfo.productKey = productKey;
myDeviceInfo.deviceName = deviceName;
myDeviceInfo.productSecret = productSecret;
LinkKitInitParams params = new LinkKitInitParams();
params.deviceInfo = myDeviceInfo;
// 设置动态注册请求 path 和 域名,域名使用默认即可
HubApiRequest hubApiRequest = new HubApiRequest();
hubApiRequest.path = "/auth/register/device";
LinkKit.getInstance().deviceRegister(context, params, hubApiRequest, new IConnectSendListener() {
    @Override
    public void onResponse(ARequest aRequest, AResponse aResponse) {
        // aRequest 用户的请求数据
        if (aResponse != null && aResponse.data != null) {
            ResponseModel<Map<String, String>> response = JSONObject.parseObject(aResponse.data.toString(),
                    new TypeReference<ResponseModel<Map<String, String>>>() {
                    }.getType());
            if ("200".equals(response.code) && response.data != null && response.data.containsKey("deviceSecret") &&
                    !TextUtils.isEmpty(response.data.get("deviceSecret"))) {
                deviceInfo.deviceSecret = response.data.get("deviceSecret");
                // getDeviceSecret success, to build connection.
                // 持久化 deviceSecret 初始化建联的时候需要
                // TODO  用户需要按照实际场景持久化设备的三元组信息,用于后续的连接
                // 成功获取 deviceSecret,调用初始化接口建联
                // TODO 调用设备初始化建联
            }
        }
    }

    @Override
    public void onFailure(ARequest aRequest, AError aError) {
        Log.d(TAG, "onFailure() called with: aRequest = [" + aRequest + "], aError = [" + aError + "]");
    }
});
//  ####### 一型一密动态注册接口结束  ######

ID2设备认证

SDK初始化的时候添加以下设置:

/**
 * 云端创建使用 iTLS 认证方式的设备采用这种方式初始化
 * 产品需要到 ID2 授权
 */
IoTMqttClientConfig clientConfig = new IoTMqttClientConfig(productKey, deviceName, deviceSecret);
clientConfig.channelHost = productKey + ".itls.cn-shanghai.aliyuncs.com:1883";
clientConfig.productSecret = productSecret;
clientConfig.secureMode = 8;
linkKitInitParams.mqttClientConfig = clientConfig;

SDK 反初始化

如果需要注销初始化,调用如下接口。反初始化为同步接口,需要确保init的时候,上一次的deinit已经执行完成。如果正在deinit的时候执行init,会返回init失败。重复init也是会返回init失败的。

// 取消注册 notifyListener,notifyListener对象需和注册的时候是同一个对象
LinkKit.getInstance().unRegisterOnPushListener(notifyListener);
LinkKit.getInstance().deinit();

连接状态监听

如果需要监听设备的上下线信息,云端下发的所有数据,可以设置以下监听器。

IConnectNotifyListener notifyListener = new IConnectNotifyListener() {
    @Override
    public void onNotify(String connectId, String topic, AMessage aMessage) {
        // 云端下行数据回调
        // connectId 连接类型 topic 下行 topic; aMessage 下行数据
        // 数据解析如下:
        //String pushData = new String((byte[]) aMessage.data);
        // pushData 示例  {"method":"thing.service.test_service","id":"123374967","params":{"vv":60},"version":"1.0.0"}
        // method 服务类型; params 下推数据内容    
}

    @Override
    public boolean shouldHandle(String connectId, String topic) {
        // 选择是否不处理某个 topic 的下行数据
        // 如果不处理某个topic,则onNotify不会收到对应topic的下行数据
        return true; //TODO 根基实际情况设置
    }

    @Override
    public void onConnectStateChange(String connectId, ConnectState connectState) {
        // 对应连接类型的连接状态变化回调,具体连接状态参考 SDK ConnectState
    }
}
// 注册下行监听,包括长连接的状态和云端下行的数据
LinkKit.getInstance().registerOnPushListener(notifyListener);

// 取消下行监听,需要确保和注册的是同一个对象
// LinkKit.getInstance().unRegisterOnPushListener(notifyListener);

更多功能

目前支持以下功能的使能,默认都是关闭的,开发者可以根据使用场景做设置

LinkKitInitParams params = new LinkKitInitParams();
// ... 其它设置参数保持和认证参数一致,以下是新增的配置参数

/**
  * 设备是否支持被生活物联网平台APP发现
  * 需要确保开发的APP具备发现该类型设备的权限
*/
IoTDMConfig ioTDMConfig = new IoTDMConfig();
// 是否启用本地通信功能,默认不开启,
// 启用之后会初始化本地通信CoAP相关模块,设备将允许被生活物联网平台的应用发现、绑定、控制,依赖enableThingModel开启
ioTDMConfig.enableLocalCommunication = false;
// 是否启用物模型功能,如果不开启,本地通信功能也不支持
// 默认不开启,开启之后init方法会等到物模型初始化(包含请求云端物模型)完成之后才返回onInitDone
ioTDMConfig.enableThingModel = false;
// 是否启用网关功能
// 默认不开启,开启之后,初始化的时候会初始化网关模块,获取云端网关子设备列表
ioTDMConfig.enableGateway = false;
// LinkKitInitParams 设置ioTDMConfig
params.ioTDMConfig = ioTDMConfig;

其他设置

日志开关

打开SDK内部日志输出开关:

PersistentNet.getInstance().openLog(true);
ALog.setLevel(ALog.LEVEL_DEBUG);

请求域名

云端接口的请求域名,请参考地域和可用区查看支持的域名。

  • MQTT域名设置

SDK初始化的时候添加以下设置。

// 设置 Mqtt 请求域名 LinkKitInitParams 初始化参数
IoTMqttClientConfig clientConfig = new IoTMqttClientConfig(productKey, deviceName, deviceSecret);
// 慎用 设置 mqtt 请求域名,默认 productKey+".iot-as-mqtt.cn-shanghai.aliyuncs.com:1883" ,如果无具体的业务需求,请不要设置。
//clientConfig.channelHost = "xxx";
linkKitInitParams.mqttClientConfig = clientConfig;
  • 一型一密域名设置

SDK动态注册的时候添加以下设置。

HubApiRequest hubApiRequest = new HubApiRequest();
// 一型一密域名 默认"iot-auth.cn-shanghai.aliyuncs.com" 
// hubApiRequest.domain = "xxx";  // 如无特殊需求,不要设置
hubApiRequest.path = "/auth/register/device";

Mqtt 连接参数

  • 设置Mqtt Keep-Alive 时间
// interval 单位秒
MqttConfigure.setKeepAliveInterval(int interval);
  • qos设置
MqttPublishRequest request = new MqttPublishRequest();
// 支持 0 和 1, 默认0
request.qos = 0;
request.isRPC = false;
request.topic = topic.replace("request", "response");
String resId = topic.substring(topic.indexOf("rrpc/request/")+13);
request.msgId = resId;
// TODO 用户根据实际情况填写 仅做参考
request.payloadObj = "{\"id\":\"" + resId + "\", \"code\":\"200\"" + ",\"data\":{} }";
  • cleanSession 设置
IoTMqttClientConfig clientConfig = new IoTMqttClientConfig(productKey, deviceName, deviceSecret);
// 对应 receiveOfflineMsg = !cleanSession, 默认不接受离线消息 
clientConfig.receiveOfflineMsg = true;
  • 设置clientId

clientId定义可以参考官方 MQTT-TCP连接通信文档。

MqttConfigure.clientId = "abcdef003d27";
  • 设置mqtt SDK是否自动重连

automaticReconnect设置为true,mqtt连接成功之后,如果因网络抖动导致mqtt断链,SDK会自动重新建联。如果设置为false,则mqtt断开之后,SDK不会自动重新建联,需要用户根据断链状态反馈,自己触发重连逻辑

MqttConfigure.automaticReconnect = true;

重连实现代码参见:

if(MqttNet.getInstance().getClient() instanceof MqttAsyncClient) {
    ((MqttAsyncClient) MqttNet.getInstance().getClient()).reconnect();
}

获取SDK的版本号

ALog.i(TAG, "sdk version = " + LinkKit.getInstance().getSDKVersion());