#星光计划1.0# HarmonyOS 分布式之聊天室应用 原创 精华

发布于 2021-11-11 11:11
浏览
12收藏

作者:梁青松

介绍

之前给大家介绍过【#星光计划1.0# HarmonyOS 分布式之仿抖音应用】,此次给大家介绍一下基于鸿蒙分布式数据服务开发的聊天室应用,模拟现实中的聊天室对话,可以与小伙伴们互动、分享自己的故事给小伙伴。

效果演示

#星光计划1.0# HarmonyOS 分布式之聊天室应用-开源基础软件社区 #星光计划1.0# HarmonyOS 分布式之聊天室应用-开源基础软件社区

#星光计划1.0# HarmonyOS 分布式之聊天室应用-开源基础软件社区

项目类说明

#星光计划1.0# HarmonyOS 分布式之聊天室应用-开源基础软件社区

主要知识点

分布式数据服务

官方介绍:分布式数据服务主要实现用户设备中应用程序的数据内容的分布式同步。当设备1上的应用A在分布式数据库中增、删、改数据后,设备2上的应用A也可以获取到该数据库变化,总结一句话:多个设备共用一个数据库。

主页代码

没有特别复杂的逻辑,主要是分布式数据服务的使用,关键地方都有注释。

import com.ldd.myapp.bean.ChatDataBean;
import com.ldd.myapp.provider.ChatProvider;
import com.ldd.myapp.util.Tools;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.ListContainer;
import ohos.agp.components.TextField;
import ohos.app.Context;
import ohos.bundle.IBundleManager;
import ohos.data.distributed.common.*;
import ohos.data.distributed.user.SingleKvStore;
import ohos.utils.zson.ZSONArray;
import ohos.utils.zson.ZSONObject;

import java.util.ArrayList;
import java.util.List;

import static ohos.security.SystemPermission.DISTRIBUTED_DATASYNC;

/**
 * 主页
 */
public class MainAbilitySlice extends AbilitySlice {
    private Context mContext;
    // 聊天列表
    private ListContainer lcList;
    // 聊天数据
    private final List<ChatDataBean> listData = new ArrayList<>();
    // 聊天数据适配器
    private ChatProvider chatProvider;
    // 输入框
    private TextField tfContent;
    // 发送按钮
    private Button btnSend;

    // 分布式数据库管理器
    private KvManager kvManager;
    // 分布式数据库
    private SingleKvStore singleKvStore;
    // 数据库名称
    private static final String STORE_NAME = "ChatStore";
    // 存入的列表数据key
    private static final String KEY_DATA = "key_data";
    // 存入的头像索引
    private static final String KEY_PIC_INDEX = "key_pic_index";
    private int picIndex = 0;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        mContext = this;
        requestPermission();
        initComponent();
        initDatabase();
    }

    /**
     * 请求分布式权限
     */
    private void requestPermission() {
        if (verifySelfPermission(DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) {
            if (canRequestPermission(DISTRIBUTED_DATASYNC)) {
                requestPermissionsFromUser(new String[]{DISTRIBUTED_DATASYNC}, 0);
            }
        }
    }

    /**
     * 初始化组件
     */
    private void initComponent() {
        lcList = (ListContainer) findComponentById(ResourceTable.Id_lc_list);
        tfContent = (TextField) findComponentById(ResourceTable.Id_tf_content);
        tfContent.setAdjustInputPanel(true);
        btnSend = (Button) findComponentById(ResourceTable.Id_btn_send);
        btnSend.setEnabled(false);

        // 初始化适配器
        chatProvider = new ChatProvider(mContext, listData);
        lcList.setItemProvider(chatProvider);

        // 输入框内容变化监听
        tfContent.addTextObserver((text, start, before, count) -> {
            btnSend.setEnabled(text.length() != 0);
        });
        // 点击发送按钮
        btnSend.setClickedListener(component -> {
            String content = tfContent.getText().trim();
            listData.add(new ChatDataBean(Tools.getDeviceId(mContext),picIndex,content));
            // 存入数据库中
            singleKvStore.putString(KEY_DATA, ZSONObject.toZSONString(listData));

            // 清空输入框
            tfContent.setText("");
        });
    }

    /**
     * 初始化分布式数据库
     */
    private void initDatabase() {
        // 创建分布式数据库管理器
        kvManager = KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this));

        // 数据库配置
        Options options = new Options();
        options.setCreateIfMissing(true) // 设置数据库不存在时是否创建
                .setEncrypt(false) // 设置数据库是否加密
                .setKvStoreType(KvStoreType.SINGLE_VERSION); //数据库类型
        // 创建分布式数据库
        singleKvStore = kvManager.getKvStore(options, STORE_NAME);
        // 监听数据库数据改变
        singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, new KvStoreObserver() {
            @Override
            public void onChange(ChangeNotification changeNotification) {
                List<Entry> insertEntries = changeNotification.getInsertEntries();
                List<Entry> updateEntries = changeNotification.getUpdateEntries();

                // 第一次存入数据,获取insertEntries
                if(insertEntries.size()>0){
                    for (Entry entry : insertEntries) {
                        if (KEY_DATA.equals(entry.getKey())) {
                            // 回调为非UI线程,需要在UI线程更新UI
                            getUITaskDispatcher().syncDispatch(() -> {
                                listData.clear();
                                listData.addAll(ZSONArray.stringToClassList(entry.getValue().getString(),ChatDataBean.class));
                                chatProvider.notifyDataChanged();
                                lcList.scrollTo(listData.size() - 1);
                            });
                        }
                    }
                }else if(updateEntries.size()>0){
                    for (Entry entry : updateEntries) {
                        if (KEY_DATA.equals(entry.getKey())) {
                            // 回调为非UI线程,需要在UI线程更新UI
                            getUITaskDispatcher().syncDispatch(() -> {
                                listData.clear();
                                listData.addAll(ZSONArray.stringToClassList(entry.getValue().getString(),ChatDataBean.class));
                                chatProvider.notifyDataChanged();
                                lcList.scrollTo(listData.size() - 1);
                            });
                        }
                    }
                }
            }
        });

        try {
            picIndex = singleKvStore.getInt(KEY_PIC_INDEX);
            singleKvStore.putInt(KEY_PIC_INDEX, picIndex + 1);
        } catch (KvStoreException e) {
            e.printStackTrace();
            // 没有找到,首次进入
            if (e.getKvStoreErrorCode() == KvStoreErrorCode.KEY_NOT_FOUND) {
                picIndex = 0;
                singleKvStore.putInt(KEY_PIC_INDEX, picIndex + 1);
            }
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        kvManager.closeKvStore(singleKvStore);
    }
}

简单案例

1、config.json配置

"reqPermissions": [
      {
        "reason": "多设备协同",
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "usedScene": {
          "ability": [
            "MainAbility"
          ],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
      },
      {
        "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
      },
      {
        "name": "ohos.permission.GET_BUNDLE_INFO"
      }
    ]

2、布局页面

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical">

    <Text
        ohos:id="$+id:text"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text="数据:0"
        ohos:text_size="15fp"/>

    <Button
        ohos:margin="20vp"
        ohos:id="$+id:button"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:background_element="$graphic:button_bg"
        ohos:padding="10vp"
        ohos:text="点击+1"
        ohos:text_color="white"
        ohos:text_size="15fp"/>

</DirectionalLayout>

3、MainAbilitySlice代码

import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.ListContainer;
import ohos.agp.components.Text;
import ohos.agp.components.TextField;
import ohos.bundle.IBundleManager;
import ohos.data.distributed.common.*;
import ohos.data.distributed.user.SingleKvStore;
import ohos.utils.zson.ZSONArray;

import java.util.List;

import static ohos.security.SystemPermission.DISTRIBUTED_DATASYNC;

public class MainAbilitySlice extends AbilitySlice {
    // 显示数据
    private Text text;
    // 分布式数据库管理器
    private KvManager kvManager;
    // 分布式数据库
    private SingleKvStore singleKvStore;
    // 数据库名称
    private static final String STORE_NAME = "MyStore";
    // 存入的数据key
    private static final String KEY_COUNT = "key_count";

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        requestPermission();
        initDatabase();
        initComponent();
    }

    /**
     * 请求分布式权限
     */
    private void requestPermission() {
        if (verifySelfPermission(DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) {
            if (canRequestPermission(DISTRIBUTED_DATASYNC)) {
                requestPermissionsFromUser(new String[]{DISTRIBUTED_DATASYNC}, 0);
            }
        }
    }

    /**
     * 初始化分布式数据库
     */
    private void initDatabase() {
        // 创建分布式数据库管理器
        kvManager = KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this));

        // 数据库配置
        Options options = new Options();
        options.setCreateIfMissing(true) // 设置数据库不存在时是否创建
                .setEncrypt(false) // 设置数据库是否加密
                .setKvStoreType(KvStoreType.SINGLE_VERSION); //数据库类型
        // 创建分布式数据库
        singleKvStore = kvManager.getKvStore(options, STORE_NAME);
        // 监听数据库数据改变
        singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, new KvStoreObserver() {
            @Override
            public void onChange(ChangeNotification changeNotification) {
                List<Entry> insertEntries = changeNotification.getInsertEntries();
                List<Entry> updateEntries = changeNotification.getUpdateEntries();

                // 第一次存入数据,获取insertEntries
                if (insertEntries.size() > 0) {
                    for (Entry entry : insertEntries) {
                        if (KEY_COUNT.equals(entry.getKey())) {
                            // 回调为非UI线程,需要在UI线程更新UI
                            getUITaskDispatcher().syncDispatch(() -> {
                                int count = entry.getValue().getInt();
                                text.setText("数据:"+count);
                            });
                        }
                    }
                } else if (updateEntries.size() > 0) {
                    for (Entry entry : updateEntries) {
                        if (KEY_COUNT.equals(entry.getKey())) {
                            // 回调为非UI线程,需要在UI线程更新UI
                            getUITaskDispatcher().syncDispatch(() -> {
                                int count = entry.getValue().getInt();
                                text.setText("数据:"+count);
                            });
                        }
                    }
                }
            }
        });

    }

    /**
     * 初始化组件
     */
    private void initComponent() {
        text = (Text) findComponentById(ResourceTable.Id_text);
        Button button = (Button) findComponentById(ResourceTable.Id_button);

        // 点击事件
        button.setClickedListener(component -> {
            try {
                int count = singleKvStore.getInt(KEY_COUNT);
                singleKvStore.putInt(KEY_COUNT, count + 1);
            } catch (KvStoreException e) {
                e.printStackTrace();
                // 没有找到,首次进入
                if (e.getKvStoreErrorCode() == KvStoreErrorCode.KEY_NOT_FOUND) {
                    int count = 0;
                    singleKvStore.putInt(KEY_COUNT, count + 1);
                }
            }
        });
    }
}

注释比较详细,主要注意2个点:

  1. 获取数据时加入try catch块,处理key未找到的情况
  2. 数据库数据改变监听回调是非UI线程,如果更新UI必须切换到UI线程

以上简单案例就是让你快速掌握分布式数据服务:多个设备相同的应用之间使用同一个数据库。

项目地址

https://gitee.com/liangdidi/DistributedChatDemo.git(需要登录才能看到演示图)

更多原创内容请关注:开鸿 HarmonyOS 学院

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

【本文正在参与51CTO HarmonyOS技术社区创作者激励-星光计划1.0】

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-11-11 11:11:28修改
14
收藏 12
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐