之前给大家介绍过《HarmonyOS 分布式之仿抖音应用》,此次给大家介绍一下基于鸿蒙分布式数据服务开发的聊天室应用,模拟现实中的聊天室对话,可以与小伙伴们互动、分享自己的故事给小伙伴。
主要知识点
分布式数据服务:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/database-mdds-guidelines-0000000000030122
官方介绍:分布式数据服务主要实现用户设备中应用程序的数据内容的分布式同步。
当设备 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 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 insertEntries = changeNotification.getInsertEntries();
List 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);
}
}
简单案例
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"
}
]
布局页面:
<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>
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 insertEntries = changeNotification.getInsertEntries();
List 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 个点:获取数据时加入 try catch 块,处理 key 未找到的情况。
数据库数据改变监听回调是非 UI 线程,如果更新 UI 必须切换到 UI 线程。
以上简单案例就是让你快速掌握分布式数据服务:多个设备相同的应用之间使用同一个数据库。 项目地址(需要登录才能看到演示图):
https://gitee.com/liangdidi/DistributedChatDemo.git
作者:梁青松全部0条评论
快来发表一下你的评论吧 !