如何使用分布式数据库方法详解
1. 如何使用分布式数据库
介绍
分布式数据服务(Distributed Data Service,DDS) 为应用程序提供不同设备间数据库数据分布式的能力。通过调用分布式数据接口,应用程序将数据保存到分布式数据库中。通过结合帐号、应用和数据库三元组,分布式数据服务对属于不同的应用的数据进行隔离,保证不同应用之间的数据不能通过分布式数据服务互相访问。在通过可信认证的设备间,分布式数据服务支持应用数据相互同步,为用户提供在多种终端设备上一致的数据访问体验。
有关分布式数据服务更加详细的介绍可以参考分布式数据服务。
本教程将为您完整介绍以下内容并展示完整示例:
- 分布式数据库的创建
- 分布式数据库的插入和删除
- 分布式数据库的数据同步
说明:
实现分布式数据库,需要至少两个设备处于同一个分布式网络中,可以通过操作如下配置实现:所有设备接入同一网络,
所有设备登陆相同华为账号,
所有设备上开启“设置->更多连接->多设备协同 ”
2. 实现分布式数据库需要申请的权限
为了实现分布式数据库,需要在entry\src\main\config.json中申请ohos.permission.DISTRIBUTED_DATASYNC权限,以便允许不同设备间的数据交换。示例代码如下:
module": {
......
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
3. 如何创建分布式数据库
要创建分布式数据库,首先要做的就是创建分布式数据库管理器实例KvManager,我们定义了如下方法:
private KvManager createManager() {
KvManager manager = null;
try {
KvManagerConfig config = new KvManagerConfig(this);
manager = KvManagerFactory.getInstance().createKvManager(config);
}
catch (KvStoreException exception) {
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "some exception happen");
}
return manager;
}
创建成功后,借助KvManager创建SINGLE_VERSION分布式数据库,方法如下:
private SingleKvStore createDb(KvManager kvManager) {
SingleKvStore kvStore = null;
try {
Options options = new Options();
options.setCreateIfMissing(true).setEncrypt(false).setKvStoreType(KvStoreType.SINGLE_VERSION);
kvStore = kvManager.getKvStore(options, STORE_ID);
} catch (KvStoreException exception) {
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "some exception happen");
}
return kvStore;
}
说明:
SINGLE_VERSION分布式数据库是指数据在本地保存是以单个KV条目为单位的方式保存,对每个Key最多只保存一个条目项,当数据在本地被用户修改时,不管它是否已经被同步出去,均直接在这个条目上进行修改。
最后是订阅分布式数据库中数据变化,方法如下:
private void subscribeDb(SingleKvStore singleKvStore) {
KvStoreObserver kvStoreObserverClient = new KvStoreObserverClient();
singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_REMOTE, kvStoreObserverClient);
}
4. 如何进行数据查询、插入和删除
数据插入
在将数据写入分布式数据库之前,需要先构造分布式数据库的Key(键)和Value(值),通过putString方法将数据写入到数据库中,具体示例如下:
private void writeData(String key, String value) {
if (key == null || key.isEmpty() || value == null || value.isEmpty()) {
return;
}
singleKvStore.putString(key, value);
}
数据查询
分布式数据库中的数据查询是根据Key(键)来进行的,如果指定Key(键),则会查询出对应Key(键)的数据;如果不指定Key,既为空,则查询出所有数据,查询示例代码如下
private void queryContact() {
List entryList = singleKvStore.getEntries("");
contactArray.clear();
try {
for (Entry entry : entryList) {
contactArray.add(new Contacter(entry.getValue().getString(), entry.getKey()));
}
} catch (KvStoreException exception) {
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG,"the value must be String");
}
contactAdapter.notifyDataChanged();
}
说明:
以上代码除查询外,还有部分场景业务代码。
数据删除
对于分布式数据库的删除操作,可以直接调用deleteKvStore()方法,但是需要传递事先定义好的STORE_ID参数(具体定义参考下文完整代码),示例代码如下:
@Override
protected void onStop() {
super.onStop();
kvManager.closeKvStore(singleKvStore);
kvManager.deleteKvStore(STORE_ID);
}
5. 如何进行分布式数据库的同步
在进行数据同步之前,首先需要先获取当前组网环境中的设备列表,然后指定同步方式(PULL_ONLY,PUSH_ONLY,PUSH_PULL)进行同步,以PUSH_PULL方式为例,示例代码如下:
private void syncContact() {
List<DeviceInfo> deviceInfoList = kvManager.getConnectedDevicesInfo(DeviceFilterStrategy.NO_FILTER);
List<String> deviceIdList = new ArrayList<>();
for (DeviceInfo deviceInfo : deviceInfoList) {
deviceIdList.add(deviceInfo.getId());
}
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "device size= " + deviceIdList.size());
if (deviceIdList.size() == 0) {
showTip("组网失败");
return;
}
singleKvStore.registerSyncCallback(new SyncCallback() {
@Override
public void syncCompleted(Map<String, Integer> map) {
getUITaskDispatcher().asyncDispatch(new Runnable() {
@Override
public void run() {
queryContact();
showTip("同步成功");
}
});
singleKvStore.unRegisterSyncCallback();
}
});
singleKvStore.sync(deviceIdList, SyncMode.PUSH_PULL);
}
说明:
以上代码除数据同步外,还有部分场景业务代码。
6. 最终实现效果
8. 示例代码
代码结构解读
为了方便您的学习,我们提供了的分布式数据库示例工程的完整代码,工程以一个简易的信息管理系统为背景,代码的工程结构如下:
- Provider:ContactProvider为数据信息的适配器,主要作用为高效传递和使用相关数据。
- Been:Contacter封装了联系人信息。
- Componet:ContactComponent添加和修改信息的Dialog实现类。
- Slice:ContactSlice应用主页面,分布式数据库逻辑实现类。
除以上文件,工程中还用到了三个xml布局文件,他们声明在了main\resources\base\layout目录下。
编写布局和样式
首先在HUAWEI DevEco Studio创建一个Phone的Empty Feature Ability(Java)模板工程,然后在 "resources/layout"下新增三个布局。
ability_contact.xml:该布局定义了"同步"和"添加"两个Button,姓名、手机号码、信息管理三个Text,具体代码如下:
<?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:orientation="vertical">
<DependentLayout
ohos:width="match_parent"
ohos:height="60vp"
>
<Text
ohos:height="match_content"
ohos:width="match_content"
ohos:text="信息管理"
ohos:text_color="#222222"
ohos:text_size="22fp"
ohos:center_in_parent="true"
/>
<Button
ohos:id="$+id:addContact"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="添加"
ohos:text_color="#a0a0a0"
ohos:text_size="18fp"
ohos:align_parent_right="true"
ohos:vertical_center="true"
ohos:right_margin="16vp"
/>
<Button
ohos:id="$+id:sync"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="同步"
ohos:text_color="#a0a0a0"
ohos:text_size="18fp"
ohos:align_parent_left="true"
ohos:vertical_center="true"
ohos:left_margin="16vp"
/>
</DependentLayout>
<Component
ohos:height="1vp"
ohos:width="match_parent"
ohos:background_element="#eeeeee"
/>
<DirectionalLayout
ohos:width="match_parent"
ohos:height="40vp"
ohos:orientation="horizontal"
>
<Text
ohos:width="match_parent"
ohos:height="match_content"
ohos:id="$+id:name"
ohos:text="姓名"
ohos:text_color="#222222"
ohos:text_size="16fp"
ohos:weight="10"
ohos:text_alignment="center"
ohos:layout_alignment="center"
/>
<Text
ohos:width="match_parent"
ohos:height="match_content"
ohos:id="$+id:phone"
ohos:text="手机号码"
ohos:text_color="#555555"
ohos:text_size="16fp"
ohos:weight="10"
ohos:text_alignment="center"
ohos:layout_alignment="center"
/>
<Text
ohos:width="match_parent"
ohos:height="40vp"
ohos:weight="7"
ohos:left_margin="20vp"
>
</Text>
</DirectionalLayout>
<ListContainer
ohos:id="$+id:listContainer"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical"
/>
</DirectionalLayout>
item_contact.xml:该布局为新增信息后,每条信息后面的编辑和删除Button,具体代码如下:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:width="match_parent"
ohos:height="41vp"
ohos:orientation="vertical"
>
<DirectionalLayout
ohos:id="$+id:dir_id"
ohos:width="match_parent"
ohos:height="40vp"
ohos:orientation="horizontal"
>
<Text
ohos:width="match_parent"
ohos:height="match_content"
ohos:id="$+id:name"
ohos:text_color="#222222"
ohos:text_size="16fp"
ohos:weight="10"
ohos:text_alignment="center"
ohos:layout_alignment="center"
/>
<Text
ohos:width="match_parent"
ohos:height="match_content"
ohos:id="$+id:phone"
ohos:text_color="#555555"
ohos:text_size="16fp"
ohos:weight="10"
ohos:text_alignment="center"
ohos:layout_alignment="center"
/>
<DirectionalLayout
ohos:width="match_parent"
ohos:height="40vp"
ohos:orientation="horizontal"
ohos:left_margin="20vp"
ohos:weight="7"
>
<Button
ohos:id="$+id:edit"
ohos:width="match_content"
ohos:height="match_content"
ohos:text="编辑"
ohos:text_color="#00dddd"
ohos:text_size="16fp"
ohos:padding="4vp"
ohos:layout_alignment="center"
/>
<Button
ohos:id="$+id:delete"
ohos:width="match_content"
ohos:height="match_content"
ohos:text="删除"
ohos:text_color="#cc0000"
ohos:text_size="16fp"
ohos:padding="4vp"
ohos:layout_alignment="center"
/>
</DirectionalLayout>
</DirectionalLayout>
<Text
ohos:width="match_parent"
ohos:height="1vp"
ohos:background_element="#aaeeeeee"
ohos:left_margin="20vp"
ohos:right_margin="20vp"
/>
</DirectionalLayout>
item_dialog.xml:该布局为点击添加按钮后出现的Dialog,主要由姓名和手机号码TextFIeld,以及确认Button构成,具体示例代码如下
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:width="match_parent"
ohos:height="match_content"
ohos:orientation="vertical"
>
<Text
ohos:id="$+id:title"
ohos:width="match_content"
ohos:height="match_content"
ohos:text="添加信息"
ohos:text_color="#111111"
ohos:text_size="18fp"
ohos:layout_alignment="center"
ohos:top_margin="30vp"
ohos:bottom_margin="30vp"
/>
<DirectionalLayout
ohos:width="match_parent"
ohos:height="40vp"
ohos:orientation="horizontal"
ohos:left_margin="19vp"
ohos:right_margin="19vp"
>
<Text
ohos:width="match_parent"
ohos:height="match_content"
ohos:text="姓名:"
ohos:text_color="#222222"
ohos:text_size="16fp"
ohos:weight="1"
ohos:layout_alignment="vertical_center|left"
/>
<TextField
ohos:width="match_parent"
ohos:height="match_parent"
ohos:id="$+id:name"
ohos:text_color="#555555"
ohos:text_size="16fp"
ohos:weight="3"
ohos:hint="输入姓名"
ohos:background_element="$graphic:background_input"
ohos:text_alignment="vertical_center|left"
ohos:left_padding="10vp"
ohos:layout_alignment="center"
/>
</DirectionalLayout>
<DirectionalLayout
ohos:width="match_parent"
ohos:height="40vp"
ohos:orientation="horizontal"
ohos:left_margin="19vp"
ohos:right_margin="19vp"
ohos:top_margin="16vp"
>
<Text
ohos:width="match_parent"
ohos:height="match_content"
ohos:text="手机号码:"
ohos:text_color="#222222"
ohos:text_size="16fp"
ohos:weight="1"
ohos:layout_alignment="vertical_center|left"
/>
<TextField
ohos:width="match_parent"
ohos:height="match_parent"
ohos:id="$+id:phone"
ohos:text_color="#555555"
ohos:text_size="16fp"
ohos:weight="3"
ohos:hint="输入手机号"
ohos:background_element="$graphic:background_input"
ohos:text_alignment="vertical_center|left"
ohos:left_padding="10vp"
ohos:layout_alignment="center"
/>
</DirectionalLayout>
<Button
ohos:id="$+id:confirm"
ohos:width="match_parent"
ohos:height="match_content"
ohos:text="确认"
ohos:text_color="#ffffff"
ohos:text_size="16fp"
ohos:layout_alignment="center"
ohos:left_margin="30vp"
ohos:right_margin="30vp"
ohos:top_margin="40vp"
ohos:bottom_margin="40vp"
ohos:bottom_padding="10vp"
ohos:top_padding="10vp"
ohos:background_element="$graphic:background_button"
/>
</DirectionalLayout>
随后在“resources/base/graphic/”目录下增加如下文件:
background_input.xml文件用来定义Input背景属性,示例代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="8vp"/>
<solid
ohos:color="#00000000"/>
<stroke ohos:width="1vp" ohos:color="#eeeeee"/>
</shape>
background_button.xml文件用来定义Button背景属性,示例代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="8vp"/>
<solid
ohos:color="#00dddd"/>
</shape>
业务逻辑代码
在"entry\src\main\java\com\huawei\codelab\provider"下新增ContactProvider.Java文件,该类的主要作用是提高使用和编辑数据的效率,示例代码如下:
import com.huawei.cookbooks.ResourceTable;
import com.huawei.cookbooks.been.Contacter;
import ohos.agp.components.BaseItemProvider;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Text;
import ohos.app.Context;
import java.util.List;
/**
* Contact List Adapter
*/
public class ContactProvider extends BaseItemProvider {
private List contactArray;
private Context context;
private AdapterClickListener adapterClickListener;
public ContactProvider(Context context, List contactArray) {
this.context = context;
this.contactArray = contactArray;
}
@Override
public int getCount() {
return contactArray == null ? 0 : contactArray.size();
}
@Override
public Object getItem(int position) {
if (position < contactArray.size() && position >= 0) {
return contactArray.get(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
/**
* Component of each item
*
* @param position Index
* @param componentPara Reused Component
* @param componentContainer Container
* @return Component of each item
*/
@Override
public Component getComponent(int position, Component componentPara, ComponentContainer componentContainer) {
ViewHolder viewHolder = null;
Component component = componentPara;
if (component == null) {
component = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_contact,
null, false);
viewHolder = new ViewHolder();
Component componentText = component.findComponentById(ResourceTable.Id_name);
if (componentText instanceof Text) {
viewHolder.name = (Text) componentText;
}
componentText = component.findComponentById(ResourceTable.Id_phone);
if (componentText instanceof Text) {
viewHolder.phone = (Text) componentText;
}
viewHolder.delete = (Button) component.findComponentById(ResourceTable.Id_delete);
viewHolder.edit = (Button) component.findComponentById(ResourceTable.Id_edit);
component.setTag(viewHolder);
} else {
if (component.getTag() instanceof ViewHolder) {
viewHolder = (ViewHolder) component.getTag();
}
}
if (viewHolder != null) {
viewHolder.name.setText(contactArray.get(position).getName());
viewHolder.phone.setText(contactArray.get(position).getPhone());
viewHolder.edit.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
if (adapterClickListener != null) {
adapterClickListener.edit(position);
}
}
});
viewHolder.delete.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
if (adapterClickListener != null) {
adapterClickListener.delete(position);
}
}
});
}
return component;
}
private static class ViewHolder {
private Text name;
private Text phone;
private Button edit;
private Button delete;
}
/**
* Defines the callback event interface.
*/
public interface AdapterClickListener {
void edit(int position);
void delete(int position);
}
public void setAdapterClickListener(AdapterClickListener adapterClickListener) {
this.adapterClickListener = adapterClickListener;
}
}
在"entry\src\main\java\com\huawei\codelab\been"下新增Contacter.Java文件,该类的主要作用是封装姓名和电话号信息,示例代码如下:
public class Contacter {
private String name;
private String phone;
public Contacter(String name, String phone) {
this.name = name;
this.phone = phone;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
}
在"entry\src\main\java\com\huawei\codelab\componet"下新增ContactComponent.Java文件,该类的主要作用是整合添加数据时dialog用到的组件以及初始化部分数据,示例代码如下:
import com.huawei.cookbooks.ResourceTable;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Text;
import ohos.agp.components.TextField;
import ohos.app.Context;
/**
* add/edit contact layout
*/
public class ContactComponent extends DirectionalLayout {
private Component component;
private TextField nameTextField;
private TextField phoneTextField;
private Text title;
private DialogCallBack dialogCallBack;
public ContactComponent(Context context) {
super(context);
addComponent(context);
initView();
initEvent();
}
private void initEvent() {
component.findComponentById(ResourceTable.Id_confirm).setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
confirmContact();
}
});
}
private void confirmContact() {
String nameInput = nameTextField.getText();
String phoneInput = phoneTextField.getText();
if (dialogCallBack != null) {
dialogCallBack.result(nameInput, phoneInput);
}
}
private void initView() {
Component componentText = component.findComponentById(ResourceTable.Id_name);
if (componentText instanceof TextField) {
nameTextField = (TextField) componentText;
}
componentText = component.findComponentById(ResourceTable.Id_phone);
if (componentText instanceof TextField) {
phoneTextField = (TextField) componentText;
}
componentText = component.findComponentById(ResourceTable.Id_title);
if (componentText instanceof Text) {
title = (Text) componentText;
}
}
private void addComponent(Context context) {
component = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_dialog,
null, false);
addComponent(component);
LayoutConfig layoutConfig = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_CONTENT);
setLayoutConfig(layoutConfig);
}
public void initData(String name, String phone) {
nameTextField.setBubbleSize(0,0);
phoneTextField.setBubbleSize(0,0);
if (name != null) {
nameTextField.setText(name);
}
if (phone != null) {
phoneTextField.setText(phone);
phoneTextField.setEnabled(false);
title.setText("修改信息");
}
}
public void setDialogCallBack(DialogCallBack dialogCallBack) {
this.dialogCallBack = dialogCallBack;
}
public interface DialogCallBack {
void result(String name, String phone);
}
}
在"entry\src\main\java\com\huawei\codelab\slice"下新增ContactSlice.Java文件,该类主要实现了分布式数据的创建,删除,和同步等操作以及部分业务逻辑,具体示例代码如下:
import com.huawei.cookbooks.ResourceTable;
import com.huawei.cookbooks.been.Contacter;
import com.huawei.cookbooks.component.ContactComponent;
import com.huawei.cookbooks.provider.ContactProvider;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.agp.window.dialog.IDialog;
import ohos.agp.window.dialog.ToastDialog;
import ohos.data.distributed.common.*;
import ohos.data.distributed.device.DeviceFilterStrategy;
import ohos.data.distributed.device.DeviceInfo;
import ohos.data.distributed.user.SingleKvStore;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ContactSlice extends AbilitySlice implements ContactProvider.AdapterClickListener {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "HiContact");
private static final String LOG_FORMAT = "%{public}s: %{public}s";
private static final String TAG = "ContactSlice";
private static final String STORE_ID = "contact_db1";
private static final int DIALOG_SIZE_WIDTH = 800;
private static final int DIALOG_SIZE_HEIGHT = 800;
private static final int SHOW_TIME = 1500;
private ContactProvider contactAdapter;
private List contactArray;
private KvManager kvManager;
private SingleKvStore singleKvStore;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_contact);
initDbManager();
initList();
initEvent();
queryContact();
}
/**
* Initialize click event
*/
private void initEvent() {
findComponentById(ResourceTable.Id_addContact).setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
addContact();
}
});
findComponentById(ResourceTable.Id_sync).setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
syncContact();
}
});
}
/**
* Initialize ListContainer
*/
private void initList() {
Component component = findComponentById(ResourceTable.Id_listContainer);
ListContainer listContainer;
if (component instanceof ListContainer) {
listContainer = (ListContainer) component;
contactArray = new ArrayList<>();
contactAdapter = new ContactProvider(this, contactArray);
listContainer.setItemProvider(contactAdapter);
contactAdapter.setAdapterClickListener(this);
}
}
/**
* Synchronizing contacts data
*/
private void syncContact() {
List deviceInfoList = kvManager.getConnectedDevicesInfo(DeviceFilterStrategy.NO_FILTER);
List deviceIdList = new ArrayList<>();
for (DeviceInfo deviceInfo : deviceInfoList) {
deviceIdList.add(deviceInfo.getId());
}
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "device size= " + deviceIdList.size());
if (deviceIdList.size() == 0) {
showTip("组网失败");
return;
}
singleKvStore.registerSyncCallback(new SyncCallback() {
@Override
public void syncCompleted(Map map) {
getUITaskDispatcher().asyncDispatch(new Runnable() {
@Override
public void run() {
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "sync success");
queryContact();
showTip("同步成功");
}
});
singleKvStore.unRegisterSyncCallback();
}
});
singleKvStore.sync(deviceIdList, SyncMode.PUSH_PULL);
}
/**
* Initializing Database Management
*/
private void initDbManager() {
kvManager = createManager();
singleKvStore = createDb(kvManager);
subscribeDb(singleKvStore);
}
/**
* Create a distributed database manager instance
*
* @return database manager
*/
private KvManager createManager() {
KvManager manager = null;
try {
KvManagerConfig config = new KvManagerConfig(this);
manager = KvManagerFactory.getInstance().createKvManager(config);
}
catch (KvStoreException exception) {
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "some exception happen");
}
return manager;
}
/**
* Creating a Single-Version Distributed Database
*
* @param kvManager Database management
* @return SingleKvStore
*/
private SingleKvStore createDb(KvManager kvManager) {
SingleKvStore kvStore = null;
try {
Options options = new Options();
options.setCreateIfMissing(true).setEncrypt(false).setKvStoreType(KvStoreType.SINGLE_VERSION);
kvStore = kvManager.getKvStore(options, STORE_ID);
} catch (KvStoreException exception) {
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "some exception happen");
}
return kvStore;
}
/**
* Subscribing to All (Currently, Remote) Data Change Notifications of a Single-Version Distributed Database
*
* @param singleKvStore Data operation
*/
private void subscribeDb(SingleKvStore singleKvStore) {
KvStoreObserver kvStoreObserverClient = new KvStoreObserverClient();
singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_REMOTE, kvStoreObserverClient);
}
/**
* Receive database messages
*/
private class KvStoreObserverClient implements KvStoreObserver {
@Override
public void onChange(ChangeNotification notification) {
getUITaskDispatcher().asyncDispatch(new Runnable() {
@Override
public void run() {
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "come to auto sync");
queryContact();
showTip("同步成功");
}
});
}
}
/**
* Query Local Contacts
*/
private void queryContact() {
List entryList = singleKvStore.getEntries("");
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG,"entryList size" + entryList.size());
contactArray.clear();
try {
for (Entry entry : entryList) {
contactArray.add(new Contacter(entry.getValue().getString(), entry.getKey()));
}
} catch (KvStoreException exception) {
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG,"the value must be String");
}
contactAdapter.notifyDataChanged();
}
/**
* Write key-value data to the single-version distributed database.
*
* @param key Stored key
* @param value Stored value
*/
private void writeData(String key, String value) {
if (key == null || key.isEmpty() || value == null || value.isEmpty()) {
return;
}
singleKvStore.putString(key, value);
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "writeContact key= " + key + " writeContact value= " + value);
}
/**
* Deleting Key Value Data from the Single-Version Distributed Database
*
* @param key Deleted Key
*/
private void deleteData(String key) {
if (key.isEmpty()) {
return;
}
singleKvStore.delete(key);
HiLog.info(LABEL_LOG, LOG_FORMAT,TAG, "deleteContact key= " + key);
}
/**
* Add Contact
*/
private void addContact() {
showDialog(null, null, new ContactComponent.DialogCallBack() {
@Override
public void result(String name, String phone) {
writeData(phone, name);
contactArray.add(new Contacter(name, phone));
contactAdapter.notifyDataSetItemInserted(contactAdapter.getCount());
queryContact();
}
});
}
/**
* Display dialog box
*
* @param name Contacts
* @param phone phone
* @param dialogCallBack callback result
*/
private void showDialog(String name, String phone, ContactComponent.DialogCallBack dialogCallBack) {
CommonDialog commonDialog = new CommonDialog(this);
ContactComponent component = new ContactComponent(this);
component.initData(name, phone);
component.setDialogCallBack(new ContactComponent.DialogCallBack() {
@Override
public void result(String nameInput, String phoneInput) {
if (nameInput.isEmpty() || phoneInput.isEmpty()) {
showTip("姓名与手机号码必填");
return;
}
if (phone == null && phoneIsExist(phoneInput)) {
showTip("手机号码已经存在了");
return;
}
if (dialogCallBack != null) {
dialogCallBack.result(nameInput, phoneInput);
}
commonDialog.remove();
}
});
commonDialog.setAutoClosable(true);
commonDialog.setContentCustomComponent(component);
commonDialog.show();
}
/**
* Check whether the mobile number exists
*
* @param phone phone
* @return boolean
*/
private boolean phoneIsExist(String phone) {
List entryList = singleKvStore.getEntries("");
for (Entry entry : entryList) {
if (entry.getKey().equals(phone)) {
return true;
}
}
return false;
}
/**
* Event triggered by clicking each edit button
*
* @param position Position of the item number
*/
@Override
public void edit(int position) {
Contacter contacter = contactArray.get(position);
showDialog(contacter.getName(), contacter.getPhone(), new ContactComponent.DialogCallBack() {
@Override
public void result(String name, String phone) {
writeData(phone, name);
contactArray.set(position, new Contacter(name, phone));
contactAdapter.notifyDataSetItemChanged(position);
queryContact();
}
});
}
/**
* Event triggered by clicking each delete button
*
* @param position Position of the item number
*/
@Override
public void delete(int position) {
CommonDialog commonDialog = new CommonDialog(this);
commonDialog.setSize(DIALOG_SIZE_WIDTH, DIALOG_SIZE_HEIGHT);
commonDialog.setAutoClosable(true);
commonDialog.setTitleText(" 警告")
.setContentText(" 确定要删除吗?")
.setButton(0, "取消", new IDialog.ClickedListener() {
@Override
public void onClick(IDialog iDialog, int i) {
iDialog.destroy();
}
})
.setButton(1, "确认", new IDialog.ClickedListener() {
@Override
public void onClick(IDialog iDialog, int i) {
if (position > contactArray.size() - 1) {
showTip("要删除的元素不存在");
return;
}
deleteData(contactArray.get(position).getPhone());
contactArray.remove(position);
contactAdapter.notifyDataChanged();
showTip("删除成功");
iDialog.destroy();
}
}).show();
}
/**
* tip message
*
* @param message message
*/
private void showTip(String message) {
new ToastDialog(this).setAlignment(LayoutAlignment.CENTER)
.setText(message).setDuration(SHOW_TIME).show();
}
@Override
protected void onStop() {
super.onStop();
kvManager.closeKvStore(singleKvStore);
kvManager.deleteKvStore(STORE_ID);
}
}
最后在MainAbility.Java文件中增加页面路由和动态权限申请,具体示例代码如下:
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.bundle.IBundleManager;
import com.huawei.codelab.slice.ContactSlice;
public class MainAbility extends Ability {
private static final String DISTRIBUTED_DATASYNC = "ohos.permission.DISTRIBUTED_DATASYNC";
private static final int PERMISSION_CODE = 20201203;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(ContactSlice.class.getName());
requestPermission();
}
private void requestPermission() {
if (verifySelfPermission(DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) {
if (canRequestPermission(DISTRIBUTED_DATASYNC)) {
requestPermissionsFromUser(
new String[]{DISTRIBUTED_DATASYNC}, PERMISSION_CODE);
}
}
}
}
恭喜您
通过本教程的学习,您已经学会了分布式数据库服务的基础操作(创建、新增、删除、同步)。