跨设备消息发送:基于HarmonyOS分布式能力的多设备协同实现

一、项目概述

本文基于HarmonyOS分布式能力,实现手机和平板间的简单文本消息同步功能。项目借鉴《鸿蒙跨端U同步》中游戏多设备玩家状态同步的设计思想,构建了一个轻量级的跨设备消息发送系统,支持实时文本消息同步、设备状态感知和消息历史记录等功能。

二、架构设计

+---------------------+       +---------------------+
|     手机设备        |<----->|     平板设备        |
+----------+----------+       +----------+----------+
           |                             |
+----------v----------+       +----------v----------+
|  消息发送UI组件     |       |  消息接收UI组件     |
+----------+----------+       +----------+----------+
           |                             |
+----------v-----------------------------v----------+
|                消息同步服务                         |
|            (MessageSyncService)                   |
+----------+----------------------------------+-----+
           |                                  |
+----------v----------+            +---------v-----------+
|  分布式数据管理     |            |  设备连接管理       |
| (DistributedData)  |            | (DeviceManager)     |
+---------------------+            +---------------------+

三、核心代码实现

1. 消息同步服务实现

public class MessageSyncService {
    private static final String TAG = "MessageSyncService";
    private static final String MESSAGE_CHANNEL = "cross_device_messages";
    private static final String DEVICE_INFO_KEY = "connected_devices";
    
    private static MessageSyncService instance;
    private DistributedDataManager dataManager;
    private List<Message> messageHistory = new ArrayList<>();
    private List<DeviceInfo> connectedDevices = new ArrayList<>();
    private MessageListener messageListener;
    private DeviceStateListener deviceListener;
    
    private MessageSyncService(Context context) {
        this.dataManager = DistributedDataManagerFactory.getInstance()
            .createDistributedDataManager(context);
        initDeviceListener();
    }
    
    public static synchronized MessageSyncService getInstance(Context context) {
        if (instance == null) {
            instance = new MessageSyncService(context);
        }
        return instance;
    }
    
    private void initDeviceListener() {
        DeviceManager deviceManager = DeviceManager.getInstance();
        deviceListener = new DeviceStateListener() {
            @Override
            public void onDeviceConnected(DeviceInfo device) {
                connectedDevices.add(device);
                syncDeviceList();
                if (messageListener != null) {
                    messageListener.onDeviceConnected(device);
                }
            }
            
            @Override
            public void onDeviceDisconnected(DeviceInfo device) {
                connectedDevices.remove(device);
                syncDeviceList();
                if (messageListener != null) {
                    messageListener.onDeviceDisconnected(device);
                }
            }
        };
        deviceManager.registerDeviceListener(deviceListener);
    }
    
    // 同步设备列表
    private void syncDeviceList() {
        JSONArray deviceArray = new JSONArray();
        for (DeviceInfo device : connectedDevices) {
            JSONObject deviceJson = new JSONObject();
            try {
                deviceJson.put("deviceId", device.getDeviceId());
                deviceJson.put("deviceName", device.getDeviceName());
                deviceJson.put("deviceType", device.getDeviceType());
                deviceArray.put(deviceJson);
            } catch (JSONException e) {
                HiLog.error(TAG, "Failed to serialize device info");
            }
        }
        dataManager.putString(DEVICE_INFO_KEY, deviceArray.toString());
    }
    
    // 发送消息
    public void sendMessage(String content) {
        Message message = new Message(
            content,
            System.currentTimeMillis(),
            DistributedDeviceInfo.getLocalDeviceId(),
            DistributedDeviceInfo.getLocalDeviceName()
        );
        
        messageHistory.add(message);
        broadcastMessage(message);
    }
    
    // 广播消息到所有设备
    private void broadcastMessage(Message message) {
        JSONObject messageJson = new JSONObject();
        try {
            messageJson.put("content", message.getContent());
            messageJson.put("timestamp", message.getTimestamp());
            messageJson.put("senderId", message.getSenderId());
            messageJson.put("senderName", message.getSenderName());
            messageJson.put("version", getNextVersion());
        } catch (JSONException e) {
            HiLog.error(TAG, "Failed to serialize message");
            return;
        }
        
        dataManager.putString(MESSAGE_CHANNEL, messageJson.toString());
    }
    
    // 注册消息监听器
    public void registerMessageListener(MessageListener listener) {
        this.messageListener = listener;
        
        // 监听消息变化
        dataManager.registerDataChangeListener(MESSAGE_CHANNEL, new DataChangeListener() {
            @Override
            public void onDataChanged(String deviceId, String key, String value) {
                try {
                    JSONObject messageJson = new JSONObject(value);
                    Message message = new Message(
                        messageJson.getString("content"),
                        messageJson.getLong("timestamp"),
                        messageJson.getString("senderId"),
                        messageJson.getString("senderName")
                    );
                    
                    // 避免处理自己发送的消息
                    if (!message.getSenderId().equals(DistributedDeviceInfo.getLocalDeviceId())) {
                        messageHistory.add(message);
                        if (messageListener != null) {
                            messageListener.onMessageReceived(message);
                        }
                    }
                } catch (JSONException e) {
                    HiLog.error(TAG, "Failed to parse message: " + e.getMessage());
                }
            }
        });
        
        // 监听设备列表变化
        dataManager.registerDataChangeListener(DEVICE_INFO_KEY, new DataChangeListener() {
            @Override
            public void onDataChanged(String deviceId, String key, String value) {
                updateConnectedDevices(value);
            }
        });
    }
    
    // 更新已连接设备列表
    private void updateConnectedDevices(String devicesJson) {
        try {
            JSONArray deviceArray = new JSONArray(devicesJson);
            List<DeviceInfo> newDevices = new ArrayList<>();
            
            for (int i = 0; i < deviceArray.length(); i++) {
                JSONObject deviceJson = deviceArray.getJSONObject(i);
                DeviceInfo device = new DeviceInfo(
                    deviceJson.getString("deviceId"),
                    deviceJson.getString("deviceName"),
                    deviceJson.getString("deviceType")
                );
                newDevices.add(device);
            }
            
            connectedDevices = newDevices;
            if (messageListener != null) {
                messageListener.onDeviceListUpdated(connectedDevices);
            }
        } catch (JSONException e) {
            HiLog.error(TAG, "Failed to parse device list: " + e.getMessage());
        }
    }
    
    // 获取消息历史
    public List<Message> getMessageHistory() {
        return new ArrayList<>(messageHistory);
    }
    
    // 获取已连接设备
    public List<DeviceInfo> getConnectedDevices() {
        return new ArrayList<>(connectedDevices);
    }
    
    // 消息数据类
    public static class Message {
        private String content;
        private long timestamp;
        private String senderId;
        private String senderName;
        
        public Message(String content, long timestamp, String senderId, String senderName) {
            this.content = content;
            this.timestamp = timestamp;
            this.senderId = senderId;
            this.senderName = senderName;
        }
        
        // getter方法省略...
    }
    
    // 设备信息类
    public static class DeviceInfo {
        private String deviceId;
        private String deviceName;
        private String deviceType;
        
        public DeviceInfo(String deviceId, String deviceName, String deviceType) {
            this.deviceId = deviceId;
            this.deviceName = deviceName;
            this.deviceType = deviceType;
        }
        
        // getter方法省略...
    }
    
    public interface MessageListener {
        void onMessageReceived(Message message);
        void onDeviceConnected(DeviceInfo device);
        void onDeviceDisconnected(DeviceInfo device);
        void onDeviceListUpdated(List<DeviceInfo> devices);
    }
    
    private int versionCounter = 0;
    private synchronized int getNextVersion() {
        return ++versionCounter;
    }
}

2. 消息发送界面实现

public class MessageSenderSlice extends AbilitySlice {
    private static final String TAG = "MessageSenderSlice";
    private MessageSyncService messageSync;
    private TextField messageInput;
    private Text messageDisplay;
    private Text deviceStatusDisplay;
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        setUIContent(ResourceTable.Layout_message_sender_layout);
        
        // 初始化消息同步服务
        messageSync = MessageSyncService.getInstance(this);
        
        // 获取UI组件
        messageInput = (TextField) findComponentById(ResourceTable.Id_message_input);
        Button sendButton = (Button) findComponentById(ResourceTable.Id_send_button);
        messageDisplay = (Text) findComponentById(ResourceTable.Id_message_display);
        deviceStatusDisplay = (Text) findComponentById(ResourceTable.Id_device_status);
        
        // 设置发送按钮点击事件
        sendButton.setClickedListener(component -> {
            String message = messageInput.getText();
            if (!message.isEmpty()) {
                messageSync.sendMessage(message);
                messageInput.setText("");
                updateMessageDisplay();
            }
        });
        
        // 注册消息监听器
        messageSync.registerMessageListener(new MessageSyncService.MessageListener() {
            @Override
            public void onMessageReceived(MessageSyncService.Message message) {
                getUITaskDispatcher().asyncDispatch(() -> {
                    updateMessageDisplay();
                });
            }
            
            @Override
            public void onDeviceConnected(MessageSyncService.DeviceInfo device) {
                getUITaskDispatcher().asyncDispatch(() -> {
                    updateDeviceStatus();
                    showToast(device.getDeviceName() + " 已连接");
                });
            }
            
            @Override
            public void onDeviceDisconnected(MessageSyncService.DeviceInfo device) {
                getUITaskDispatcher().asyncDispatch(() -> {
                    updateDeviceStatus();
                    showToast(device.getDeviceName() + " 已断开");
                });
            }
            
            @Override
            public void onDeviceListUpdated(List<MessageSyncService.DeviceInfo> devices) {
                getUITaskDispatcher().asyncDispatch(() -> {
                    updateDeviceStatus();
                });
            }
        });
        
        // 初始更新显示
        updateMessageDisplay();
        updateDeviceStatus();
    }
    
    private void updateMessageDisplay() {
        StringBuilder builder = new StringBuilder();
        List<MessageSyncService.Message> messages = messageSync.getMessageHistory();
        
        for (MessageSyncService.Message message : messages) {
            String prefix = message.getSenderId().equals(DistributedDeviceInfo.getLocalDeviceId()) 
                ? "我: " : message.getSenderName() + ": ";
            builder.append(prefix).append(message.getContent()).append("\n");
        }
        
        messageDisplay.setText(builder.toString());
    }
    
    private void updateDeviceStatus() {
        StringBuilder builder = new StringBuilder("已连接设备:\n");
        List<MessageSyncService.DeviceInfo> devices = messageSync.getConnectedDevices();
        
        for (MessageSyncService.DeviceInfo device : devices) {
            builder.append("- ").append(device.getDeviceName())
                .append(" (").append(device.getDeviceType()).append(")\n");
        }
        
        deviceStatusDisplay.setText(builder.toString());
    }
    
    private void showToast(String message) {
        new ToastDialog(getContext())
            .setText(message)
            .show();
    }
}

3. XML布局文件

<!-- 消息发送界面布局 message_sender_layout.xml -->
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:width="match_parent"
    ohos:height="match_parent"
    ohos:orientation="vertical"
    ohos:padding="24vp">
    
    <Text
        ohos:width="match_parent"
        ohos:height="wrap_content"
        ohos:text="跨设备消息发送"
        ohos:text_size="24fp"
        ohos:margin_bottom="16vp"/>
        
    <TextField
        ohos:id="$+id:message_input"
        ohos:width="match_parent"
        ohos:height="80vp"
        ohos:hint="输入要发送的消息"
        ohos:margin_bottom="16vp"/>
        
    <Button
        ohos:id="$+id:send_button"
        ohos:width="match_parent"
        ohos:height="60vp"
        ohos:text="发送"
        ohos:margin_bottom="16vp"/>
        
    <ScrollView
        ohos:width="match_parent"
        ohos:height="200vp"
        ohos:margin_bottom="16vp">
        
        <Text
            ohos:id="$+id:message_display"
            ohos:width="match_parent"
            ohos:height="match_parent"
            ohos:text="消息将显示在这里..."/>
    </ScrollView>
    
    <ScrollView
        ohos:width="match_parent"
        ohos:height="200vp">
        
        <Text
            ohos:id="$+id:device_status"
            ohos:width="match_parent"
            ohos:height="match_parent"
            ohos:text="设备状态将显示在这里..."/>
    </ScrollView>
</DirectionalLayout>

4. Ability配置 (config.json)

{
  "abilities": [
    {
      "name": "MessageSenderAbility",
      "type": "page",
      "label": "MessageSender",
      "icon": "$media:icon",
      "launchType": "standard",
      "backgroundModes": ["distributedNotification", "dataSync"]
    }
  ]
}

四、与《鸿蒙跨端U同步》的技术关联

本项目借鉴了游戏多设备同步的以下关键技术:

  1. ​状态同步机制​​:类似游戏中玩家状态的实时同步,本系统实现消息的实时同步
  2. ​设备管理​​:参考游戏中的设备连接管理,实现设备上下线通知
  3. ​冲突解决​​:采用类似游戏中的版本控制机制,确保消息顺序
  4. ​数据格式​​:使用JSON作为跨设备数据交换格式,与游戏同步机制一致

增强的同步逻辑(借鉴游戏同步):

// 增强的消息广播方法,增加可靠性检查
private void broadcastMessage(Message message) {
    // 检查设备连接状态
    if (connectedDevices.isEmpty()) {
        HiLog.warn(TAG, "No connected devices to broadcast");
        return;
    }
    
    // 创建消息对象
    JSONObject messageJson = new JSONObject();
    try {
        messageJson.put("content", message.getContent());
        messageJson.put("timestamp", message.getTimestamp());
        messageJson.put("senderId", message.getSenderId());
        messageJson.put("senderName", message.getSenderName());
        messageJson.put("version", getNextVersion());
        
        // 增加消息类型标识
        messageJson.put("type", "text_message");
        
        // 增加重试机制
        int retryCount = 0;
        boolean success = false;
        
        while (retryCount < 3 && !success) {
            success = dataManager.putString(MESSAGE_CHANNEL, 
                messageJson.toString(), 
                DistributedDataManager.PUT_MODE_RELIABLE);
            
            if (!success) {
                retryCount++;
                try {
                    Thread.sleep(200); // 短暂等待后重试
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
        
        if (!success) {
            HiLog.error(TAG, "Failed to broadcast message after retries");
        }
    } catch (JSONException e) {
        HiLog.error(TAG, "Failed to serialize message");
    }
}

五、项目特色与创新点

  1. ​实时消息同步​​:基于HarmonyOS分布式能力实现毫秒级消息同步
  2. ​设备感知​​:实时显示所有连接设备状态
  3. ​消息历史​​:完整记录所有设备发送的消息
  4. ​可靠传输​​:借鉴游戏同步的重试机制,确保消息送达
  5. ​轻量级设计​​:核心代码不超过300行,易于理解和扩展

六、总结

本项目成功实现了以下功能:

  1. 跨设备的实时文本消息发送与接收
  2. 设备连接状态的实时监控与显示
  3. 完整的消息历史记录
  4. 可靠的传输机制

通过借鉴游戏中的多设备同步技术,我们构建了一个稳定高效的跨设备通信系统。该系统不仅可以用于简单的消息传递,还可以扩展为:

  1. 跨设备文件共享
  2. 多设备协同办公
  3. 分布式计算任务分配
  4. 智能家居设备控制

未来可增加的功能包括:

  1. 消息加密传输
  2. 大文件分片传输
  3. 消息已读回执
  4. 设备间直接P2P通信

这个项目展示了HarmonyOS分布式能力的强大之处,为开发更复杂的跨设备应用奠定了基础。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐