认证与连接
设备接入物联网平台之前,需通过身份认证。本文介绍如何将Android Link SDK进行初始化,实现将设备接入物联网平台。
前提条件
已创建产品和设备。具体操作,请参见创建产品和设备。
已获取Demo,下载链接:Android Link SDK Demo。
已获取设备的认证信息以及设备的接入域名。具体请参见需配置的文件参数说明。
Android Link SDK支持设备密钥和ID²认证进行设备身份认证。
设备密钥
设备密钥分为以下几种认证方式:
认证方式
注册方式
说明
相关文档
一机一密
不涉及
每台设备烧录自己的设备证书(ProductKey、DeviceName和DeviceSecret)。
一型一密
预注册
同一产品下设备烧录相同产品证书(ProductKey和ProductSecret)。
产品需开启动态注册功能。
设备通过动态注册获取DeviceSecret。
说明一型一密预注册和免预注册的区别,请参见预注册和免预注册的区别。
免预注册
同一产品下设备烧录相同产品证书(ProductKey和ProductSecret)。
产品需开启动态注册功能。
设备通过动态注册获取ClientID与DeviceToken的组合。
ID²认证:ID²是一种物联网设备的可信身份标识,具备不可篡改、不可伪造等安全属性。
更多信息,请参见ID²认证。
背景信息
具体代码实现请参见Demo中的InitManager.java。
Demo使用说明
如果设备初始化失败,需再次对SDK进行初始化。当设备由于网络等原因导致与物联网平台断开连接时,SDK会自动尝试与物联网平台建立连接。
修改Android Link SDK Demo中
./app/src/main/res/raw/deviceinfo
文件的参数,实现设备的快速接入。表格中提示”需要填写“的参数进行填写,为否的参数保持默认设置不需修改。使用新版公共实例、企业实例接入的用户,控制台的实例详情中带有端口号信息,可直接拷贝使用。格式如下:
{instanceid}.mqtt.iothub.aliyuncs.com:443
使用旧版实例的用户,控制台的实例详情中没有端口号信息, 请拷贝后自行添加端口号信息。实例接入参考格式如下:
{YourProductKey}.iot-as-mqtt.{region}.aliyuncs.com:443
具体请参见查看实例终端节点。
参数 | 一机一密 | 一型一密(预注册) | 一型一密(免预注册) |
productKey (物联网平台为产品颁发的标识符) | 需要填写 | 需要填写 | 需要填写 |
deviceName (设备在产品内的标识符) | 需要填写 | 需要填写 | 需要填写 |
productSecret (产品密钥) | 否 | 需要填写 | 需要填写 |
deviceSecret (设备密钥) | 需要填写 | 否 | 否 |
registerType (一型一密的方式,预注册或免预注册) | 否 | 否 | 需要填写,内容为字符串regnwl。 |
instanceId (实例ID,对企业实例和新版本公共实例适用,具体请参见实例概述) | 否 | 需要填写 | 需要填写 |
mqttHost (MQTT接入域名) | 需要填写(上海区域除外),需要添加端口号信息。 |
一机一密
一机一密的设备认证方式的示例代码如下:
final LinkKitInitParams params = new LinkKitInitParams();
//Step1: 构造设备认证信息
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = productKey; // 产品类型
deviceInfo.deviceName = deviceName; // 设备名称
deviceInfo.deviceSecret = deviceSecret; // 设备密钥
deviceInfo.productSecret = productSecret; // 产品密钥
params.deviceInfo = deviceInfo;
//Step2: 全局默认域名
IoTApiClientConfig userData = new IoTApiClientConfig();
params.connectConfig = userData;
//Step3: 物模型缓存
Map<String, ValueWrapper> propertyValues = new HashMap<>();
/**
* 物模型的数据会缓存到该字段中,不可删除或者设置为空,否则功能会异常
* 用户调用物模型上报接口之后,物模型会有相关数据缓存。
*/
params.propertyValues = propertyValues;
//Step4: mqtt设置
/**
* 慎用
* Mqtt 相关参数设置,包括接入点等信息,具体参见deviceinfo文件说明
* 域名、产品密钥、认证安全模式等;
*/
IoTMqttClientConfig clientConfig = new IoTMqttClientConfig(productKey, deviceName, deviceSecret);
clientConfig.receiveOfflineMsg = false;//cleanSession=1 不接受离线消息
//mqtt接入点信息
clientConfig.channelHost = mqttHost;
params.mqttClientConfig = clientConfig;
//Step5: 高阶功能配置,除物模型外,其余默认均为关闭状态
IoTDMConfig ioTDMConfig = new IoTDMConfig();
// 默认开启物模型功能,物模型初始化(包含请求云端物模型)完成后才返回onInitDone
ioTDMConfig.enableThingModel = true;
// 默认不开启网关功能,开启之后,初始化的时候会初始化网关模块,获取云端网关子设备列表
ioTDMConfig.enableGateway = false;
// 默认不开启,是否开启日志推送功能
ioTDMConfig.enableLogPush = false;
params.ioTDMConfig = ioTDMConfig;
//Step6: 下行消息处理回调设置
LinkKit.getInstance().registerOnPushListener(notifyListener);
//Step7: 一型一密免预注册设置(可选)
//对于一型一密免预注册的设备, 设备连云时要用上deviceToken和clientId
MqttConfigure.deviceToken = DemoApplication.deviceToken;
MqttConfigure.clientId = DemoApplication.clientId;
//Step8: H2文件上传设置(可选)
/**
* 如果要用到HTTP2文件上传, 需要用户设置域名
*/
IoTH2Config ioTH2Config = new IoTH2Config();
ioTH2Config.clientId = "client-id";
ioTH2Config.endPoint = "https://" + productKey + ioTH2Config.endPoint;// 线上环境
params.iotH2InitParams = ioTH2Config;
/**
* 设备初始化建联
* onError 初始化建联失败,如果因网络问题导致初始化失败,需要用户重试初始化
* onInitDone 初始化成功
*/
LinkKit.getInstance().init(context, params, new ILinkKitConnectListener() {
@Override
public void onError(AError error) {
AppLog.d(TAG, "onError() called with: error = [" + getAErrorString(error) + "]");
callback.onError(error);
}
@Override
public void onInitDone(Object data) {
AppLog.d(TAG, "onInitDone() called with: data = [" + data + "]");
callback.onInitDone(data);
}
});
一型一密
一型一密又称动态注册,用于向物联网平台获取设备的密钥,具体分为免预注册和预注册两种方式。使用该功能前,需要确保:
已在物联网平台创建产品已开启动态注册开关。
demo的deviceinfo文件中的deviceSecret的值为空,productSecret不为空。
请确保已执行Demo中的step 1、step 2、step 3。
动态注册成功或失败之后,需要断开当前的动态注册长连接。如果动态注册成功,可以在断开后执行建连的流程,具体可参考Demo中
destroyRegisterConnect()
的实现。为了您的设备安全,一型一密的认证方式获取到设备密钥后,请将其持久固化至设备,若需连接至物联网平台,请参考上述一机一密流程。
免预注册和预注册区别如下:
区别 | 免预注册 | 预注册 |
通信协议/地域支持 | MQTT/华东2(上海) | MQTT/物联网平台支持的所有地域; 基于HTTPS的动态注册仅限于上海区域,不推荐使用,不在本示例代码介绍范围之内。 |
返回的设备密钥 | 设备的clientID和deviceToken,请将其持久固化至设备,以便连云等其他功能使用。具体使用方式,请参考上述一机一密示例中的Step7。 | DeviceSecret 具体使用方式,请参考上述一机一密示例中的Step1。 |
添加设备 | 不需要在物联网平台预注册设备DeviceName。 | 需要在物联网平台预注册设备DeviceName。 |
使用次数限制 | 物联网平台允许最多5个物理设备使用同一组ProductKey、ProductSecret、DeviceName进行激活,并为不同物理设备颁发不同的ClientID、DeviceToken。 |
|
示例代码如下:
MqttInitParams initParams = new MqttInitParams(productKey, productSecret, deviceName, deviceSecret, MqttConfigure.MQTT_SECURE_MODE_TLS);
//动态注册step1: 确定一型一密的类型(免预注册, 还是非免预注册)
//case 1: 如果registerType里面填写了regnwl, 表明设备的一型一密方式为免预注册(即无需创建设备)
//case 2: 如果这个字段为空, 则表示为需要预注册的一型一密(需要实现创建设备)
initParams.registerType = registerType;
//动态注册step2: 设置动态注册的注册接入点域名
MqttConfigure.mqttHost = mqttHost;
//动态注册step3: 如果用户所用的实例为新版本的公共实例或者企业实例(控制台中有实例详情的页面), 需设置动态注册的实例id
MqttConfigure.registerInstanceId = instanceId;
LinkKit.getInstance().deviceDynamicRegister(this, initParams, new IOnCallListener() {
@Override
public void onSuccess(com.aliyun.alink.linksdk.channel.core.base.ARequest request, com.aliyun.alink.linksdk.channel.core.base.AResponse response) {
AppLog.i(TAG, "onSuccess() called with: request = [" + request + "], response = [" + response + "]");
// response.data is byte array
try {
String responseData = new String((byte[]) response.data);
JSONObject jsonObject = JSONObject.parseObject(responseData);
String pk = jsonObject.getString("productKey");
String dn = jsonObject.getString("deviceName");
// 非一型一密免预注册返回
String ds = jsonObject.getString("deviceSecret");
// 一型一密免预注册返回
String ci = jsonObject.getString("clientId");
String dt = jsonObject.getString("deviceToken");
clientId = ci;
deviceToken = dt;
deviceSecret = ds;
// 持久化 clientId & deviceToken 初始化建联的时候需要
// 这里仅为测试代码,请将认证信息持久化到外部存储,确保app清除缓存或者卸载重装后仍能取到
SharedPreferences preferences = getSharedPreferences("deviceAuthInfo", 0);
if ((!TextUtils.isEmpty(clientId) && !TextUtils.isEmpty(deviceToken)) || (!TextUtils.isEmpty(deviceSecret))) {
showToast("一型一密成功");
SharedPreferences.Editor editor = preferences.edit();
editor.putString("deviceId", productKey + deviceName);
editor.putString("clientId", clientId);
editor.putString("deviceToken", deviceToken);
editor.putString("deviceSecret", deviceSecret);
editor.commit();
try {
Thread.sleep(2000);
} catch (Exception e){
}
destroyRegisterConnect(true);
} else {
showToast("一型一密失败,返回信息无效 " + responseData);
destroyRegisterConnect(false);
}
} catch (Exception e) {
showToast("一型一密失败,返回数据信息无效");
e.printStackTrace();
destroyRegisterConnect(false);
}
}
@Override
public void onFailed(com.aliyun.alink.linksdk.channel.core.base.ARequest request, com.aliyun.alink.linksdk.channel.core.base.AError error) {
AppLog.w(TAG, "onFailed() called with: request = [" + request + "], error = [" + error + "]");
showToast("一型一密失败 " + error);
destroyRegisterConnect(false);
}
@Override
public boolean needUISafety() {
return false;
}
});
/**
* 注意该接口不能在动态注册回调线程里面调用,mqtt 通道会报 Disconnecting is not allowed from a callback method (32107)
* @param needConnect
*/
private void destroyRegisterConnect(final boolean needConnect) {
new Thread(new Runnable() {
@Override
public void run() {
try {
LinkKit.getInstance().stopDeviceDynamicRegister(10 * 1000, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken iMqttToken) {
AppLog.d(TAG, "onSuccess() called with: iMqttToken = [" + iMqttToken + "]");
if (needConnect) {
connect();
}
}
@Override
public void onFailure(IMqttToken iMqttToken, Throwable throwable) {
AppLog.w(TAG, "onFailure() called with: iMqttToken = [" + iMqttToken + "], throwable = [" + throwable + "]");
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
ID²认证
/**
* 在物联网平台创建使用iTLS认证方式的设备,采用该方式初始化
* 产品需要ID²授权
*/
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;
示例代码中的参数clientConfig.channelHost
为设备的接入域名。
更多设置
您可以设置以下参数,实现设备接入相关的更多设置。
MQTT连接:
配置项
说明
相关代码
保活时间
设置设备的保活时间。通过该设置实现设备与物联网平台保持长连接。
说明当前SDK默认保活时间为65秒。
您可设置的心跳周期在30 ~ 1200秒。
// interval 单位秒 MqttConfigure.setKeepAliveInterval(int interval);
QoS等级
设置QoS(Quality of Service)等级,即物联网平台与设备之间保证交付信息的协议。仅支持:
0
:最多一次。1
:最少一次。
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;
客户端ID
通过clientId设置客户端ID。clientId的详细说明,请参见clientId说明。
MqttConfigure.clientId = "abcdef******";
重连机制
通过automaticReconnect参数的值,设置是否重连。
true
:重连。false
:不重连。
参数设置:
MqttConfigure.automaticReconnect = true;
SDK反初始化:
如果需要注销初始化,调用如下反初始化接口。该接口为同步接口。
说明SDK进行初始化时,需确保最近一次的反初始化已执行完成。否则,会导致重复初始化失败。
// 取消注册 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);
日志开关:
打开SDK内部日志输出开关:
PersistentNet.getInstance().openLog(true); ALog.setLevel(ALog.LEVEL_DEBUG);
获取SDK的版本号:
ALog.i(TAG, "sdk version = " + LinkKit.getInstance().getSDKVersion());
安卓设备如何快速重连?
引用import com.aliyun.alink.linksdk.channel.core.persistent.PersistentNet;
坐标后,可以通过PersistentNet.getInstance().reconnect();
来实现快速重连。
如果想自定义一个重连时间,可以启动一个定时器,定时器到期后调用PersistentNet.getInstance().reconnect();
,可以让断开的MQTT连接立刻重连。