
# Android Demo使用说明
#### 概述
本文以Android语言为例，介绍通过MQTTS/MQTT协议接入平台，基于[平台接口](https://support.huaweicloud.com/api-iothub/iot_06_v5_3002.html)实现"属性上报"、"订阅接收命令"等功能。
![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
本文中使用的代码为样例代码，仅用于体验平台通信功能，如需进行商用，可以参考[资源获取](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html)获取对应语言的IoT Device SDK进行集成。
#### 前提条件
- 已安装Android，若未安装请参考[安装android studio](https://developer.android.google.cn/studio/#downloads)，还需要[安装JDK]。
- 已在[管理控制台](https://console.huaweicloud.com/iotdm/#/dm-portal/home)获取设备接入地址。获取地址的操作步骤，请参考[平台对接信息](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#ZH-CN_TOPIC_0187644975__section436212919379)。
- 已在[管理控制台](https://console.huaweicloud.com/iotdm/#/dm-portal/home)创建产品和设备。创建产品和设备的具体操作细节，请参考[创建产品](https://support.huaweicloud.com/usermanual-iothub/iot_01_0054.html)、[注册单个设备](https://support.huaweicloud.com/usermanual-iothub/iot_01_0031.html)或[批量注册设备](https://support.huaweicloud.com/usermanual-iothub/iot_01_0032.html)。
 
#### 准备工作
- 安装android studio 访问[android studio官网](https://developer.android.google.cn/studio/#downloads)，选择合适系统的版本下载并安装。（本文以windows 64-bit系统Android Studio 3.5为例）。
  ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0242161203.png "点击放大")
  
- 安装JDK（也可以使用IDE自带的JDK）
  1. 访问[Oracle官网](https://www.oracle.com/java/technologies/javase-downloads.html)，选择合适的JDK版本单击"Download"下载（本文以Windows x64 JDK8为例）。
  
  2. 下载完成后，运行安装文件，根据界面提示安装。
   
 
#### 导入代码样例
1. 下载[quickStart(Android)](https://developer.obs.cn-north-4.myhuaweicloud.com:443/manage/south/quickStart/mqttdemo(android).zip)样例。
2. 运行Android Studio，单击Open，选择步骤[1]中下载的样例。
   
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0242176708.png "点击放大")
   
   
3. 完成代码导入。
   
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0242176738.png "点击放大")
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0242176843.png "点击放大")
   **代码目录简述：**
   - **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客户端组件；
     
   
   
   
   
4. （可选）了解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')
     ```
     
   
   
   
   
 
#### 界面展示
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0242199278.png "点击放大")
1. MainActivity类主要提供了界面显示，请填写设备ID和设备密钥，在物联网平台或调用接口注册设备后获取。
2. 示例中默认写了设备侧接入的域名地址（SSL加密接入时该域名要与对应的[证书文件匹配使用](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#ZH-CN_TOPIC_0187644975__section197481637133318)）。
   ```
   private final static String IOT_PLATFORM_URL = "iot-mqtts.cn-north-4.myhuaweicloud.com";
   ```
   
3. 用户可以选择设备侧建链时是否SSL加密/不加密，选择Qos方式是0还是1，当前不支持Qos2，可参考[使用限制](https://support.huaweicloud.com/devg-iothub/iot_02_2200.html#ZH-CN_TOPIC_0246021145__section1565109112518)。
   ```
   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不加密");
           }    
       }
   })
   ```
   
 
#### 建立连接
设备或网关在接入物联网平台时首先需要和平台建立连接，从而将设备或网关与平台进行关联。开发者通过传入设备信息，将设备或网关连接到物联网平台。
1. 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";
   }
   ```
   
2. ConnectUtils类主要提供了SSL加载证书的getMqttsCertificate方法，如果是MQTTS建链方式，需要调用该方法加载证书。
   DigiCertGlobalRootCA.bks：设备校验平台身份的证书，用于设备侧接入物联网平台登录鉴权使用，可以在资源获取中[下载证书文件](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#ZH-CN_TOPIC_0187644975__section197481637133318)。
   ```
   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();
   ```
   
3. MainActivity类提供了设置初始化MqttConnectOptions的方法。mqtt连接心跳时间的建议值是120秒，有[使用限制](https://support.huaweicloud.com/devg-iothub/iot_02_2200.html#ZH-CN_TOPIC_0246021145__section1565109112518)。
   ```
   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;
   }
   ```
   
4. 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定义](https://support.huaweicloud.com/api-iothub/iot_06_v5_3004.html)。
在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界面日志栏显示如下信息：
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0242208181.png "点击放大")
#### 属性上报
属性上报是指设备主动向平台上报自己的属性。更多接口信息请参考[设备属性上报](https://support.huaweicloud.com/api-iothub/iot_06_v5_3010.html)。
在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);
```
设备上报属性成功后可在[设备详情](https://console.huaweicloud.com/iotdm/#/dm-portal/device/all-device)页面查看到上报的属性
图1查看上报数据-PeriodicalReportConfig   
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001981624453.png "点击放大")
图2查看上报数据-Battery_level   
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001950422302.png "点击放大")
![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
如果在"设备详情"页面没有最新上报数据，请确认设备上报的服务/属性和产品模型中的服务/属性一致。
#### 接收下发命令
在MainActivity类中提供了接收平台下发命令的功能，在MQTT建链完成后，可以在[管理控制台](https://console.huaweicloud.com/iotdm/#/dm-portal/home)设备详情中命令下发或[使用应用侧Demo](https://support.huaweicloud.com/devg-iothub/iot_02_9994.html)对该设备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界面显示如下：
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0242209313.png "点击放大")
