设备接入物联网平台之前,需通过身份认证。本文介绍如何进行Android Link SDK初始化,实现将设备接入物联网平台。

背景信息

Android Link SDK支持以下方式,进行设备身份认证:

  • 设备密钥:

    设备密钥分为以下几种认证方式。

    认证方式 说明 相关文档
    一机一密 每台设备烧录自己的设备证书(ProductKey、DeviceName和DeviceSecret)。 一机一密
    一型一密预注册
    • 同一产品下设备烧录相同产品证书(ProductKey和ProductSecret)。
    • 产品需开启动态注册功能。
    • 设备通过动态注册获取DeviceSecret。
    一型一密
    说明

    一型一密预注册和免预注册的区别,请参见预注册和免预注册的区别

    一型一密免预注册
    • 同一产品下设备烧录相同产品证书(ProductKey和ProductSecret)。
    • 产品需开启动态注册功能。
    • 设备通过动态注册获取ClientID与DeviceToken的组合。
  • ID²认证:ID²是一种物联网设备的可信身份标识,具备不可篡改、不可伪造、全球唯一等安全属性。

    更多信息,请参见ID²认证

一机一密

一机一密的设备认证方式的示例代码如下:

/**
 * 设置设备认证信息
 */
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = productKey;// 产品类型
deviceInfo.deviceName = deviceName;// 设备名称
deviceInfo.deviceSecret = deviceSecret;// 设备密钥
//如果使用deviceToken和clientID连接物联网平台,那么设备的deviceSecret需要设置为null

//一型一密免白名单动态注册之后建联需要设置deviceToken、clientId这两个值,这两个值由deviceDynamicRegister接口返回
MqttConfigure.deviceToken = deviceToken;
MqttConfigure.clientId = clientId;

/**
 * 设置设备当前的初始状态值,属性需要和物联网平台创建的物模型属性一致
 * 若此处为空,物模型属性的初始值则为空。
 * 调用物模型上报接口之后,物模型会有相关数据缓存。
 */
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 作为预留参数
    }
});
     

示例代码说明:

  • 您需实现Android Link SDK的重连逻辑。如果设备初始化失败,需再次对SDK进行初始化。当设备由于网络等原因导致与物联网平台断开连接时,SDK会自动尝试与物联网平台建立连接。
  • 如果使用Android Link SDK Demo,您可以修改以下文件中的参数,实现设备的快速接入
    需配置的文件 参数 示例 说明
    ./app/src/main/res/raw/deviceinfo productKey a18wP****** 设备认证信息。即完成添加设备后,您保存至本地的设备证书。

    您也可以在物联网平台的设备详情页查看设备的认证信息。更多信息,请参见获取设备认证信息

    deviceName LightSwitch
    deviceSecret uwMTmVAMnGGHaAkqmeDY6cHxxB******
    app/src/main/java/com/aliyun/alink/devicesdk/manager/InitManager.java MqttConfigure.mqttHost a18wP******.iot-as-mqtt.cn-shanghai.aliyuncs.com 设备的接入域名。
    您需在LinkKit.getInstance().init前,添加以下代码:
    MqttConfigure.mqttHost = "YourHostName";

    其中,YourHostName需修改为您实例的接入域名。

    • 企业版实例和新版公共实例:在实例详情页面的开发配置面板,查看接入域名。
    • 旧版公共实例:接入域名格式为${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com

    新旧版公共实例和企业版实例、以及接入域名的更多信息,请参见实例管理

一型一密预注册

一型一密预注册的示例代码如下:

//  #######  一型一密动态注册接口开始 ######
/**
 * 注意:动态注册成功,设备上线之后,不能再次执行动态注册,物联网平台会返回已注册错误信息。
 *   在编程时首先需要判断设备是否已获取过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 + "]");
    }
});
//  ####### 一型一密动态注册接口结束  ######
         

示例代码说明:

  • 请确保已在物联网平台创建产品已开启动态注册,并为产品添加设备。
  • 请确保deviceSecret 的值为空。
  • 请确保已在./DemoApplication.java文件中设置以下参数:
        MqttConfigure.registerInstanceId="iot-06******";
        MqttConfigure.mqttHost ="iot-06******.mqtt.iothub.aliyuncs.com";

    其中,iot-06******为实例ID,更多信息,请参见查看实例终端节点

  • 动态注册成功或失败之后,需要断开当前的动态注册长连接。如果动态注册成功,可以在断开后执行建连的流程,具体可参考Demo实现。
  • Android Link SDK仅支持地域为华东2(上海)的实例使用一型一密的认证方式。
  • 为了您的设备安全,一型一密的认证方式获取DeviceSecret后,请将其持久固化至设备,以便后续其他功能使用,例如OTA升级。

一型一密免预注册

一型一密免预注册的示例代码如下:

MqttInitParams initParams = new MqttInitParams(productKey, productSecret, deviceName, deviceSecret, MqttConfigure.MQTT_SECURE_MODE_TLS);
initParams.registerType = "regnwl"; // 一型一密免预注册
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 ci = jsonObject.getString("clientId");
            String dt = jsonObject.getString("deviceToken");

            if ((!TextUtils.isEmpty(clientId) && !TextUtils.isEmpty(deviceToken)) || (!TextUtils.isEmpty(deviceSecret))) {
                // TODO by user 这里仅为测试代码,请将认证信息持久化到外部存储,确保应用程序清除缓存或者卸载重装后仍能取到
                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;
    }
});         

// 断开当前动态注册连接,并重新建连
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 + "]");
                        }

                        @Override
                        public void onFailure(IMqttToken iMqttToken, Throwable throwable) {
                            AppLog.w(TAG, "onFailure() called with: iMqttToken = [" + iMqttToken + "], throwable = [" + throwable + "]");
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
  • 请确保已在物联网平台创建产品已开启动态注册,并为产品添加设备。
  • 请确保deviceSecret 的值为空。
  • 请确保已在./DemoApplication.java文件中设置以下参数:
        MqttConfigure.registerInstanceId="iot-06******";
        MqttConfigure.mqttHost ="iot-06******.mqtt.iothub.aliyuncs.com";

    其中,iot-06******为实例ID,更多信息,请参见查看实例终端节点

  • 动态注册成功或失败之后,需要断开当前的动态注册长连接。如果动态注册成功,可以在断开后执行建连的流程,具体可参考Demo实现。
  • Android Link SDK仅支持地域为华东2(上海)的实例使用一型一密的认证方式。
  • 为了您的设备安全,一型一密的认证方式获取ClientIDDeviceToken后,请将其持久固化至设备,以便后续其他功能使用,例如OTA升级。

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连接:
    配置项 说明 相关代码
    保活时间 设置设备的保活时间。通过该设置实现设备与物联网平台保持长连接。
    // 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;
    • 实现逻辑:
      if(MqttNet.getInstance().getClient() instanceof MqttAsyncClient) {
          ((MqttAsyncClient) MqttNet.getInstance().getClient()).reconnect();
      }
  • 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());

后续步骤

设备接入物联网平台后,您可以开启以下功能,实现更多高级能力。以下功能默认为关闭,您可以根据使用场景设置所需功能。

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;
            
  • 针对有休眠场景仍需要保持设备心跳,详细内容,请参见AlarmPingSender

    MqttConfigure.pingSender = new UserDefinePingSender();
  • 物模型功能的详细说明,请参见物模型
  • 网关与子设备功能的详细说明,请参见子设备接入