Android Demo使用说明
概述
本文以Android语言为例,介绍通过MQTTS/MQTT协议接入平台,基于平台接口实现“属性上报”、“订阅接收命令”等功能。
本文中使用的代码为样例代码,仅用于体验平台通信功能,如需进行商用,可以参考资源获取获取对应语言的IoT Device SDK进行集成。
前提条件
准备工作
- 安装android studio
访问android studio官网,选择合适系统的版本下载并安装。(本文以windows 64-bit系统Android Studio 3.5为例)。
- 安装JDK(也可以使用IDE自带的JDK)
- 访问Oracle官网,选择合适的JDK版本单击“Download”下载(本文以Windows x64 JDK8为例)。
- 下载完成后,运行安装文件,根据界面提示安装。
导入代码样例
- 下载quickStart(Android)样例。
- 运行Android Studio,单击Open,选择步骤1中下载的样例。
- 完成代码导入。
代码目录简述:
- manifests:Android项目的配置文件;
- java:项目java代码;
MainActivity:demo界面类;
ConnectUtils:mqtt连接辅助类;
- asset:项目原生文件;
DigiCertGlobalRootCA.bks:设备校验平台身份的证书,用于设备侧接入物联网平台登录鉴权使用;
- res:项目资源文件(图片、布局、字符串等);
- gradle:项目全局的gradle构建脚本.
- libs:项目中使用到了第三方jar包归档目录
org.eclipse.paho.android.service-1.1.0.jar:Android启动后台service组件实现消息发布和订阅的组件;
org.eclipse.paho.client.mqttv3-1.2.0.jar:mqtt java客户端组件;
- (可选)了解Demo里的关键工程配置(默认不用修改)。
- AndroidManifest.xml:需要添加,支持mqtt service。
<service android:name="org.eclipse.paho.android.service.MqttService" />
- build.gradle:添加依赖,导入libs下的两个mqtt连接所需要的jar包。(也可以添加jar包官网引用)
implementation files('libs/org.eclipse.paho.android.service-1.1.0.jar') implementation files('libs/org.eclipse.paho.client.mqttv3-1.2.0.jar')
- AndroidManifest.xml:需要添加,支持mqtt service。
界面展示
- MainActivity类主要提供了界面显示,请填写设备ID和设备密钥,在物联网平台或调用接口注册设备后获取。
- 示例中默认写了设备侧接入的域名地址(SSL加密接入时该域名要与对应的证书文件匹配使用)。
private final static String IOT_PLATFORM_URL = "iot-mqtts.cn-north-4.myhuaweicloud.com";
- 用户可以选择设备侧建链时是否SSL加密/不加密,选择Qos方式是0还是1,当前不支持Qos2,可参考使用限制。
checkbox_mqtt_connet_ssl.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { isSSL = true; checkbox_mqtt_connet_ssl.setText("SSL加密"); } else { isSSL = false; checkbox_mqtt_connet_ssl.setText("SSL不加密"); } } })
建立连接
设备或网关在接入物联网平台时首先需要和平台建立连接,从而将设备或网关与平台进行关联。开发者通过传入设备信息,将设备或网关连接到物联网平台。
- MainActivity类主要提供建立MQTT/MQTTS连接等方法,MQTT默认使用1883端口,MQTTS默认使用8883端口(需要加载证书)。
if (isSSL) { editText_mqtt_log.append("开始建立MQTTS连接" + "\n"); serverUrl = "ssl://" + IOT_PLATFORM_URL + ":8883"; } else { editText_mqtt_log.append("开始建立MQTT连接" + "\n"); serverUrl = "tcp://" + IOT_PLATFORM_URL + ":1883"; }
- ConnectUtils类主要提供了SSL加载证书的getMqttsCerificate方法,如果是MQTTS建链方式,需要调用该方法加载证书。
DigiCertGlobalRootCA.bks:设备校验平台身份的证书,用于设备侧接入物联网平台登录鉴权使用,可以在资源获取中下载证书文件。
SSLContext sslContext = SSLContext.getInstance("SSL"); KeyStore keyStore = KeyStore.getInstance("bks"); keyStore.load(context.getAssets().open("DigiCertGlobalRootCA.bks"), null);//加载libs目录下的证书 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509"); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); sslContext.init(null, trustManagers, new SecureRandom()); sslSocketFactory = sslContext.getSocketFactory();
- MainActivity类提供了设置初始化MqttConnectOptions的方法。mqtt连接心跳时间的建议值是120秒,有使用限制。
mqttAndroidClient = new MqttAndroidClient(mContext, serverUrl, clientId); private MqttConnectOptions intitMqttConnectOptions(String currentDate) { String password = ConnectUtils.sha256_HMAC(editText_mqtt_device_connect_password.getText().toString(), currentDate); MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); mqttConnectOptions.setAutomaticReconnect(true); mqttConnectOptions.setCleanSession(true); mqttConnectOptions.setKeepAliveInterval(120); mqttConnectOptions.setConnectionTimeout(30); mqttConnectOptions.setUserName(editText_mqtt_device_connect_deviceId.getText().toString()); mqttConnectOptions.setPassword(password.toCharArray()); return mqttConnectOptions; }
- MainActivity类提供了Mqtt客户端建立连接的的方法connect,并通过回调函数处理连接后的消息返回结果。
mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener()
mqttAndroidClient.setCallback(new MqttCallBack4IoTHub());
注:如果连接失败,在initMqttConnects函数中的onFailure回调函数中已实现退避重连,代码样例如下:
@Override public void onFailure(IMqttToken asyncActionToken, Throwable exception) { exception.printStackTrace(); Log.e(TAG, "Fail to connect to: " + exception.getMessage()); editText_mqtt_log.append("建立连接失败:" + exception.getMessage() + "\n"); //退避重连 int lowBound = (int) (defaultBackoff * 0.8); int highBound = (int) (defaultBackoff * 1.2); long randomBackOff = random.nextInt(highBound - lowBound); long backOffWithJitter = (int) (Math.pow(2.0, (double) retryTimes)) * (randomBackOff + lowBound); long waitTImeUntilNextRetry = (int) (minBackoff + backOffWithJitter) > maxBackoff ? maxBackoff : (minBackoff + backOffWithJitter); try { Thread.sleep(waitTImeUntilNextRetry); } catch (InterruptedException e) { System.out.println("sleep failed, the reason is" + e.getMessage().toString()); } retryTimes++; MainActivity.this.initMqttConnects(); }
订阅Topic
订阅某Topic的设备才能接收broker发布的关于该Topic的消息,关于平台预置Topic可参考Topic定义。
在MainActivity类中提供了订阅命令下发Topic、订阅Topic、取消订阅Topic等功能:
String mqtt_sub_topic_command_json = String.format("$oc/devices/%s/sys/commands/#", editText_mqtt_device_connect_deviceId.getText().toString());
mqttAndroidClient.subscribe(getSubscriptionTopic(), qos, null, new IMqttActionListener()
mqttAndroidClient.unsubscribe(getSubscriptionTopic(), null, new IMqttActionListener()
如果建链成功,可以在回调函数中订阅Topic:
mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener() { @Overridepublic void onSuccess(IMqttToken asyncActionToken) { ...... subscribeToTopic(); }
建链成功后,APP界面日志栏显示如下信息:
属性上报
属性上报是指设备主动向平台上报自己的属性。更多接口信息请参考设备属性上报。
在MainActivity类中实现了属性上报Topic、属性上报功能。
String mqtt_report_topic_json = String.format("$oc/devices/%s/sys/properties/report", editText_mqtt_device_connect_deviceId.getText().toString());
MqttMessage mqttMessage = new MqttMessage(); mqttMessage.setPayload(publishMessage.getBytes()); mqttAndroidClient.publish(publishTopic, mqttMessage);
设备上报属性成功后可在设备详情页面查看到上报的属性
如果在“设备详情”页面没有最新上报数据,请确认设备上报的服务/属性和产品模型中的服务/属性一致。
接收下发命令
在MainActivity类中提供了接收平台下发命令的功能,在MQTT建链完成后,可以在管理控制台设备详情中命令下发或使用应用侧Demo对该设备ID进行命令下发,例如下发参数名为command,参数值为5的命令,下发成功后,在MQTT的回调函数中接收到。
private final class MqttCallBack4IoTHub implements MqttCallbackExtended { ...... @Overridepublic void messageArrived(String topic, MqttMessage message) throws Exception { Log.i(TAG, "Incoming message: " + new String(message.getPayload(), StandardCharsets.UTF_8)); editText_mqtt_log.append("MQTT接收下发命令成功:" + message + "\n"); }
在设备详情页面可以查看到命令下发状态,这里显示timeout是因为该Demo示例中仅演示接收命令,没有回复响应给平台。
属性上报和命令接收成功,APP界面显示如下: