高效Android MQTT封装工具:简化物联网开发,提升性能与稳定性

news2025/3/9 20:17:22

在Android开发中,封装MQTT工具可以帮助简化与MQTT服务器的通信。MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅消息传输协议,常用于物联网(IoT)设备之间的通信。

以下是一个简单的MQTT封装工具示例,使用Eclipse Paho库来实现MQTT客户端功能。

  1. 添加依赖
    首先,在build.gradle文件中添加Paho MQTT库的依赖:
dependencies {
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
}
  1. 权限配置
    在AndroidManifest.xml中添加必要的权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  1. 服务配置
    在AndroidManifest.xml中注册Paho的MqttService:
<service android:name="org.eclipse.paho.android.service.MqttService" />

4.MQTT封装工具类支持:
自定义客户端ID:支持用户自定义客户端ID。

遗言消息:支持设置遗言消息,用于客户端异常断开时通知其他客户端。

线程池管理:使用线程池处理MQTT操作,避免阻塞主线程。

灵活的回调机制:支持为每个操作单独设置回调。

连接重试机制:增加连接失败后的重试机制,提升连接稳定性。

日志级别:支持动态调整日志级别,便于调试和生产环境切换。
支持多服务器连接:允许同时连接多个MQTT服务器。

消息缓存策略:增加消息缓存的最大数量和时间限制,避免内存泄漏。

更灵活的重试机制:支持自定义重试次数和重试间隔。

连接状态管理:增加更详细的连接状态(如连接中、已连接、断开中等)。

支持QoS级别配置:允许动态配置发布和订阅的QoS级别。

MQTT工具类

import android.content.Context;
import android.util.Log;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MqttManager {

    private static final String TAG = "MqttManager";

    private static MqttManager instance;
    private Map<String, MqttAndroidClient> mqttClients = new HashMap<>(); // 支持多服务器连接
    private Map<String, MqttConnectOptions> mqttConnectOptionsMap = new HashMap<>();
    private Map<String, Boolean> connectionStatusMap = new HashMap<>(); // 连接状态
    private Map<String, List<MqttMessage>> messageQueueMap = new HashMap<>(); // 消息队列
    private Map<String, List<String>> subscribedTopicsMap = new HashMap<>(); // 已订阅的主题
    private ExecutorService executorService = Executors.newCachedThreadPool(); // 线程池

    private ConnectionStateListener connectionStateListener;

    private static final int DEFAULT_QOS = 1;
    private static final boolean DEFAULT_RETAINED = false;
    private static final int MAX_MESSAGE_QUEUE_SIZE = 100; // 消息队列最大数量
    private static final long MESSAGE_QUEUE_TIMEOUT = 60000; // 消息队列超时时间(毫秒)
    private static final int MAX_RETRY_COUNT = 3; // 最大重试次数
    private static final int RETRY_INTERVAL = 5000; // 重试间隔(毫秒)

    // 单例模式
    public static synchronized MqttManager getInstance() {
        if (instance == null) {
            instance = new MqttManager();
        }
        return instance;
    }

    private MqttManager() {}

    /**
     * 初始化MQTT客户端
     */
    public void initClient(Context context, String serverUri, String clientId) {
        if (mqttClients.containsKey(serverUri)) {
            Log.w(TAG, "Client already initialized for server: " + serverUri);
            return;
        }

        MqttAndroidClient client = new MqttAndroidClient(context, serverUri, clientId);
        mqttClients.put(serverUri, client);
        mqttConnectOptionsMap.put(serverUri, new MqttConnectOptions());
        connectionStatusMap.put(serverUri, false);
        messageQueueMap.put(serverUri, new ArrayList<>());
        subscribedTopicsMap.put(serverUri, new ArrayList<>());

        // 设置回调
        client.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                connectionStatusMap.put(serverURI, true);
                Log.d(TAG, "MQTT connected: " + serverURI);
                if (connectionStateListener != null) {
                    connectionStateListener.onConnected(serverURI, reconnect);
                }
                // 连接成功后发送缓存的消息
                processMessageQueue(serverURI);
                // 重新订阅主题
                resubscribeTopics(serverURI);
            }

            @Override
            public void connectionLost(Throwable cause) {
                for (Map.Entry<String, MqttAndroidClient> entry : mqttClients.entrySet()) {
                    if (entry.getValue().equals(client)) {
                        String serverUri = entry.getKey();
                        connectionStatusMap.put(serverUri, false);
                        Log.e(TAG, "MQTT connection lost: " + serverUri, cause);
                        if (connectionStateListener != null) {
                            connectionStateListener.onDisconnected(serverUri, cause);
                        }
                        // 尝试重新连接
                        attemptReconnect(serverUri, 0);
                        break;
                    }
                }
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                Log.d(TAG, "Message arrived: " + new String(message.getPayload()));
                if (connectionStateListener != null) {
                    connectionStateListener.onMessageReceived(topic, message);
                }
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                Log.d(TAG, "Message delivered");
            }
        });
    }

    /**
     * 设置连接参数
     */
    public void setConnectionOptions(String serverUri, String username, String password) {
        MqttConnectOptions options = mqttConnectOptionsMap.get(serverUri);
        if (options != null) {
            options.setUserName(username);
            options.setPassword(password.toCharArray());
        }
    }

    /**
     * 设置遗言消息
     */
    public void setWillMessage(String serverUri, String topic, String message, int qos, boolean retained) {
        MqttConnectOptions options = mqttConnectOptionsMap.get(serverUri);
        if (options != null) {
            options.setWill(topic, message.getBytes(), qos, retained);
        }
    }

    /**
     * 设置SSL/TLS加密连接
     */
    public void setSSL(String serverUri, boolean useSSL) {
        MqttConnectOptions options = mqttConnectOptionsMap.get(serverUri);
        if (options != null && useSSL) {
            options.setSocketFactory(new DefaultSSLSocketFactory());
        }
    }

    /**
     * 连接到MQTT服务器
     */
    public void connect(String serverUri) {
        if (connectionStatusMap.get(serverUri)) {
            Log.w(TAG, "Already connected to MQTT server: " + serverUri);
            return;
        }

        MqttAndroidClient client = mqttClients.get(serverUri);
        MqttConnectOptions options = mqttConnectOptionsMap.get(serverUri);

        if (client == null || options == null) {
            Log.e(TAG, "Client or options not initialized for server: " + serverUri);
            return;
        }

        executorService.execute(() -> {
            try {
                client.connect(options, null, new IMqttActionListener() {
                    @Override
                    public void onSuccess(IMqttToken asyncActionToken) {
                        Log.d(TAG, "Connected to MQTT server: " + serverUri);
                    }

                    @Override
                    public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                        Log.e(TAG, "Failed to connect to MQTT server: " + serverUri, exception);
                        attemptReconnect(serverUri, 0);
                    }
                });
            } catch (MqttException e) {
                Log.e(TAG, "MQTT connection error: " + serverUri, e);
                attemptReconnect(serverUri, 0);
            }
        });
    }

    /**
     * 断开连接
     */
    public void disconnect(String serverUri) {
        if (!connectionStatusMap.get(serverUri)) {
            Log.w(TAG, "Already disconnected from MQTT server: " + serverUri);
            return;
        }

        MqttAndroidClient client = mqttClients.get(serverUri);
        if (client == null) {
            Log.e(TAG, "Client not initialized for server: " + serverUri);
            return;
        }

        executorService.execute(() -> {
            try {
                client.disconnect(null, new IMqttActionListener() {
                    @Override
                    public void onSuccess(IMqttToken asyncActionToken) {
                        connectionStatusMap.put(serverUri, false);
                        Log.d(TAG, "Disconnected from MQTT server: " + serverUri);
                    }

                    @Override
                    public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                        Log.e(TAG, "Failed to disconnect from MQTT server: " + serverUri, exception);
                    }
                });
            } catch (MqttException e) {
                Log.e(TAG, "MQTT disconnection error: " + serverUri, e);
            }
        });
    }

    /**
     * 订阅主题
     */
    public void subscribe(String serverUri, String topic, int qos, IMqttActionListener listener) {
        if (!connectionStatusMap.get(serverUri)) {
            Log.w(TAG, "Not connected, subscription will be attempted after connection: " + serverUri);
            subscribedTopicsMap.get(serverUri).add(topic); // 缓存主题
            return;
        }

        MqttAndroidClient client = mqttClients.get(serverUri);
        if (client == null) {
            Log.e(TAG, "Client not initialized for server: " + serverUri);
            return;
        }

        executorService.execute(() -> {
            try {
                client.subscribe(topic, qos, null, listener);
            } catch (MqttException e) {
                Log.e(TAG, "MQTT subscription error: " + serverUri, e);
            }
        });
    }

    /**
     * 发布消息
     */
    public void publish(String serverUri, String topic, String message, int qos, boolean retained, IMqttActionListener listener) {
        MqttMessage mqttMessage = new MqttMessage(message.getBytes());
        mqttMessage.setQos(qos);
        mqttMessage.setRetained(retained);

        if (!connectionStatusMap.get(serverUri)) {
            Log.w(TAG, "Not connected, message will be queued: " + serverUri);
            List<MqttMessage> queue = messageQueueMap.get(serverUri);
            if (queue.size() < MAX_MESSAGE_QUEUE_SIZE) {
                queue.add(mqttMessage); // 缓存消息
            } else {
                Log.w(TAG, "Message queue is full, discarding message: " + serverUri);
            }
            return;
        }

        MqttAndroidClient client = mqttClients.get(serverUri);
        if (client == null) {
            Log.e(TAG, "Client not initialized for server: " + serverUri);
            return;
        }

        executorService.execute(() -> {
            try {
                client.publish(topic, mqttMessage, null, listener);
            } catch (MqttException e) {
                Log.e(TAG, "MQTT publish error: " + serverUri, e);
            }
        });
    }

    /**
     * 处理消息队列
     */
    private void processMessageQueue(String serverUri) {
        List<MqttMessage> queue = messageQueueMap.get(serverUri);
        for (MqttMessage message : queue) {
            publish(serverUri, "default/topic", new String(message.getPayload()), message.getQos(), message.isRetained(), null);
        }
        queue.clear();
    }

    /**
     * 重新订阅主题
     */
    private void resubscribeTopics(String serverUri) {
        List<String> topics = subscribedTopicsMap.get(serverUri);
        for (String topic : topics) {
            subscribe(serverUri, topic, DEFAULT_QOS, null);
        }
    }

    /**
     * 尝试重新连接
     */
    private void attemptReconnect(String serverUri, int retryCount) {
        if (retryCount >= MAX_RETRY_COUNT) {
            Log.w(TAG, "Max retry count reached for server: " + serverUri);
            return;
        }

        executorService.execute(() -> {
            try {
                Thread.sleep(RETRY_INTERVAL);
                connect(serverUri);
            } catch (InterruptedException e) {
                Log.e(TAG, "Reconnect attempt interrupted: " + serverUri, e);
            }
        });
    }

    /**
     * 设置连接状态监听器
     */
    public void setConnectionStateListener(ConnectionStateListener listener) {
        this.connectionStateListener = listener;
    }

    /**
     * 连接状态监听器接口
     */
    public interface ConnectionStateListener {
        void onConnected(String serverUri, boolean isReconnect);
        void onDisconnected(String serverUri, Throwable cause);
        void onMessageReceived(String topic, MqttMessage message);
    }
}

以下是一个使用 MqttManager 的完整示例。这个示例展示了如何初始化MQTT客户端、连接服务器、订阅主题、发布消息以及处理连接状态和消息回调。

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttMessage;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final String SERVER_URI = "tcp://your.mqtt.broker:1883"; // MQTT服务器地址
    private static final String CLIENT_ID = "android_client_" + System.currentTimeMillis(); // 客户端ID
    private static final String TOPIC = "your/topic"; // 订阅的主题
    private static final String WILL_TOPIC = "your/lwt/topic"; // 遗言主题
    private static final String WILL_MESSAGE = "Client disconnected"; // 遗言消息

    private MqttManager mqttManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化MQTT管理器
        mqttManager = MqttManager.getInstance();
        mqttManager.initClient(this, SERVER_URI, CLIENT_ID);

        // 设置连接参数
        mqttManager.setConnectionOptions(SERVER_URI, "username", "password");

        // 设置遗言消息
        mqttManager.setWillMessage(SERVER_URI, WILL_TOPIC, WILL_MESSAGE, 1, true);

        // 设置连接状态监听器
        mqttManager.setConnectionStateListener(new MqttManager.ConnectionStateListener() {
            @Override
            public void onConnected(String serverUri, boolean isReconnect) {
                Log.d(TAG, "Connected to MQTT server: " + serverUri + ", isReconnect: " + isReconnect);

                // 订阅主题
                mqttManager.subscribe(SERVER_URI, TOPIC, 1, new IMqttActionListener() {
                    @Override
                    public void onSuccess(IMqttToken asyncActionToken) {
                        Log.d(TAG, "Subscribed to topic: " + TOPIC);
                    }

                    @Override
                    public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                        Log.e(TAG, "Failed to subscribe to topic: " + TOPIC, exception);
                    }
                });
            }

            @Override
            public void onDisconnected(String serverUri, Throwable cause) {
                Log.e(TAG, "Disconnected from MQTT server: " + serverUri, cause);
            }

            @Override
            public void onMessageReceived(String topic, MqttMessage message) {
                Log.d(TAG, "Received message from topic: " + topic + ", payload: " + new String(message.getPayload()));
            }
        });

        // 连接MQTT服务器
        mqttManager.connect(SERVER_URI);

        // 发布消息
        publishMessage("Hello, MQTT!");
    }

    /**
     * 发布消息
     */
    private void publishMessage(String message) {
        mqttManager.publish(SERVER_URI, TOPIC, message, 1, false, new IMqttActionListener() {
            @Override
            public void onSuccess(IMqttToken asyncActionToken) {
                Log.d(TAG, "Message published to topic: " + TOPIC);
            }

            @Override
            public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                Log.e(TAG, "Failed to publish message to topic: " + TOPIC, exception);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 断开连接
        mqttManager.disconnect(SERVER_URI);
    }
}

示例说明
初始化MQTT客户端:

使用 MqttManager.getInstance() 获取单例实例。

调用 initClient() 初始化MQTT客户端,传入服务器地址和客户端ID。

设置连接参数:

使用 setConnectionOptions() 设置用户名和密码。

设置遗言消息:

使用 setWillMessage() 设置遗言消息,当客户端异常断开时,服务器会发布该消息。

设置连接状态监听器:

实现 ConnectionStateListener 接口,监听连接状态变化和消息到达事件。

连接MQTT服务器:

调用 connect() 连接到MQTT服务器。

订阅主题:

在 onConnected() 回调中订阅主题。

发布消息:

使用 publish() 方法发布消息到指定主题。

断开连接:

在 onDestroy() 中调用 disconnect() 断开连接。

注意事项
服务器地址和主题:

替换 SERVER_URI 和 TOPIC 为实际的MQTT服务器地址和主题。

用户名和密码:

如果MQTT服务器需要认证,请设置正确的用户名和密码。

遗言消息:

遗言消息用于在客户端异常断开时通知其他客户端,确保设置合适的主题和消息内容。

线程安全:

MQTT操作在后台线程中执行,避免阻塞主线程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2312317.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2025最新比较使用的ai工具都有哪些,分别主要用于哪些方面?

文章目录 一、AI对话与交互工具二、AI写作与内容生成工具三、AI绘画与设计工具四、AI视频生成工具五、办公与效率工具六、其他实用工具选择建议 根据2025年最新行业动态和用户反馈&#xff0c;以下AI工具在多个领域表现突出&#xff0c;覆盖对话、写作、设计、视频生成等场景&a…

在ArcMap中通过Python编写自定义工具(Python Toolbox)实现点转线工具

文章目录 一、需求二、实现过程2.1、创建Python工具箱&#xff08;.pyt&#xff09;2.2、使用catalog测试代码2.3、在ArcMap中使用工具 三、测试 一、需求 通过插件的形式将点转线功能嵌入ArcMap界面&#xff0c;如何从零开始创建一个插件&#xff0c;包括按钮的添加、工具的实…

后智能体时代的LLM和Agent

文章目录 1. 关于AI重塑的哲学体系2. 关于AI大模型体系的认知3. 关于AI大模型体系的畅想4. 关于人和AI大模型体系的共处5. 写在最后 随着OpenAI、Deepseek、Manus等等智能体的爆火&#xff0c;人们茶前饭后、插科打诨的话题都离不开这些智能体&#xff0c;现状也正如《人民日报…

景联文科技:以精准数据标注赋能AI进化,构筑智能时代数据基石

在人工智能技术席卷全球的浪潮中&#xff0c;高质量数据已成为驱动AI模型进化的核心燃料。作为全球领先的AI数据服务解决方案提供商&#xff0c;景联文科技深耕数据标注领域多年&#xff0c;以技术为基、以专业为本&#xff0c;致力于为全球客户提供全场景、高精度、多模态的数…

嵌入式L6计算机网络

Telnet不加密 socket是应用层和下面的内核

华为鸿蒙系统全景解读:从内核设计到生态落地的技术革命

华为鸿蒙系统全景解读&#xff1a;从内核设计到生态落地的技术革命 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;可以分享一下给大家。点击跳转到网站。 https://www.captainbed.cn/ccc 文章目录 华为鸿蒙系统全景解读&#x…

八卡5090服务器首发亮相!

AI 人工智能领域热度居高不下。OpenAI 的 GPT - 4 凭强悍语言处理能力&#xff0c;在内容创作、智能客服等领域广泛应用。清华大学团队的 DeepSeek 大模型在深度学习训练优势突出&#xff0c;正促使各行业应用端算力需求向推理主导转变&#xff0c;呈爆发式增长 。 随着 DeepS…

基于SSM+Vue+uniapp的驾校预约管理小程序+LW示例

系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…

《用Python+PyGame开发双人生存游戏!源码解析+完整开发思路分享》

导语​ "你是否想过用Python开发一款可玩性高的双人合作游戏&#xff1f;本文将分享如何从零开始实现一款类《吸血鬼幸存者》的生存射击游戏&#xff01;包含完整源码解析、角色系统设计、敌人AI逻辑等核心技术点&#xff0c;文末提供完整代码包下载&#xff01;" 哈…

ArcGIS操作:13 生成最小外接矩阵

应用情景&#xff1a;筛选出屋面是否能放下12*60m的长方形&#xff0c;作为起降场候选点&#xff08;一个不规则的形状内&#xff0c;判断是否能放下指定长宽的长方形&#xff09; 1、面积初步筛选 Area ≥ 720 ㎡ 面积计算见 2、打开 ArcToolbox → Data Management Tools …

manus对比ChatGPT-Deep reaserch进行研究类学术相关数据分析!谁更胜一筹?

没有账号&#xff0c;只能挑选一个案例 一夜之间被这个用全英文介绍全华班出品的新爆款国产AI产品的小胖刷频。白天还没有切换语言的选项&#xff0c;晚上就加上了。简单看了看团队够成&#xff0c;使用很长实践的Monica创始人也在其中。逐渐可以理解&#xff0c;重心放在海外产…

Python —— pow()函数

一、示例1 # 计算 2 的 3 次幂 result1 pow(2, 3) print(result1) # 输出: 8# 计算 2.5 的 2 次幂 result2 pow(2.5, 2) print(result2) # 输出: 6.25 二、示例2 # 计算 (2 ** 3) % 5 result3 pow(2, 3, 5) print(result3) # 输出: 3 三、示例3 ntxt input("请输…

开发环境搭建-完善登录功能

一.完善登录功能 我们修改密码为md5中的格式&#xff0c;那么就需要修改数据库中的密码和将从前端获取到的密码转化成md5格式&#xff0c;然后进行比对。比对成功则登录成功&#xff0c;失败则禁止登录。 二.md5格式 使用DigestUtils工具类进行md5加密&#xff0c;调用md4Dig…

STM32G431RBT6--(3)片上外设及其关系

前边我们已经了解了STM32的内核&#xff0c;下面我们来介绍片上外设&#xff0c;对于这些外设&#xff0c;如果我们弄清楚一个单片机都有什么外设&#xff0c;弄清他们之间的关系&#xff0c;对于应用单片机有很大的帮助&#xff0c;我们以G431为例&#xff1a; 这个表格描述了…

docker 安装达梦数据库(离线)

docker安装达梦数据库&#xff0c;官网上已经下载不了docker版本的了&#xff0c;下面可通过百度网盘下载 通过网盘分享的文件&#xff1a;dm8_20240715_x86_rh6_rq_single.tar.zip 链接: https://pan.baidu.com/s/1_ejcs_bRLZpICf69mPdK2w?pwdszj9 提取码: szj9 上传到服务…

AI 驱动的软件测试革命:从自动化到智能化的进阶之路

&#x1f680;引言&#xff1a;软件测试的智能化转型浪潮 在数字化转型加速的今天&#xff0c;软件产品的迭代速度与复杂度呈指数级增长。传统软件测试依赖人工编写用例、执行测试的模式&#xff0c;已难以应对快速交付与高质量要求的双重挑战。人工智能技术的突破为测试领域注…

六轴传感器ICM-20608

ICM-20608-G是一个6轴传感器芯片&#xff0c;由3轴陀螺仪和3轴加速度计组成。陀螺仪可编程的满量程有&#xff1a;250&#xff0c;500&#xff0c;1000和2000度/秒。加速度计可编程的满量程有&#xff1a;2g&#xff0c;4g&#xff0c;8g和16g。学习Linux之SPI之前&#xff0c;…

TikTok Shop欧洲市场爆发,欧洲TikTok 运营网络专线成运营关键

TikTok在欧洲的影响力还在持续攀升&#xff0c;日前&#xff0c;TikTok发布了最新的欧盟执行和使用数据报告&#xff0c;报告中提到&#xff1a; 2024年7~12月期间&#xff0c;TikTok在欧盟地区的月活用户达1.591亿&#xff0c;较上一报告期&#xff08;2024年10月发布&#xf…

专业工具,提供多种磁盘分区方案

随着时间的推移&#xff0c;电脑的磁盘空间往往会越来越紧张&#xff0c;许多人都经历过磁盘空间不足的困扰。虽然通过清理垃圾文件可以获得一定的改善&#xff0c;但随着文件和软件的增多&#xff0c;磁盘空间仍然可能显得捉襟见肘。在这种情况下&#xff0c;将其他磁盘的闲置…

你会测量管道液体流阻吗?西-魏斯巴赫方程(Darcy-Weisbach Equation)、Colebrook-White 方程帮你

测量管道液体流阻需要测量以下关键量&#xff1a; 需要测量的量 压力差&#xff08;ΔP&#xff09;&#xff1a;管道入口和出口之间的压力差&#xff0c;通常通过压力传感器或差压计测量。流量&#xff08;Q&#xff09;&#xff1a;流经管道的液体体积流量&#xff0c;可通…