Android 物联网开发:设备通过 MQTT 协议接入阿里云 IoT

在全球智能手机市场里,谷歌开发的Android移动操作系统市场占有率已经高达90%。随着物联网智能硬件升级,也逐渐成为智能摄像头,智能对讲门禁,人脸识别闸机,智能电视,智能广告屏等 IoT 设备的首选操作系统。

《物联网开发实战》本期为大家详细讲解 Android 设备通过 MQTT 协议接入阿里云 IoT 物联网平台的完整过程。

目录:

  • 阿里云 IoT 物联网云端开发

  • 使用Android Studio开发设备端程序

  • 联机运行,日志详解

   IoT 物联网平台   

1.免费开通阿里云 IoT物联网云服务:

https://www.aliyun.com/product/iot-deviceconnect

2.创建产品,选择自定义品类,直连设备:

3.添加产品功能定义,我们以采集Android设备的硬件厂商、型号、Android版本、SDK版本信息为例:

物模型属性通信Topic和Payload如下:

// 属性上报 Topic:
/sys/{productKey}/{deviceName}/thing/event/property/post

// 属性 Payload:
{
    "id":123452452,
    "version":"1.0",
    "params":{
        "brand":"HUAWEI",
        "model":"ELE-AL00",
        "android":"10",
        "sdk":29
    },
    "method":"thing.event.property.post"
}

参考文档: 

https://help.aliyun.com/document_detail/89301.html

4.注册设备,此时设备为未激活状态。

点击DeviceSecret的查看,获取身份三元组。

   Android 设备开发   

5.Android应用程序依赖

Gradle 配置:

repositories {
        maven {
            url "https://repo.eclipse.org/content/repositories/paho-releases/"
        }
}

dependencies {
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.2'
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'

}

MANIFEST 配置:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application>
  <!-- Mqtt Service -->
    <service android:name="org.eclipse.paho.android.service.MqttService" />
</application>

6.设备三元组生成MQTT 的CONNECT参数转换工具

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
import java.util.Map;

/**
 * AliyunIoTSignUtil
 */

public class AliyunIoTSignUtil {
    public static String sign(Map<String, String> params, String deviceSecret, String signMethod) {
        //将参数Key按字典顺序排序
        String[] sortedKeys = params.keySet().toArray(new String[] {});
        Arrays.sort(sortedKeys);

        //生成规范化请求字符串
        StringBuilder canonicalizedQueryString = new StringBuilder();
        for (String key : sortedKeys) {
            if ("sign".equalsIgnoreCase(key)) {
                continue;
            }
            canonicalizedQueryString.append(key).append(params.get(key));
        }

        try {
            String key = deviceSecret;
            return encryptHMAC(signMethod,canonicalizedQueryString.toString(), key);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * HMACSHA1加密
     *
     */
    public static String encryptHMAC(String signMethod,String content, String key) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key.getBytes("utf-8"), signMethod);
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        byte[] data = mac.doFinal(content.getBytes("utf-8"));
        return bytesToHexString(data);
    }

    public static final String bytesToHexString(byte[] bArray) {

        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

}

7.设备发起建立 MQTT 连接

public void connectMqtt(String url, String clientId, String mqttUsername, String mqttPassword) throws Exception {

        mqttAndroidClient = new MqttAndroidClient(getApplicationContext(), url, clientId);
        MqttConnectOptions connOpts = new MqttConnectOptions();
        // MQTT 3.1.1
        connOpts.setMqttVersion(4);
        connOpts.setAutomaticReconnect(true);
        connOpts.setCleanSession(true);

        connOpts.setUserName(mqttUsername);
        connOpts.setPassword(mqttPassword.toCharArray());
        connOpts.setKeepAliveInterval(60);

        mqttAndroidClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                postTopic();
            }

            @Override
            public void connectionLost(Throwable cause) {
                Log.d(TAG, "connectionLost " + cause.getMessage());
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                Log.d(TAG, "messageArrived topic=" + topic+", message=" + new String(message.getPayload()));
                //此处为收到的云端数据
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                Log.d(TAG, "deliveryComplete token=" + token.toString());
            }
        });

        mqttAndroidClient.connect(connOpts,null,
                new IMqttActionListener(){
                    @Override
                    public void onSuccess(IMqttToken asyncActionToken) {
                        Log.d(TAG, "connect 回调 连接成功");
                    }

                    @Override
                    public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                        Log.d(TAG, "connect 回调 onFailure,原因 exception=" + exception.toString());
                    }
                });

    }

8.设备上报物模型属性信息

public void postTopic(){ 
    JSONObject params = new JSONObject();
    params.put("brand",Build.BRAND);
    params.put("model",Build.MODEL);
    params.put("android",Build.VERSION.RELEASE);
    params.put("sdk",Build.VERSION.SDK_INT);

    JSONObject payload = new JSONObject();
    payload.put("id",System.currentTimeMillis());
    payload.put("version","1.0");
    payload.put("params",params);
    payload.put("method","thing.event.property.post");

    MqttMessage message = new MqttMessage();
    message.setPayload(payload.toString().getBytes("utf-8"));
    message.setQos(0);

    mqttAndroidClient.publish(postTopic, message);
}

   联机运行   

9.编译并部署程序到手机

10.查看设备详情,状态变为在线,物模型数据可以查看到Android设备的信息

11.日志服务查看属性上报完整日志:

通过观察日志,我们了解到物模型属性上报包含三个步骤:

  1. 设备publish数据到IoT平台

  2. IoT物联网平台按照预先定义的物模型校验并存储数据

  3. IoT物联网平台给设备响应物模型属性reply消息

完整源码获取方式:

### 如何在单链表中对信息进行分类 单链表作为一种常见的数据结构,其基本特性是非连续、非顺序的存储方式,其中每个节点由两部分组成:元素(即实际存储的数据)和指向下一个节点的指针[^1]。为了实现在单链表中对信息进行分类的操作,可以采用以下几种常见的方式: #### 方法一:基于特定条件重新构建多个子链表 可以通过遍历整个单链表并根据某些预定义的条件将节点分配到不同的子链表中。例如,假设需要按照数值大小分为两类,则可以在一次遍历过程中创建两个新的链表分别保存满足条件和不满足条件的节点。 ```java public class Node { int data; Node next; public Node(int d) { this.data = d; } } // 假设按data值是否大于某个阈值threshold来进行分类 public void classifyByValue(Node head, int threshold) { Node lessHead = null, lessTail = null; // 小于等于threshold的部分 Node greaterHead = null, greaterTail = null; // 大于threshold的部分 while (head != null) { if (head.data <= threshold) { if (lessHead == null) { lessHead = lessTail = new Node(head.data); } else { lessTail.next = new Node(head.data); lessTail = lessTail.next; } } else { if (greaterHead == null) { greaterHead = greaterTail = new Node(head.data); } else { greaterTail.next = new Node(head.data); greaterTail = greaterTail.next; } } head = head.next; } // 可选操作:打印或进一步处理这些链表... } ``` 此方法的时间复杂度主要取决于原始列表长度 \(n\) 和具体实现细节,通常为 \(O(n)\)[^2]。 #### 方法二:原地修改法——调整现有链表内部链接关系完成分类 这种方法不需要额外空间去建立新链表而是直接改变当前链表里各节点之间的相对位置达到目的。比如对于整数类型的节点来说可能希望正负分开排列;或者字符串类型则依据字母顺序重组等等。 下面给出一个简单的例子展示如何利用双指针技术来交换相邻两项直到所有符合条件项都移到前面为止(类似于冒泡排序的思想): ```java public boolean shouldMoveToFront(Node node){ return true;// Define your own logic here. } public void moveElementsToBeginning(Node head){ if(null==head || null == head.next)return ; Node prev=null,current=head,nextNode=null; do{ current=current.next; if(current!=null &&shouldMoveToFront(current)){ if(prev!=null){prev.next=current;} nextNode=current.next; current.next=head; head=current; current=nextNode; }else{ prev=current; } }while(current!=null); System.out.println("After moving elements to beginning:"); printList(head); } private static void printList(Node n){ while(n!=null){ System.out.print(n.data+" "); n=n.next; } System.out.println(); } ``` 上述代码片段展示了如何通过不断向前移动满足 `shouldMoveToFront` 函数判定标准的节点至头部从而形成一种形式上的“分类”。当然实际情况下的判断准则会更加多样化也更复杂一些[^3]. #### 注意事项 当涉及到删除某类特殊项目时务必小心边界状况以及断开后的重连过程以免丢失重要数据或是造成内存泄漏等问题生。另外,在执行任何大规模变动前最好先备份初始状态以便必要时候回滚恢复[^4]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值