需要被持久化的数据分为两种类型:
鸿蒙系统提供四种基于数据库的数据持久化方式:
数据库 | 结构化数据 | 非结构化数据 |
---|---|---|
关系型数据库(RDB) | √ | × |
对象关系映射(ORM) | √ | × |
应用偏好数据库 | × | √ |
分布式数据库 | √ | √ |
DatabaseHelper helper = new DatabaseHelper(this); StoreConfig config = StoreConfig .newDefaultConfig("tarena.sqlite"); rdb = helper.getRdbStore(config, 1, new RdbOpenCallback() { @Override public void onCreate(RdbStore rdbStore) { rdbStore.executeSql( "CREATE TABLE IF NOT EXISTS t_student(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT NOT NULL, " + "age INTEGER, " + "sex TINYINT, " + "subject TEXT)"); } @Override public void onUpgrade(RdbStore rdbStore, int i, int i1) { } });
ValuesBucket student = new ValuesBucket(); student.putString("name", "张飞"); student.putInteger("age", 22); student.putInteger("sex", 1); student.putString("subject", "C++"); long id = rdb.insert("t_student", student); if (id == -1) showToast("插入数据失败"); else showToast("插入数据成功(" + id + ")");
List<ValuesBucket> students = new ArrayList<>(); ValuesBucket student1 = new ValuesBucket(); student1.putString("name", "貂蝉"); student1.putInteger("age", 18); student1.putInteger("sex", 0); student1.putString("subject", "Java"); students.add(student1); ValuesBucket student2 = new ValuesBucket(); student2.putString("name", "曹操"); student2.putInteger("age", 30); student2.putInteger("sex", 1); student2.putString("subject", "Java"); students.add(student2); List<Long> ids = rdb.batchInsertOrThrowException( "t_student", students, ConflictResolution.ON_CONFLICT_ABORT); for (long id : ids) if (id == -1) showToast("插入数据失败"); else showToast("插入数据成功(" + id + ")");
RdbPredicates predicates = new RdbPredicates( "t_student").orderByAsc("id"); String[] columns = new String[] { "id", "name", "age", "sex", "subject"}; ResultSet result = rdb.query(predicates, columns); if (result == null) showToast("查询数据失败"); else { showToast("查询数据成功(" + result.getRowCount() + ")"); while (result.goToNextRow()) { int id = result.getInt(0); String name = result.getString(1); int age = result.getInt(2); int sex = result.getInt(3); String subject = result.getString(4); HiLog.info(label, "%{public}d, %{public}s, " + "%{public}d, %{public}d, %{public}s", id, name, age, sex, subject); } }
谓词(Predicates)方法 | 描述 |
---|---|
and | 逻辑与 |
or | 逻辑或 |
distinct | 每一记录均为唯一的 |
beginWrap | 左小括号 |
endWrap | 右小括号 |
equalTo | 等于 |
notEqualTo | 不等于 |
greaterThan | 大于 |
greaterThanOrEqualTo | 大于等于 |
lessThan | 小于 |
lessThanOrEqualTo | 小于等于 |
between | 在某范围内 |
notBetween | 在某范围外 |
contains | 包含 |
beginsWith | 匹配字符串开头的子字符串 |
endsWith | 匹配字符串结尾的子字符串 |
in | 在某离散值的范围内 |
notIn | 在某离散值的范围外 |
like | 模糊匹配 |
glob | 文本通配符匹配 |
isNull | 为空 |
isNotNull | 非空 |
crossJoin | 交叉连接 |
innerJoin | 内连接 |
leftOuterJoin | 左外连接 |
using | 简化连接查询 |
on | 连接条件 |
indexedBy | 建立索引 |
groupBy | 分组 |
orderByAsc | 正序 |
orderByDesc | 逆序 |
limit | 限制查询结果数 |
offset | 跳过指定数量的结果数 |
ValuesBucket student = new ValuesBucket(); student.putString("subject", "HarmonyOS"); RdbPredicates predicates = new RdbPredicates( "t_student").equalTo("subject", "Java"); int nrows = rdb.update(student, predicates); if (nrows == -1) showToast("更新数据失败"); else showToast("更新数据成功(" + nrows + ")");
RdbPredicates predicates = new RdbPredicates( "t_student").equalTo("subject", "HarmonyOS"); int nrows = rdb.delete(predicates); if (nrows == -1) showToast("删除数据失败"); else showToast("删除数据成功(" + nrows + ")");
rdb.close();
DatabaseHelper helper = new DatabaseHelper(this); if (helper.deleteRdbStore("tarena.sqlite")) showToast("删数据库成功"); else showToast("删数据库失败");
通过getDatabaseDir()可以获得数据库文件的存储目录。
例程:RdbStorage
...\RdbStorage\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#00a2e8"/> </shape>
...\RdbStorage\entry\src\main\resources\base\graphic\background_toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#80000000"/> <corners ohos:radius="12vp"/> </shape>
...\RdbStorage\entry\src\main\resources\base\layout\toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_content" ohos:width="match_content" ohos:left_padding="20vp" ohos:top_padding="5vp" ohos:right_padding="20vp" ohos:bottom_padding="5vp" ohos:background_element="$graphic:background_toast_dialog"> <Text ohos:id="$+id:txt" ohos:height="match_content" ohos:width="match_content" ohos:text_size="20fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\RdbStorage\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="关系型数据库" ohos:text_size="28fp" ohos:text_color="#00a2e8" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#00a2e8" /> <Button ohos:id="$+id:btnInsert" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="插入数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnBatch" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="批量插入" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnQuery" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="查询数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnUpdate" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="更新数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDelete" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="删除数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDrop" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="删数据库" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\RdbStorage\entry\src\main\java\com\minwei\rdbstorage\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); private RdbStore rdb; @Override public void onStart(Intent intent) { ... findComponentById(ResourceTable.Id_btnInsert) .setClickedListener(component -> onInsert()); findComponentById(ResourceTable.Id_btnBatch) .setClickedListener(component -> onBatch()); findComponentById(ResourceTable.Id_btnQuery) .setClickedListener(component -> onQuery()); findComponentById(ResourceTable.Id_btnUpdate) .setClickedListener(component -> onUpdate()); findComponentById(ResourceTable.Id_btnDelete) .setClickedListener(component -> onDelete()); findComponentById(ResourceTable.Id_btnDrop) .setClickedListener(component -> onDrop()); HiLog.info(label, "数据库存储目录:%{public}s", getDatabaseDir()); } ... private void onInsert() { connectDatabase(); ValuesBucket student = new ValuesBucket(); student.putString("name", "张飞"); student.putInteger("age", 22); student.putInteger("sex", 1); student.putString("subject", "C++"); long id = rdb.insert("t_student", student); if (id == -1) showToast("插入数据失败"); else showToast("插入数据成功(" + id + ")"); disconnectDatabase(); } private void onBatch() { connectDatabase(); List<ValuesBucket> students = new ArrayList<>(); ValuesBucket student1 = new ValuesBucket(); student1.putString("name", "貂蝉"); student1.putInteger("age", 18); student1.putInteger("sex", 0); student1.putString("subject", "Java"); students.add(student1); ValuesBucket student2 = new ValuesBucket(); student2.putString("name", "曹操"); student2.putInteger("age", 30); student2.putInteger("sex", 1); student2.putString("subject", "Java"); students.add(student2); List<Long> ids = rdb.batchInsertOrThrowException( "t_student", students, ConflictResolution.ON_CONFLICT_ABORT); for (long id : ids) if (id == -1) showToast("插入数据失败"); else showToast("插入数据成功(" + id + ")"); disconnectDatabase(); } private void onQuery() { connectDatabase(); RdbPredicates predicates = new RdbPredicates( "t_student").orderByAsc("id"); String[] columns = new String[] { "id", "name", "age", "sex", "subject"}; ResultSet result = rdb.query(predicates, columns); if (result == null) showToast("查询数据失败"); else { showToast("查询数据成功(" + result.getRowCount() + ")"); while (result.goToNextRow()) { int id = result.getInt(0); String name = result.getString(1); int age = result.getInt(2); int sex = result.getInt(3); String subject = result.getString(4); HiLog.info(label, "%{public}d, %{public}s, " + "%{public}d, %{public}d, %{public}s", id, name, age, sex, subject); } } disconnectDatabase(); } private void onUpdate() { connectDatabase(); ValuesBucket student = new ValuesBucket(); student.putString("subject", "HarmonyOS"); RdbPredicates predicates = new RdbPredicates( "t_student").equalTo("subject", "Java"); int nrows = rdb.update(student, predicates); if (nrows == -1) showToast("更新数据失败"); else showToast("更新数据成功(" + nrows + ")"); disconnectDatabase(); } private void onDelete() { connectDatabase(); RdbPredicates predicates = new RdbPredicates( "t_student").equalTo("subject", "HarmonyOS"); int nrows = rdb.delete(predicates); if (nrows == -1) showToast("删除数据失败"); else showToast("删除数据成功(" + nrows + ")"); disconnectDatabase(); } private void onDrop() { DatabaseHelper helper = new DatabaseHelper(this); if (helper.deleteRdbStore("tarena.sqlite")) showToast("删数据库成功"); else showToast("删数据库失败"); } private void connectDatabase() { DatabaseHelper helper = new DatabaseHelper(this); StoreConfig config = StoreConfig .newDefaultConfig("tarena.sqlite"); rdb = helper.getRdbStore(config, 1, new RdbOpenCallback() { @Override public void onCreate(RdbStore rdbStore) { rdbStore.executeSql( "CREATE TABLE IF NOT EXISTS t_student(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT NOT NULL, " + "age INTEGER, " + "sex TINYINT, " + "subject TEXT)"); } @Override public void onUpgrade(RdbStore rdbStore, int i, int i1) { } }); } private void disconnectDatabase() { rdb.close(); } private void showToast(String text) { Component component = LayoutScatter.getInstance(this).parse( ResourceTable.Layout_toast_dialog, null, false); ((Text)component.findComponentById(ResourceTable.Id_txt)) .setText(text); new ToastDialog(this) .setContentCustomComponent(component) .setSize(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT) .setDuration(5000) .setAlignment(LayoutAlignment.BOTTOM) .setOffset(0, AttrHelper.vp2px(60, this)) .show(); } }
运行效果如下图所示:
以面向对象的思想操作关系型数据库。
在...\OrmStorage\entry\build.gradle中添加编译选项:
compileOptions {
annotationEnabled true
}
右上角同步。
添加orm包,添加Student类:
@Entity(tableName = "t_student") public class Student extends OrmObject { @PrimaryKey(autoGenerate = true) private Integer id; private String name; private Integer age; private Integer sex; private String subject; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } }
在orm包中添加TarenaDatabase类:
@Database(entities = {Student.class}, version = 1) public abstract class TarenaDatabase extends OrmDatabase { }
DatabaseHelper helper = new DatabaseHelper(this); orm = helper.getOrmContext("TarenaDB", "tarena.db", TarenaDatabase.class);
Student[] students = new Student[3]; students[0] = new Student(); students[0].setName("张飞"); students[0].setAge(22); students[0].setSex(1); students[0].setSubject("C++"); students[1] = new Student(); students[1].setName("貂蝉"); students[1].setAge(18); students[1].setSex(0); students[1].setSubject("Java"); students[2] = new Student(); students[2].setName("曹操"); students[2].setAge(30); students[2].setSex(1); students[2].setSubject("Java"); for (Student student : students) if (orm.insert(student) && orm.flush()) showToast("插入数据成功"); else showToast("插入数据失败");
OrmPredicates predicates = new OrmPredicates( Student.class).orderByAsc("id"); List<Student> students = orm.query(predicates); if (students == null) showToast("查询数据失败"); else { showToast("查询数据成功(" + students.size() + ")"); for (Student student : students) HiLog.info(label, "%{public}d, %{public}s, " + "%{public}d, %{public}d, %{public}s", student.getId(), student.getName(), student.getAge(), student.getSex(), student.getSubject()); }
OrmPredicates predicates = new OrmPredicates( Student.class).equalTo("subject", "Java"); ValuesBucket student = new ValuesBucket(); student.putString("subject", "HarmonyOS"); int nrows = orm.update(predicates, student); showToast("更新数据成功(" + nrows + ")");
OrmPredicates predicates = new OrmPredicates( Student.class).equalTo("subject", "HarmonyOS"); List<Student> students = orm.query(predicates); for (Student student : students) if (orm.delete(student) && orm.flush()) showToast("删除数据成功"); else showToast("删除数据失败");
orm.close();
通过getDatabaseDir()可以获得数据库文件的存储目录。
例程:OrmStorage
...\OrmStorage\entry\build.gradle
...
ohos {
...
compileOptions {
annotationEnabled true
}
}
...
...\OrmStorage\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#22b14c"/> </shape>
...\OrmStorage\entry\src\main\resources\base\graphic\background_toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#80000000"/> <corners ohos:radius="12vp"/> </shape>
...\OrmStorage\entry\src\main\resources\base\layout\toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_content" ohos:width="match_content" ohos:left_padding="20vp" ohos:top_padding="5vp" ohos:right_padding="20vp" ohos:bottom_padding="5vp" ohos:background_element="$graphic:background_toast_dialog"> <Text ohos:id="$+id:txt" ohos:height="match_content" ohos:width="match_content" ohos:text_size="20fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\OrmStorage\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="对象关系映射" ohos:text_size="28fp" ohos:text_color="#22b14c" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#22b14c" /> <Button ohos:id="$+id:btnInsert" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="插入数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnQuery" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="查询数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnUpdate" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="更新数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDelete" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="删除数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\OrmStorage\entry\src\main\java\com\minwei\ormstorage\orm\Student.java
@Entity(tableName = "t_student") public class Student extends OrmObject { @PrimaryKey(autoGenerate = true) private Integer id; private String name; private Integer age; private Integer sex; private String subject; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } }
...\OrmStorage\entry\src\main\java\com\minwei\ormstorage\orm\TarenaDatabase.java
@Database(entities = {Student.class}, version = 1) public abstract class TarenaDatabase extends OrmDatabase { }
...\OrmStorage\entry\src\main\java\com\minwei\ormstorage\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); private OrmContext orm; @Override public void onStart(Intent intent) { ... findComponentById(ResourceTable.Id_btnInsert) .setClickedListener(component -> onInsert()); findComponentById(ResourceTable.Id_btnQuery) .setClickedListener(component -> onQuery()); findComponentById(ResourceTable.Id_btnUpdate) .setClickedListener(component -> onUpdate()); findComponentById(ResourceTable.Id_btnDelete) .setClickedListener(component -> onDelete()); HiLog.info(label, "数据库存储目录:%{public}s", getDatabaseDir()); } ... private void onInsert() { connectDatabase(); Student[] students = new Student[3]; students[0] = new Student(); students[0].setName("张飞"); students[0].setAge(22); students[0].setSex(1); students[0].setSubject("C++"); students[1] = new Student(); students[1].setName("貂蝉"); students[1].setAge(18); students[1].setSex(0); students[1].setSubject("Java"); students[2] = new Student(); students[2].setName("曹操"); students[2].setAge(30); students[2].setSex(1); students[2].setSubject("Java"); for (Student student : students) if (orm.insert(student) && orm.flush()) showToast("插入数据成功"); else showToast("插入数据失败"); disconnectDatabase(); } private void onQuery() { connectDatabase(); OrmPredicates predicates = new OrmPredicates( Student.class).orderByAsc("id"); List<Student> students = orm.query(predicates); if (students == null) showToast("查询数据失败"); else { showToast("查询数据成功(" + students.size() + ")"); for (Student student : students) HiLog.info(label, "%{public}d, %{public}s, " + "%{public}d, %{public}d, %{public}s", student.getId(), student.getName(), student.getAge(), student.getSex(), student.getSubject()); } disconnectDatabase(); } private void onUpdate() { connectDatabase(); OrmPredicates predicates = new OrmPredicates( Student.class).equalTo("subject", "Java"); ValuesBucket student = new ValuesBucket(); student.putString("subject", "HarmonyOS"); int nrows = orm.update(predicates, student); showToast("更新数据成功(" + nrows + ")"); disconnectDatabase(); } private void onDelete() { connectDatabase(); OrmPredicates predicates = new OrmPredicates( Student.class).equalTo("subject", "HarmonyOS"); List<Student> students = orm.query(predicates); for (Student student : students) if (orm.delete(student) && orm.flush()) showToast("删除数据成功"); else showToast("删除数据失败"); disconnectDatabase(); } private void connectDatabase() { DatabaseHelper helper = new DatabaseHelper(this); orm = helper.getOrmContext("TarenaDB", "tarena.db", TarenaDatabase.class); } private void disconnectDatabase() { orm.close(); } private void showToast(String text) { Component component = LayoutScatter.getInstance(this).parse( ResourceTable.Layout_toast_dialog, null, false); ((Text)component.findComponentById(ResourceTable.Id_txt)) .setText(text); new ToastDialog(this) .setContentCustomComponent(component) .setSize(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT) .setDuration(5000) .setAlignment(LayoutAlignment.BOTTOM) .setOffset(0, AttrHelper.vp2px(60, this)) .show(); } }
运行效果如下图所示:
应用偏好数据库采用键值对(而非二维表)的形式保存整型、浮点型、字符型等常用的数据类型。每个被存储在应用偏好数据库中的数据都需要为其设置一个键,通过键查找值,这就是应用偏好数据库的查询方式。
键值对形式的应用偏好数据库适用于存储应用程序的配置信息等非结构化数据。
DatabaseHelper helper = new DatabaseHelper(this); pre = helper.getPreferences("config");
pre.putBoolean("isAutoUpdate", true) .putInt("version", 1) .putString("currentUser", "minwei").flush();
boolean isAutoUpdate = pre.getBoolean("isAutoUpdate", false); int version = pre.getInt("version", 0); String currentUser = pre.getString("currentUser", "");
pre.delete("isAutoUpdate") .delete("version") .delete("currentUser");
private PreferencesObserver observer = new PreferencesObserver() { @Override public void onChange(Preferences preferences, String s) { if (s.equals("counter")) ((Text)findComponentById(ResourceTable.Id_txt)) .setText(String.valueOf(pre.getInt(s, 0))); } };
pre.registerObserver(observer);
pre.unregisterObserver(observer);
应用偏好数据库其实就是一个XML文件,通过getPreferencesDir()可以获得该文件的存储目录。
例程:PreStorage
...\PreStorage\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#ff7f27"/> </shape>
...\PreStorage\entry\src\main\resources\base\graphic\background_text.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <stroke ohos:width="2vp" ohos:color="#ff7f27"/> </shape>
...\PreStorage\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="偏好数据库" ohos:text_size="28fp" ohos:text_color="#ff7f27" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#ff7f27" /> <Button ohos:id="$+id:btnSave" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="保存数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnQuery" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="查询数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDelete" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="删除数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnObserve" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="观察数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Text ohos:id="$+id:txt" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_text" ohos:text_size="24fp" ohos:text_color="#ff7f27" ohos:text_alignment="horizontal_center" /> </DirectionalLayout>
...\PreStorage\entry\src\main\java\com\minwei\prestorage\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); private Preferences pre; private PreferencesObserver observer = new PreferencesObserver() { @Override public void onChange(Preferences preferences, String s) { if (s.equals("counter")) ((Text)findComponentById(ResourceTable.Id_txt)) .setText(String.valueOf(pre.getInt(s, 0))); } }; @Override public void onStart(Intent intent) { ... findComponentById(ResourceTable.Id_btnSave) .setClickedListener(component -> onSave()); findComponentById(ResourceTable.Id_btnQuery) .setClickedListener(component -> onQuery()); findComponentById(ResourceTable.Id_btnDelete) .setClickedListener(component -> onDelete()); findComponentById(ResourceTable.Id_btnObserve) .setClickedListener(component -> onObserve()); HiLog.info(label, "数据库存储目录:%{public}s", getPreferencesDir().toString()); connectDatabase(); ((Text)findComponentById(ResourceTable.Id_txt)) .setText(String.valueOf(pre.getInt("counter", 0))); pre.registerObserver(observer); } ... @Override protected void onStop() { super.onStop(); connectDatabase(); pre.unregisterObserver(observer); } private void onSave() { connectDatabase(); pre.putBoolean("isAutoUpdate", true) .putInt("version", 1) .putString("currentUser", "minwei").flush(); } private void onQuery() { connectDatabase(); boolean isAutoUpdate = pre.getBoolean("isAutoUpdate", false); int version = pre.getInt("version", 0); String currentUser = pre.getString("currentUser", ""); HiLog.info(label, "[%{public}b][%{public}d][%{public}s]", isAutoUpdate, version, currentUser); } private void onDelete() { connectDatabase(); pre.delete("isAutoUpdate") .delete("version") .delete("currentUser"); } private void onObserve() { connectDatabase(); pre.putInt("counter", pre.getInt("counter", 0) + 1).flush(); } private void connectDatabase() { DatabaseHelper helper = new DatabaseHelper(this); pre = helper.getPreferences("config"); } }
运行效果如下图所示:
分布式数据库属于NoSQL数据库,采用键值对的形式存储数据。因此分布式数据库不仅能象关系型数据库和对象关系映射那样存储结构化数据,也能象应用偏好数据库那样存储非结构化数据。
基于鸿蒙系统提供的分布式数据库API,可以借助分布式软总线,在多个组网设备之间进行无中心、点对点的同步。
人在哪儿数据在哪儿,而不是设备在哪儿数据在哪儿。
能够实现分布式组网的设备必须满足以下三个条件:
华为账号
_____________|_____________
/ | \
手表 <-蓝牙-> 手机 <-WIFI-> 智慧屏
\_____________|_____________/
|
基于分布式软总线多设备组网
为实现分布式数据库同步还需要满足以下两个条件:
KvManagerConfig Options
| |
v v
KvManagerFactory--->KvManager--->KvStore
KvStore派生出两个子类:
_____ _____
| A | | A |
| | | |
| K:1 | New | K:1 |
|_____| |_____|
\_______________/
_____ / SingleKvStore \ _____
| B | | B |
| | | |
| K:2 | Old | K:1 |
|_____| |_____|
-------------------------------
_____ _____
| A | | A |
| | |A_K:1|
| K:1 | |B_K:2|
|_____| |_____|
\_______________/
_____ / DeviceKvStore \ _____
| B | | B |
| | |A_K:1|
| K:2 | |B_K:2|
|_____| |_____|
config.json
"reqPermissions": [ { "name": "ohos.permission.DISTRIBUTED_DATASYNC" } ]
List<String> reqPermissions = new ArrayList<>(); String[] permissions = {"ohos.permission.DISTRIBUTED_DATASYNC"}; for (String permission : permissions) if (verifySelfPermission(permission) != 0 && canRequestPermission(permission)) reqPermissions.add(permission); requestPermissionsFromUser( reqPermissions.toArray(new String[0]), 0);
KvManagerConfig config = new KvManagerConfig(this); KvManager manager = KvManagerFactory .getInstance().createKvManager(config); Options options = new Options() .setCreateIfMissing(true) .setEncrypt(false) .setAutoSync(true) .setBackup(true) .setKvStoreType(KvStoreType.SINGLE_VERSION); skv = manager.getKvStore(options, "config");
try { skv.putBoolean("isAutoUpdate", true); List<Entry> items = new ArrayList<>(); Entry item1 = new Entry("version", Value.get(1)); items.add(item1); Entry item2 = new Entry("currentUser", Value.get("minwei")); items.add(item2); skv.putBatch(items); } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); }
try { boolean isAutoUpdate = skv.getBoolean("isAutoUpdate"); int version = skv.getInt("version"); String currentUser = skv.getString("currentUser"); HiLog.info(label, "[%{public}b][%{public}d][%{public}s]", isAutoUpdate, version, currentUser); } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); }
try { skv.delete("isAutoUpdate"); skv.delete("version"); skv.delete("currentUser"); } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); }
KvManagerConfig config = new KvManagerConfig(this); KvManager manager = KvManagerFactory .getInstance().createKvManager(config); Options options = new Options() .setCreateIfMissing(true) .setEncrypt(false) .setAutoSync(true) .setBackup(true) .setKvStoreType(KvStoreType.SINGLE_VERSION) .setSchema(createStudentSchema()); skv = manager.getKvStore(options, "t_student");
private Schema createStudentSchema() { Schema studentSchema = new Schema(); studentSchema.setSchemaMode(SchemaMode.COMPATIBLE); FieldNode idNode = new FieldNode("id"); idNode.setType(FieldValueType.INTEGER); studentSchema.getRootFieldNode().appendChild(idNode); FieldNode nameNode = new FieldNode("name"); nameNode.setType(FieldValueType.STRING); studentSchema.getRootFieldNode().appendChild(nameNode); FieldNode ageNode = new FieldNode("age"); ageNode.setType(FieldValueType.INTEGER); studentSchema.getRootFieldNode().appendChild(ageNode); FieldNode sexNode = new FieldNode("sex"); sexNode.setType(FieldValueType.INTEGER); studentSchema.getRootFieldNode().appendChild(sexNode); FieldNode subjectNode = new FieldNode("subject"); subjectNode.setType(FieldValueType.STRING); studentSchema.getRootFieldNode().appendChild(subjectNode); return studentSchema; }
try { connectStructuredDatabase(); ZSONObject[] students = new ZSONObject[3]; students[0] = new ZSONObject(); students[0].put("id", 1); students[0].put("name", "张飞"); students[0].put("age", 22); students[0].put("sex", 1); students[0].put("subject", "C++"); students[1] = new ZSONObject(); students[1].put("id", 2); students[1].put("name", "貂蝉"); students[1].put("age", 18); students[1].put("sex", 0); students[1].put("subject", "Java"); students[2] = new ZSONObject(); students[2].put("id", 3); students[2].put("name", "曹操"); students[2].put("age", 30); students[2].put("sex", 1); students[2].put("subject", "Java"); for (int i = 0; i < students.length; ++i) skv.putString(String.valueOf(i), students[i].toString()); } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); }
try { connectStructuredDatabase(); Query query = Query.select().orderByAsc("$.id"); List<Entry> entries = skv.getEntries(query); for (Entry entry : entries) { String key = entry.getKey(); ZSONObject student = ZSONObject.stringToZSON( entry.getValue().getString()); HiLog.info(label, "%{public}s -> %{public}d, " + "%{public}s, %{public}d, %{public}d, %{public}s", key, student.getInteger("id"), student.getString("name"), student.getInteger("age"), student.getInteger("sex"), student.getString("subject")); } } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); }
try { connectStructuredDatabase(); Query query = Query.select().orderByAsc("$.id"); List<Entry> entries = skv.getEntries(query); for (Entry entry : entries) { String key = entry.getKey(); ZSONObject student = ZSONObject.stringToZSON( entry.getValue().getString()); if (student.getString("subject").equals("Java")) { student.put("subject", "HarmonyOS"); skv.putString(key, student.toString()); } } } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); }
try { connectStructuredDatabase(); Query query = Query.select().orderByAsc("$.id"); List<Entry> entries = skv.getEntries(query); for (Entry entry : entries) { String key = entry.getKey(); ZSONObject student = ZSONObject.stringToZSON( entry.getValue().getString()); if (student.getString("subject").equals("HarmonyOS")) skv.delete(key); } } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); }
例程:DisStorage
...\DisStorage\entry\src\main\config.json
{ ... "module": { ... "reqPermissions": [ { "name": "ohos.permission.DISTRIBUTED_DATASYNC" } ] } }
...\DisStorage\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#ee1c24"/> </shape>
...\DisStorage\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="分布式数据库" ohos:text_size="28fp" ohos:text_color="#ee1c24" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#ee1c24" /> <Button ohos:id="$+id:btnSet" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="设置键值" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnGet" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="获取键值" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDel" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="删除键值" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Text ohos:height="1vp" ohos:width="match_parent" ohos:top_margin="15vp" ohos:bottom_margin="5vp" ohos:background_element="#ee1c24" /> <Button ohos:id="$+id:btnInsert" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="插入数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnQuery" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="查询数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnUpdate" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="更新数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDelete" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="删除数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\DisStorage\entry\src\main\java\com\minwei\disstorage\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); private SingleKvStore skv; @Override public void onStart(Intent intent) { ... requestPermissions(); findComponentById(ResourceTable.Id_btnSet) .setClickedListener(component -> onSet()); findComponentById(ResourceTable.Id_btnGet) .setClickedListener(component -> onGet()); findComponentById(ResourceTable.Id_btnDel) .setClickedListener(component -> onDel()); findComponentById(ResourceTable.Id_btnInsert) .setClickedListener(component -> onInsert()); findComponentById(ResourceTable.Id_btnQuery) .setClickedListener(component -> onQuery()); findComponentById(ResourceTable.Id_btnUpdate) .setClickedListener(component -> onUpdate()); findComponentById(ResourceTable.Id_btnDelete) .setClickedListener(component -> onDelete()); } ... private void onSet() { try { connectUnstructuredDatabase(); skv.putBoolean("isAutoUpdate", true); List<Entry> entries = new ArrayList<>(); Entry entry1 = new Entry("version", Value.get(1)); entries.add(entry1); Entry entry2 = new Entry("currentUser", Value.get("minwei")); entries.add(entry2); skv.putBatch(entries); } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); } } private void onGet() { try { connectUnstructuredDatabase(); boolean isAutoUpdate = skv.getBoolean("isAutoUpdate"); int version = skv.getInt("version"); String currentUser = skv.getString("currentUser"); HiLog.info(label, "[%{public}b][%{public}d][%{public}s]", isAutoUpdate, version, currentUser); } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); } } private void onDel() { try { connectUnstructuredDatabase(); skv.delete("isAutoUpdate"); skv.delete("version"); skv.delete("currentUser"); } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); } } private void onInsert() { try { connectStructuredDatabase(); ZSONObject[] students = new ZSONObject[3]; students[0] = new ZSONObject(); students[0].put("id", 1); students[0].put("name", "张飞"); students[0].put("age", 22); students[0].put("sex", 1); students[0].put("subject", "C++"); students[1] = new ZSONObject(); students[1].put("id", 2); students[1].put("name", "貂蝉"); students[1].put("age", 18); students[1].put("sex", 0); students[1].put("subject", "Java"); students[2] = new ZSONObject(); students[2].put("id", 3); students[2].put("name", "曹操"); students[2].put("age", 30); students[2].put("sex", 1); students[2].put("subject", "Java"); for (int i = 0; i < students.length; ++i) skv.putString(String.valueOf(i), students[i].toString()); } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); } } private void onQuery() { try { connectStructuredDatabase(); Query query = Query.select().orderByAsc("$.id"); List<Entry> entries = skv.getEntries(query); for (Entry entry : entries) { String key = entry.getKey(); ZSONObject student = ZSONObject.stringToZSON( entry.getValue().getString()); HiLog.info(label, "%{public}s -> %{public}d, " + "%{public}s, %{public}d, %{public}d, %{public}s", key, student.getInteger("id"), student.getString("name"), student.getInteger("age"), student.getInteger("sex"), student.getString("subject")); } } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); } } private void onUpdate() { try { connectStructuredDatabase(); Query query = Query.select().orderByAsc("$.id"); List<Entry> entries = skv.getEntries(query); for (Entry entry : entries) { String key = entry.getKey(); ZSONObject student = ZSONObject.stringToZSON( entry.getValue().getString()); if (student.getString("subject").equals("Java")) { student.put("subject", "HarmonyOS"); skv.putString(key, student.toString()); } } } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); } } private void onDelete() { try { connectStructuredDatabase(); Query query = Query.select().orderByAsc("$.id"); List<Entry> entries = skv.getEntries(query); for (Entry entry : entries) { String key = entry.getKey(); ZSONObject student = ZSONObject.stringToZSON( entry.getValue().getString()); if (student.getString("subject").equals("HarmonyOS")) skv.delete(key); } } catch (KvStoreException exception) { HiLog.info(label, exception.toString()); } } private void requestPermissions() { List<String> reqPermissions = new ArrayList<>(); String[] permissions = {"ohos.permission.DISTRIBUTED_DATASYNC"}; for (String permission : permissions) if (verifySelfPermission(permission) != 0 && canRequestPermission(permission)) reqPermissions.add(permission); requestPermissionsFromUser( reqPermissions.toArray(new String[0]), 0); } private void connectUnstructuredDatabase() { KvManagerConfig config = new KvManagerConfig(this); KvManager manager = KvManagerFactory .getInstance().createKvManager(config); Options options = new Options() .setCreateIfMissing(true) .setEncrypt(false) .setAutoSync(true) .setBackup(true) .setKvStoreType(KvStoreType.SINGLE_VERSION); skv = manager.getKvStore(options, "config"); } private void connectStructuredDatabase() { KvManagerConfig config = new KvManagerConfig(this); KvManager manager = KvManagerFactory .getInstance().createKvManager(config); Options options = new Options() .setCreateIfMissing(true) .setEncrypt(false) .setAutoSync(true) .setBackup(true) .setKvStoreType(KvStoreType.SINGLE_VERSION) .setSchema(createStudentSchema()); skv = manager.getKvStore(options, "t_student"); } private Schema createStudentSchema() { Schema studentSchema = new Schema(); studentSchema.setSchemaMode(SchemaMode.COMPATIBLE); FieldNode idNode = new FieldNode("id"); idNode.setType(FieldValueType.INTEGER); studentSchema.getRootFieldNode().appendChild(idNode); FieldNode nameNode = new FieldNode("name"); nameNode.setType(FieldValueType.STRING); studentSchema.getRootFieldNode().appendChild(nameNode); FieldNode ageNode = new FieldNode("age"); ageNode.setType(FieldValueType.INTEGER); studentSchema.getRootFieldNode().appendChild(ageNode); FieldNode sexNode = new FieldNode("sex"); sexNode.setType(FieldValueType.INTEGER); studentSchema.getRootFieldNode().appendChild(sexNode); FieldNode subjectNode = new FieldNode("subject"); subjectNode.setType(FieldValueType.STRING); studentSchema.getRootFieldNode().appendChild(subjectNode); return studentSchema; } }
类似图片、音频、视频、文档等数据量较大的二进制信息,不便于通过数据库实现持久化,一般直接以文件形式保存在文件系统中,分为以下两种形式:
在鸿蒙设备中用于应用程序存储文件的位置有两个:
/data/user/0/<bundle_name>/ - 沙盒目录,getDataDir()
|
|_ cache/ - 缓存目录,getCacheDir()
|_ code_cache/ - 代码缓存,getCodeCacheDir()
|_ files/ - 文件目录,getFilesDir()
/storage/emulated/0/ \
或 | - 外部存储
/storage/emulated/sdcard/ /
|
|_ Android/data/<bundle_name>/ - 私有目录,用户能访问,其它应用不能访问
| |
| |_ cache/ - 缓存目录,getExternalCacheDir()
| |_ <name1>/ - 自建目录,getExternalFilesDir(<name1>)
| |_ <name2>/ - 自建目录,getExternalFilesDir(<name2>)
| |_ ... - ...
|
|_ ... - 公有区域,用户和其它应用都能访问
应用程序访问沙盒目录和外部存储私有目录中的文件,不需要任何特殊权限,直接使用Java语言提供的文件操作接口即可。但如果需要访问外部存储公有区域中的文件,则必须借助于DataAbility。
沙盒和外存中的缓存目录一般用于存放临时文件。需要持久化保存的文件通常放在files或自建目录下。
包含账号、密码、聊天记录等私密信息的文件最好放在沙盒目录中,而象电子地图、照片等文件,为了便于用户增删备份,存放在外部存储中更为合理。
try { FileWriter fw = new FileWriter( getExternalFilesDir("docs") + "/note.txt"); BufferedWriter bw = new BufferedWriter(fw); bw.write("文件中的数据"); bw.newLine(); bw.close(); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); }
try { FileReader fr = new FileReader( getExternalFilesDir("docs") + "/note.txt"); BufferedReader br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) HiLog.info(label, line); br.close(); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); }
例程:LocFile
...\LocFile\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#254061"/> </shape>
...\LocFile\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="本地文件" ohos:text_size="28fp" ohos:text_color="#254061" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#254061" /> <Button ohos:id="$+id:btnDir" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="本地目录" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnWrite" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="写入数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnRead" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="读取数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\LocFile\entry\src\main\java\com\minwei\locfile\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); @Override public void onStart(Intent intent) { ... findComponentById(ResourceTable.Id_btnDir) .setClickedListener(component -> onDir()); findComponentById(ResourceTable.Id_btnWrite) .setClickedListener(component -> onWrite()); findComponentById(ResourceTable.Id_btnRead) .setClickedListener(component -> onRead()); } ... private void onDir() { HiLog.info(label, "沙盒目录:" + getDataDir().toString()); HiLog.info(label, "沙盒缓存:" + getCacheDir().toString()); HiLog.info(label, "代码缓存:" + getCodeCacheDir().toString()); HiLog.info(label, "沙盒文件:" + getFilesDir().toString()); HiLog.info(label, "外存缓存:" + getExternalCacheDir().toString()); HiLog.info(label, "外存自建:" + getExternalFilesDir("docs").toString()); } private void onWrite() { try { FileWriter fw = new FileWriter( getExternalFilesDir("docs") + "/note.txt"); BufferedWriter bw = new BufferedWriter(fw); bw.write("文件中的数据"); bw.newLine(); bw.close(); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); } } private void onRead() { try { FileReader fr = new FileReader( getExternalFilesDir("docs") + "/note.txt"); BufferedReader br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) HiLog.info(label, line); br.close(); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); } } }
运行效果如下图所示:
分布式文件系统可以将文件在设备之间共享。与分布式数据库类似,分布式文件系统的设计目的就是能够让文件在不同设备间进行共享。让文件跟着人走,而不是跟着设备走。在分布式文件系统中实现文件共享需要满足以下条件:
不过与分布式数据库不同,分布式文件系统中的文件并不会在设备间同步,而仅仅是提供了共享访问接口(文件的元数据在设备间同步)。在多个设备间,同一个文件仅存在一份副本。
config.json
"reqPermissions": [ { "name": "ohos.permission.DISTRIBUTED_DATASYNC" } ]
List<String> reqPermissions = new ArrayList<>(); String[] permissions = {"ohos.permission.DISTRIBUTED_DATASYNC"}; for (String permission : permissions) if (verifySelfPermission(permission) != 0 && canRequestPermission(permission)) reqPermissions.add(permission); requestPermissionsFromUser( reqPermissions.toArray(new String[0]), 0);
getDistributedDir()
/mnt/mdfs/16806570328871500801/merge_view/data/com.minwei.disfile/MainAbility
try { FileWriter fw = new FileWriter( getDistributedDir() + "/note.txt"); BufferedWriter bw = new BufferedWriter(fw); bw.write("文件中的数据"); bw.newLine(); bw.close(); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); }
try { FileReader fr = new FileReader( getDistributedDir() + "/note.txt"); BufferedReader br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) HiLog.info(label, line); br.close(); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); }
使用分布式文件存储需要注意以下几点:
例程:DisFile
...\DisFile\entry\src\main\config.json
{ ... "module": { ... "reqPermissions": [ { "name": "ohos.permission.DISTRIBUTED_DATASYNC" } ] } }
...\DisFile\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#385723"/> </shape>
...\DisFile\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="分布式文件" ohos:text_size="28fp" ohos:text_color="#385723" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#385723" /> <Button ohos:id="$+id:btnDir" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="分布式目录" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnWrite" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="写入数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnRead" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="读取数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\DisFile\entry\src\main\java\com\minwei\disfile\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); @Override public void onStart(Intent intent) { ... requestPermissions(); findComponentById(ResourceTable.Id_btnDir) .setClickedListener(component -> onDir()); findComponentById(ResourceTable.Id_btnWrite) .setClickedListener(component -> onWrite()); findComponentById(ResourceTable.Id_btnRead) .setClickedListener(component -> onRead()); } ... private void onDir() { HiLog.info(label, "分布式目录:" + getDistributedDir().toString()); } private void onWrite() { try { FileWriter fw = new FileWriter( getDistributedDir() + "/note.txt"); BufferedWriter bw = new BufferedWriter(fw); bw.write("文件中的数据"); bw.newLine(); bw.close(); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); } } private void onRead() { try { FileReader fr = new FileReader( getDistributedDir() + "/note.txt"); BufferedReader br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) HiLog.info(label, line); br.close(); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); } } private void requestPermissions() { List<String> reqPermissions = new ArrayList<>(); String[] permissions = {"ohos.permission.DISTRIBUTED_DATASYNC"}; for (String permission : permissions) if (verifySelfPermission(permission) != 0 && canRequestPermission(permission)) reqPermissions.add(permission); requestPermissionsFromUser( reqPermissions.toArray(new String[0]), 0); } }
运行效果如下图所示:
DataAbility提供了对文件和数据库的统一访问接口。借助DataAbility,一个应用产生的数据既可以被自己访问,也能够被其它应用访问。允许其它应用访问自己的数据通常被称为“跨应用的数据访问”,这正是开发DataAbility的主要目的。
与分布式数据库和分布式文件系统一样,DataAbility也支持在分布式组网内的不同设备间同步和共享数据,但跨应用的数据访问显然是DataAbility的专有特征。
在config.json文件中,DataAbility的type属性被定义为data。另外两种Ability包括PageAbility和ServiceAbility,其type属性分别被定义为page和service。
与其它类型的Ability一样,DataAbility也是Ability基类的子类,但其生命周期方法中通常只会用到onStart()和onStop(),分别用于连接(打开)和断开(关闭)数据库(文件)。
作为数据需求者的应用,通常会用到如下方法:
openFile(Uri uri, String mode)
根据URI和访问模式打开文件,得到相应的文件描述符,用于后续操作
getFileTypes(Uri uri, String mimeTypeFilter)
根据URI和类型过滤器获取文件类型
insert(Uri uri, ValuesBucket value)
向数据库中插入记录
query(Uri uri, String[] columns, DataAbilityPredicates predicates)
查询数据库中的数据
update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates)
更新数据库中的数据
delete(Uri uri, DataAbilityPredicates predicates)
删除数据库中的数据
DataAbilityHelper是DataAbility的远程调用工具,其中的方法与后者一一对应。
应用A -> DataAbilityHelper DataAbility -> 应用B
|_openFile -> |_openFile
|_getFileTypes -> |_getFileTypes
|_insert -> |_insert
|_query -> |_query
|_update -> |_update
|_delete -> |_delete
|_... -> |_...
dataability://<设备ID>/<路径>[?<参数>][#<片段标识符>]
同一设备不同应用间的数据访问,“<设备ID>”可以为空,但其后的“/”不能省略:
dataability:///<路径>[?<参数>][#<片段标识符>]
添加DataAbility类的子类,其在config.json中的配置如下:
{ "name": "com.minwei.dataprovider.StudentDataAbility", "icon": "$media:icon", "description": "$string:studentdataability_description", "type": "data", "uri": "dataability://com.minwei.dataprovider.StudentDataAbility", "permissions": [ "com.minwei.dataprovider.DataAbilityShellProvider.PROVIDER" ], "visible": true }
该类的实现:
public class StudentDataAbility extends Ability { private RdbStore rdb; public void onStart(Intent intent) { DatabaseHelper helper = new DatabaseHelper(this); StoreConfig config = StoreConfig .newDefaultConfig("tarena.sqlite"); rdb = helper.getRdbStore(config, 1, new RdbOpenCallback() { public void onCreate(RdbStore rdbStore) { rdbStore.executeSql( "CREATE TABLE IF NOT EXISTS t_student(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT NOT NULL, " + "age INTEGER, " + "sex TINYINT, " + "subject TEXT)"); } ... }); } protected void onStop() { rdb.close(); } public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) { return rdb.query(DataAbilityUtils.createRdbPredicates( predicates, "t_student"), columns); } public int insert(Uri uri, ValuesBucket value) { return (int)rdb.insert("t_student", value); } public int delete(Uri uri, DataAbilityPredicates predicates) { return rdb.delete(DataAbilityUtils.createRdbPredicates( predicates, "t_student")); } public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) { return rdb.update(value, DataAbilityUtils.createRdbPredicates( predicates, "t_student")); } ... }
例程:DataProvider
...\DataProvider\entry\src\main\config.json
{ ... "module": { ... "abilities": [ ... { "name": "com.minwei.dataprovider.StudentDataAbility", "icon": "$media:icon", "description": "$string:studentdataability_description", "type": "data", "uri": "dataability://com.minwei.dataprovider.StudentDataAbility", "permissions": [ "com.minwei.dataprovider.DataAbilityShellProvider.PROVIDER" ], "visible": true } ], "defPermissions": [ { "name": "com.minwei.dataprovider.DataAbilityShellProvider.PROVIDER" } ] } }
...\DataProvider\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical" ohos:background_element="#804080"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="数据提供者" ohos:text_size="36fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\DataProvider\entry\src\main\java\com\minwei\dataprovider\StudentDataAbility.java
public class StudentDataAbility extends Ability { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, StudentDataAbility.class.getCanonicalName()); private RdbStore rdb; @Override public void onStart(Intent intent) { ... DatabaseHelper helper = new DatabaseHelper(this); StoreConfig config = StoreConfig .newDefaultConfig("tarena.sqlite"); rdb = helper.getRdbStore(config, 1, new RdbOpenCallback() { @Override public void onCreate(RdbStore rdbStore) { rdbStore.executeSql( "CREATE TABLE IF NOT EXISTS t_student(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT NOT NULL, " + "age INTEGER, " + "sex TINYINT, " + "subject TEXT)"); } @Override public void onUpgrade(RdbStore rdbStore, int i, int i1) { } }); } @Override protected void onStop() { ... rdb.close(); } @Override public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) { return rdb.query(DataAbilityUtils.createRdbPredicates( predicates, "t_student"), columns); } @Override public int insert(Uri uri, ValuesBucket value) { ... return (int)rdb.insert("t_student", value); } @Override public int delete(Uri uri, DataAbilityPredicates predicates) { return rdb.delete(DataAbilityUtils.createRdbPredicates( predicates, "t_student")); } @Override public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) { return rdb.update(value, DataAbilityUtils.createRdbPredicates( predicates, "t_student")); } ... }
在config.json中添加权限:
"defPermissions": [ { "name": "com.minwei.dataprovider.DataAbilityShellProvider.PROVIDER", "grantMode": "system_grant" } ], "reqPermissions": [ { "name": "com.minwei.dataprovider.DataAbilityShellProvider.PROVIDER" } ]
通过URI指定目标DataAbility,借助DataAbilityHelper访问其它应用的数据库:
public class MainAbilitySlice extends AbilitySlice { private DataAbilityHelper helper; private final Uri uri = Uri.parse( "dataability:///com.minwei.dataprovider.StudentDataAbility"); public void onStart(Intent intent) { helper = DataAbilityHelper.creator(this); ... } private void onInsert() { ... long id = helper.insert(uri, student); ... } private void onQuery() { ... ResultSet result = helper.query(uri, columns, predicates); ... } private void onUpdate() { ... int nrows = helper.update(uri, student, predicates); ... } private void onDelete() { ... int nrows = helper.delete(uri, predicates); ... } ... }
例程:DataDemander
...\DataDemander\entry\src\main\config.json
{ ... "module": { ... "defPermissions": [ { "name": "com.minwei.dataprovider.DataAbilityShellProvider.PROVIDER", "grantMode": "system_grant" } ], "reqPermissions": [ { "name": "com.minwei.dataprovider.DataAbilityShellProvider.PROVIDER" } ] } }
...\DataDemander\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#804080"/> </shape>
...\DataDemander\entry\src\main\resources\base\graphic\background_toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#80000000"/> <corners ohos:radius="12vp"/> </shape>
...\DataDemander\entry\src\main\resources\base\layout\toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_content" ohos:width="match_content" ohos:left_padding="20vp" ohos:top_padding="5vp" ohos:right_padding="20vp" ohos:bottom_padding="5vp" ohos:background_element="$graphic:background_toast_dialog"> <Text ohos:id="$+id:txt" ohos:height="match_content" ohos:width="match_content" ohos:text_size="20fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\DataDemander\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="数据需求者" ohos:text_size="28fp" ohos:text_color="#804080" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#804080" /> <Button ohos:id="$+id:btnInsert" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="插入数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnQuery" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="查询数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnUpdate" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="更新数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDelete" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="删除数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\DataDemander\entry\src\main\java\com\minwei\datademander\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); private DataAbilityHelper helper; private final Uri uri = Uri.parse( "dataability:///com.minwei.dataprovider.StudentDataAbility"); @Override public void onStart(Intent intent) { ... helper = DataAbilityHelper.creator(this); findComponentById(ResourceTable.Id_btnInsert) .setClickedListener(component -> onInsert()); findComponentById(ResourceTable.Id_btnQuery) .setClickedListener(component -> onQuery()); findComponentById(ResourceTable.Id_btnUpdate) .setClickedListener(component -> onUpdate()); findComponentById(ResourceTable.Id_btnDelete) .setClickedListener(component -> onDelete()); } ... private void onInsert() { ValuesBucket[] students = new ValuesBucket[3]; students[0] = new ValuesBucket(); students[0].putString("name", "张飞"); students[0].putInteger("age", 22); students[0].putInteger("sex", 1); students[0].putString("subject", "C++"); students[1] = new ValuesBucket(); students[1].putString("name", "貂蝉"); students[1].putInteger("age", 18); students[1].putInteger("sex", 0); students[1].putString("subject", "Java"); students[2] = new ValuesBucket(); students[2].putString("name", "曹操"); students[2].putInteger("age", 30); students[2].putInteger("sex", 1); students[2].putString("subject", "Java"); for (ValuesBucket student : students) try { long id = helper.insert(uri, student); if (id == -1) showToast("插入数据失败"); else showToast("插入数据成功(" + id + ")"); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } } private void onQuery() { String[] columns = new String[] { "id", "name", "age", "sex", "subject"}; DataAbilityPredicates predicates = new DataAbilityPredicates().orderByAsc("id"); try { ResultSet result = helper.query(uri, columns, predicates); showToast("查询数据成功(" + result.getRowCount() + ")"); while (result.goToNextRow()) { int id = result.getInt(0); String name = result.getString(1); int age = result.getInt(2); int sex = result.getInt(3); String subject = result.getString(4); HiLog.info(label, "%{public}d, %{public}s, " + "%{public}d, %{public}d, %{public}s", id, name, age, sex, subject); } result.close(); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } } private void onUpdate() { ValuesBucket student = new ValuesBucket(); student.putString("subject", "HarmonyOS"); DataAbilityPredicates predicates = new DataAbilityPredicates().equalTo("subject", "Java"); try { int nrows = helper.update(uri, student, predicates); if (nrows == -1) showToast("更新数据失败"); else showToast("更新数据成功(" + nrows + ")"); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } } private void onDelete() { DataAbilityPredicates predicates = new DataAbilityPredicates().equalTo("subject", "HarmonyOS"); try { int nrows = helper.delete(uri, predicates); if (nrows == -1) showToast("删除数据失败"); else showToast("删除数据成功(" + nrows + ")"); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } } private void showToast(String text) { Component component = LayoutScatter.getInstance(this).parse( ResourceTable.Layout_toast_dialog, null, false); ((Text)component.findComponentById(ResourceTable.Id_txt)) .setText(text); new ToastDialog(this) .setContentCustomComponent(component) .setSize(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT) .setDuration(5000) .setAlignment(LayoutAlignment.BOTTOM) .setOffset(0, AttrHelper.vp2px(60, this)) .show(); } }
运行效果如下图所示:
添加DataAbility类的子类,其在config.json中的配置如下:
{ "name": "com.minwei.fileprovider.FileDataAbility", "icon": "$media:icon", "description": "$string:filedataability_description", "type": "data", "uri": "dataability://com.minwei.fileprovider.FileDataAbility", "permissions": [ "com.minwei.fileprovider.DataAbilityShellProvider.PROVIDER" ], "visible": true }
该类的实现:
public class FileDataAbility extends Ability { ... public FileDescriptor openFile(Uri uri, String mode) { String filename = uri.getDecodedPathList().get(1); String pathname = getDataDir() + File.separator + filename; File file = new File(pathname); try { if (mode.equals("w")) return MessageParcel.dupFileDescriptor( new FileOutputStream(file).getFD()); else if (mode.equals("r")) return MessageParcel.dupFileDescriptor( new FileInputStream(file).getFD()); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); } return null; } ... }
例程:FileProvider
...\FileProvider\entry\src\main\config.json
{ ... "module": { ... "abilities": [ ... { "name": "com.minwei.fileprovider.FileDataAbility", "icon": "$media:icon", "description": "$string:filedataability_description", "type": "data", "uri": "dataability://com.minwei.fileprovider.FileDataAbility", "permissions": [ "com.minwei.fileprovider.DataAbilityShellProvider.PROVIDER" ], "visible": true } ], "defPermissions": [ { "name": "com.minwei.fileprovider.DataAbilityShellProvider.PROVIDER" } ] } }
...\FileProvider\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical" ohos:background_element="#804000"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="文件提供者" ohos:text_size="36fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\FileProvider\entry\src\main\java\com\minwei\fileprovider\FileDataAbility.java
public class FileDataAbility extends Ability { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, FileDataAbility.class.getCanonicalName()); ... @Override public FileDescriptor openFile(Uri uri, String mode) { String filename = uri.getDecodedPathList().get(1); String pathname = getDataDir() + File.separator + filename; File file = new File(pathname); try { if (mode.equals("w")) return MessageParcel.dupFileDescriptor( new FileOutputStream(file).getFD()); else if (mode.equals("r")) return MessageParcel.dupFileDescriptor( new FileInputStream(file).getFD()); } catch (IOException exception) { HiLog.info(label, exception.getLocalizedMessage()); } return null; } ... }
在config.json中添加权限:
"defPermissions": [ { "name": "com.minwei.fileprovider.DataAbilityShellProvider.PROVIDER", "grantMode": "system_grant" } ], "reqPermissions": [ { "name": "com.minwei.fileprovider.DataAbilityShellProvider.PROVIDER" }, { "name": "ohos.permission.WRITE_USER_STORAGE" }, { "name": "ohos.permission.READ_USER_STORAGE" } ]
通过URI指定目标DataAbility,借助DataAbilityHelper访问其它应用的文件:
public class MainAbilitySlice extends AbilitySlice { private DataAbilityHelper helper; private final Uri uri = Uri.parse( "dataability:///com.minwei.fileprovider.FileDataAbility/note.txt"); public void onStart(Intent intent) { helper = DataAbilityHelper.creator(this); ... } private void onWrite() { ... FileDescriptor fd = helper.openFile(uri, "w"); ... } private void onRead() { ... FileDescriptor fd = helper.openFile(uri, "r"); ... } ... }
例程:FileDemander
...\FileDemander\entry\src\main\config.json
{ ... "module": { ... "defPermissions": [ { "name": "com.minwei.fileprovider.DataAbilityShellProvider.PROVIDER", "grantMode": "system_grant" } ], "reqPermissions": [ { "name": "com.minwei.fileprovider.DataAbilityShellProvider.PROVIDER" }, { "name": "ohos.permission.WRITE_USER_STORAGE" }, { "name": "ohos.permission.READ_USER_STORAGE" } ] } }
...\FileDemander\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#804000"/> </shape>
...\FileDemander\entry\src\main\resources\base\layout\ability_main.xml
<?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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="文件需求者" ohos:text_size="28fp" ohos:text_color="#804000" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#804000" /> <Button ohos:id="$+id:btnWrite" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="写入数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnRead" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="读取数据" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\FileDemander\entry\src\main\java\com\minwei\filedemander\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); private DataAbilityHelper helper; private final Uri uri = Uri.parse( "dataability:///com.minwei.fileprovider.FileDataAbility/note.txt"); @Override public void onStart(Intent intent) { ... helper = DataAbilityHelper.creator(this); findComponentById(ResourceTable.Id_btnWrite) .setClickedListener(component -> onWrite()); findComponentById(ResourceTable.Id_btnRead) .setClickedListener(component -> onRead()); } ... private void onWrite() { try { FileDescriptor fd = helper.openFile(uri, "w"); FileWriter fw = new FileWriter(fd); BufferedWriter bw = new BufferedWriter(fw); bw.write("文件中的数据"); bw.newLine(); bw.close(); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } } private void onRead() { try { FileDescriptor fd = helper.openFile(uri, "r"); FileReader fr = new FileReader(fd); BufferedReader br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) HiLog.info(label, line); br.close(); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } } }
运行效果如下图所示: