数据库开发及联调
本节之前,我们已经完成了所有设置页面的开发,本节我们将开发ClockManager关系型数据库的操作,实现clock的增删改查持久化,并联调之前所有的界面,在真机上验证闹钟的创建,修改,显示和删除。
1.完善ClockManager类,实现闹钟的增删改查数据库
1.HarmonyOs内置关系型数据库,因为这个应用只需要主机设置和实现定时操作,所以只需要实现关系型数据库,而不需要使用分布式数据库。首先需要新增一个Data Ability名命为ClockDataAbility,里面定义闹钟表的表名,列,以及在onCreate中写入建表SQL。
public class ClockDataAbility extends Ability {
private static final String TAG = ClockDataAbility.class.getName();
public static final String DB_NAME = "crazyclock.db";
public static final String DB_TAB_NAME = "crazyclock";
public static final String DB_COLUMN_ID = "id";
public static final String DB_COLUMN_NAME = "name";
public static final String DB_COLUMN_BELL = "bell";
public static final String DB_COLUMN_HOUR = "hour";
public static final String DB_COLUMN_MINUTE = "minute";
public static final String DB_COLUMN_DURATION = "duration";
public static final String DB_COLUMN_ENABLE = "enable";
private static final int DB_VERSION = 1;
private StoreConfig config = StoreConfig.newDefaultConfig(DB_NAME);
private RdbStore rdbStore;
private RdbOpenCallback rdbOpenCallback = new RdbOpenCallback() {
@Override
public void onCreate(RdbStore store) {
store.executeSql("create table if not exists "
+ DB_TAB_NAME + " ("
+ DB_COLUMN_ID + " integer primary key, "
+ DB_COLUMN_NAME + " text not null, "
+ DB_COLUMN_BELL + " integer not null, "
+ DB_COLUMN_HOUR + " integer not null, "
+ DB_COLUMN_MINUTE + " integer not null, "
+ DB_COLUMN_DURATION + " integer not null, "
+ DB_COLUMN_ENABLE + " boolean)");
}
private int duration;
private int strategy;
private boolean isEnable;
@Override
public void onUpgrade(RdbStore store, int oldVersion, int newVersion) {
}
};
@Override
public void onStart(Intent intent) {
super.onStart(intent);
LogUtil.info(TAG, "ClockDataAbility onStart");
DatabaseHelper databaseHelper = new DatabaseHelper(this);
rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null);
}
}
-
在ClockDataAbility中增加函数,实现关系型数据库的增删改查持久化,可以参考官方文档。
@Override
public int insert(Uri uri, ValuesBucket value) {
LogUtil.info(TAG, "ClockDataAbility insert");
String path = uri.getLastPath();
if (!DB_TAB_NAME.equals(path)) {
LogUtil.info(TAG, "DataAbility insert path is not matched");
return -1;
}
ValuesBucket values = new ValuesBucket();
values.putString(DB_COLUMN_NAME, value.getString(DB_COLUMN_NAME));
values.putInteger(DB_COLUMN_BELL, value.getInteger(DB_COLUMN_BELL));
values.putInteger(DB_COLUMN_HOUR, value.getInteger(DB_COLUMN_HOUR));
values.putInteger(DB_COLUMN_MINUTE, value.getInteger(DB_COLUMN_MINUTE));
values.putInteger(DB_COLUMN_DURATION, value.getInteger(DB_COLUMN_DURATION));
values.putBoolean(DB_COLUMN_ENABLE, value.getBoolean(DB_COLUMN_ENABLE));
int index = (int) rdbStore.insert(DB_TAB_NAME, values);
DataAbilityHelper.creator(this, uri).notifyChange(uri);//todo ?
return index;
}
@Override
public int delete(Uri uri, DataAbilityPredicates predicates) {
RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, DB_TAB_NAME);
int index = rdbStore.delete(rdbPredicates);
DataAbilityHelper.creator(this, uri).notifyChange(uri);
return index;
}
@Override
public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, DB_TAB_NAME);
int index = rdbStore.update(value, rdbPredicates);
LogUtil.info(TAG, "update: " + index);
DataAbilityHelper.creator(this, uri).notifyChange(uri);
return index;
}
@Override
public FileDescriptor openFile(Uri uri, String mode) {
return null;
}
@Override
public String[] getFileTypes(Uri uri, String mimeTypeFilter) {
return new String[0];
}
@Override
public PacMap call(String method, String arg, PacMap extras) {
return null;
}
@Override
public String getType(Uri uri) {
return null;
}
1.3 ClockManager实现对ClockDataAbility的封装,以Clock为对象进行操作,对外提供单例对象。其中BASE_URI要与ClockDataAbility的完整类名一致。
ClockDataAbility.java
package com.madixin.clock.setting.manager;
import com.madixin.clock.common.util.LogUtil;
import com.madixin.clock.setting.ability.ClockDataAbility;
import com.madixin.clock.setting.model.Clock;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.app.Context;
import ohos.data.dataability.DataAbilityPredicates;
import ohos.data.rdb.ValuesBucket;
import ohos.data.resultset.ResultSet;
import ohos.utils.net.Uri;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
public class ClockManager {
private static final String TAG = ClockManager.class.getName();
private static ClockManager instance;
private static final String BASE_URI = "dataability:///com.madixin.clock.setting.ability.ClockDataAbility";
private Context context;
private DataAbilityHelper databaseHelper;
private static final String DATA_PATH = "/crazyclock";
private ClockManager(Context context) {
this.context = context;
this.databaseHelper = DataAbilityHelper.creator(context);
}
private List<Clock> clockList = new LinkedList<>();
public static ClockManager getInstance(Context context) {
if (instance == null) {
instance = new ClockManager(context);
}
return instance;
}
/**
* 获取所有闹钟
* @return 闹钟列表
*/
public List<Clock> getAllClocks() {
this.clockList = new LinkedList<>();
String[] columns = new String[]{ClockDataAbility.DB_COLUMN_ID,
ClockDataAbility.DB_COLUMN_NAME, ClockDataAbility.DB_COLUMN_BELL, ClockDataAbility.DB_COLUMN_HOUR,
ClockDataAbility.DB_COLUMN_MINUTE, ClockDataAbility.DB_COLUMN_DURATION, ClockDataAbility.DB_COLUMN_ENABLE};
// 构造查询条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
//predicates.between(DB_COLUMN_AGE, 15, 40);
try {
ResultSet resultSet = databaseHelper.query(Uri.parse(BASE_URI + DATA_PATH),
columns, predicates);
if (resultSet == null || resultSet.getRowCount() == 0) {
LogUtil.info(TAG, "query: resultSet is null or no result found");
return this.clockList;
}
resultSet.goToFirstRow();
do {
Clock clock = new Clock();
clock.setId(resultSet.getInt(resultSet.getColumnIndexForName(ClockDataAbility.DB_COLUMN_ID)));
clock.setName(resultSet.getString(resultSet.getColumnIndexForName(ClockDataAbility.DB_COLUMN_NAME)));
clock.setBell(resultSet.getInt(resultSet.getColumnIndexForName(ClockDataAbility.DB_COLUMN_BELL)));
clock.setHour(resultSet.getInt(resultSet.getColumnIndexForName(ClockDataAbility.DB_COLUMN_HOUR)));
clock.setMinute(resultSet.getInt(resultSet.getColumnIndexForName(ClockDataAbility.DB_COLUMN_MINUTE)));
clock.setDuration(resultSet.getInt(resultSet.getColumnIndexForName(ClockDataAbility.DB_COLUMN_DURATION)));
clock.setEnable(resultSet.getInt(resultSet.getColumnIndexForName(ClockDataAbility.DB_COLUMN_ENABLE)) == 1);
this.clockList.add(clock);
LogUtil.info(TAG, "query: clock :" + clock);
} while (resultSet.goToNextRow());
} catch (DataAbilityRemoteException | IllegalStateException exception) {
LogUtil.error(TAG, "query: dataRemote exception | illegalStateException" + exception.getMessage());
}
LogUtil.info(TAG, "getAllClocks : size= " + this.clockList.size());
return this.clockList;
}
/**
* 创建新闹钟
* @param clock clock
*/
public void createNewClock(Clock clock) {
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString(ClockDataAbility.DB_COLUMN_NAME, clock.getName());
valuesBucket.putInteger(ClockDataAbility.DB_COLUMN_BELL, clock.getBell());
valuesBucket.putInteger(ClockDataAbility.DB_COLUMN_HOUR, clock.getHour());
valuesBucket.putInteger(ClockDataAbility.DB_COLUMN_MINUTE, clock.getMinute());
valuesBucket.putInteger(ClockDataAbility.DB_COLUMN_DURATION, clock.getDuration());
valuesBucket.putBoolean(ClockDataAbility.DB_COLUMN_ENABLE, clock.isEnable());
try {
int id = databaseHelper.insert(Uri.parse(BASE_URI + DATA_PATH), valuesBucket);
if (id == -1) {
LogUtil.error(TAG, "fail to insert");
} else {
LogUtil.info(TAG, "success to insert id=" + id);
}
} catch (DataAbilityRemoteException | IllegalStateException exception) {
LogUtil.error(TAG, "insert: dataRemote exception | illegalStateException" + exception.getMessage());
}
}
/**
* 删除闹钟
* @param clock clock
* @return
*/
public int deleteClock(Clock clock) {
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.contains(ClockDataAbility.DB_COLUMN_ID, String.valueOf(clock.getId()));
try {
return databaseHelper.delete(Uri.parse(BASE_URI + DATA_PATH), predicates);
} catch (DataAbilityRemoteException e) {
LogUtil.error(TAG, "delete: dataRemote exception | illegalStateException" + e.getMessage());
}
return -1;
}
/**
* 返回列表第几个的闹钟数据
*
* @param itemId 列表项
* @return Optional<Clock>
*/
public Optional<Clock> getClockByItemId(int itemId) {
if (itemId > this.clockList.size() || this.clockList.size() == 0) {
return Optional.empty();
}
return Optional.ofNullable(this.clockList.get(itemId));
}
/**
* 更新闹钟
* @param clock clock
* @return
*/
public int updateClock(Clock clock) {
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.contains(ClockDataAbility.DB_COLUMN_ID, String.valueOf(clock.getId()));
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString(ClockDataAbility.DB_COLUMN_NAME, clock.getName());
valuesBucket.putInteger(ClockDataAbility.DB_COLUMN_BELL, clock.getBell());
valuesBucket.putInteger(ClockDataAbility.DB_COLUMN_HOUR, clock.getHour());
valuesBucket.putInteger(ClockDataAbility.DB_COLUMN_MINUTE, clock.getMinute());
valuesBucket.putInteger(ClockDataAbility.DB_COLUMN_DURATION, clock.getDuration());
valuesBucket.putBoolean(ClockDataAbility.DB_COLUMN_ENABLE, clock.isEnable());
try {
int id = databaseHelper.update(Uri.parse(BASE_URI + DATA_PATH), valuesBucket, predicates);
if (id == -1) {
LogUtil.error(TAG, "fail to insert");
} else {
LogUtil.info(TAG, "success to insert id=" + id);
return id;
}
} catch (DataAbilityRemoteException | IllegalStateException exception) {
LogUtil.error(TAG, "insert: dataRemote exception | illegalStateException" + exception.getMessage());
}
return -1;
}
}
2.实现主页面列表开关控制闹钟是否开启
2.1.在ListViewClockItemProvider中增加自定义接口SwitchStateChangedListener。
public interface SwitchStateChangedListener {
public void onSwitchStateChanged(Button button, boolean isEnable, int position);
}
private SwitchStateChangedListener switchStateChangedListener;
2.2 将Switch的改变事件向slice发布出去。
viewHolder.switchState.setCheckedStateChangedListener((button, isEnable) -> {
switchStateChangedListener.onSwitchStateChanged(button, isEnable, position);
});
2.3 在MainAbilitySlice.java的initListContainer方法中监听Switch切换事件,并更新数据库。
// Switch切换按钮
listViewClockItemProvider.setSwitchStateChangedListener(((button, isEnable, position) -> {
Optional<Clock> clockOptional = ClockManager.getInstance(this.getApplicationContext()).getClockByItemId(position);
if (clockOptional.isPresent()) {
Clock curClock = clockOptional.get();
curClock.setEnable(isEnable);
ClockManager.getInstance(this.getApplicationContext()).updateClock(curClock);
}
}));
3.小结
本节完成了数据库的联调操作后,整个应用的设置闹钟功能已经全部开发完了。在模拟器上调试后,建议使用真机P40调试来验证功能,在真机中每次操作后的数据都会保存在真机的数据库里。真机调试需要签名等步骤,可参考官方文档。
下一步我们将在闹钟FA中开发闹钟界面。
4.代码地址:
github,提交记录:e6611286ebf042ec060ef999d0c12f9f77a2b965