鸿蒙Ability(三):Particle Ability的Data Ability模板

导语

Ability可以分为FA(Feature Ability)和PA(Particle Ability)两种类型。
①Feature Ability
FA代表有界面的Ability,支持Page Ability模板(是FA唯一支持的模板),用于提供与用户交互的能力。
②Particle Ability
PA代表无界面的Ability,支持Service Ability和Data Ability模板。

Data Ability

Data Ability是基于Data模板的Ability(下文简称Data),主要用于对外部提供统一的数据访问抽象
Data有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。Data对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供。
使用Data模板的Ability形式仍然是Ability,因此,开发者需要为应用添加一个或多个Ability的子类,来提供程序与其他应用之间的接口。
Data为结构化数据和文件提供了不同API接口供用户使用,因此,开发者需要首先确定好使用何种类型的数据。
确定数据的存储方式,Data支持以下两种数据形式:
①文件数据:如文本、图片、音乐等。
②结构化数据:如数据库等。
HarmonyOS提供多种数据管理能力,例如:关系型数据库、对象关系映射数据库、轻量级偏好数据库、分布式数据服务等。
本文以对象关系映射数据库为例。
这个连接是以关系型数据库为例:https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/HarmonyOS-Relational-Database

Data的创建

UserDataAbility用于接收其他应用发送的请求,提供外部程序访问的入口,从而实现应用间的数据访问。
实现UserDataAbility,需要在“Project”窗口当前工程的主目录(“entry > src > main > java > com.xxx.xxx”)选择“File > New > Ability > Empty Data Ability”,设置“Data Name”后完成UserDataAbility的创建。
Data提供了文件存储数据库存储两组接口供用户使用。
在这里插入图片描述
输入名字(我取名为UserDataAbility,这里使用DataAbility做示范)
在这里插入图片描述
这样就创建好了呀~(我取名为UserDataAbility,这里使用DataAbility做示范)
在这里插入图片描述
可以看见UserDataAbility用于接收其他应用发送的请求,提供外部程序访问的入口,从而实现应用间的数据访问。
Data提供了文件存储和数据库存储两组接口供用户使用。
(我取名为UserDataAbility,这里使用DataAbility做示范)

public class DataAbility extends Ability {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        HiLog.info(LABEL_LOG, "DataAbility onStart");
    }

    //DataAbilityHelper 为开发者提供了一系列的接口来访问不同类型的数据(文件、数据库等)。

    //查
    @Override
    public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
        return null;
    }

    //增
    @Override
    public int insert(Uri uri, ValuesBucket value) {
        HiLog.info(LABEL_LOG, "DataAbility insert");
        return 999;
    }

    //删
    @Override
    public int delete(Uri uri, DataAbilityPredicates predicates) {
        return 0;
    }

    //改
    @Override
    public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
        return 0;
    }

    //打开文件(访问文件)
    @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;
    }

    //获取URI类型
    @Override
    public String getType(Uri uri) {
        return null;
    }
}

然后打开配置文件
在这里插入图片描述
可以看到系统自动生成了Data的配置代码,"type": "data"表示了这是一个Data Ability,"permissions"是自带的一个提供给外部数据的权限,也已经访问这个TestDataAbility的"uri"

URI

Data的提供方和使用方都通过URI(Uniform Resource Identifier)来标识一个具体的数据(例如数据库中的某个表或磁盘上的某个文件)。HarmonyOS的URI仍基于URI通用标准,格式如下:
在这里插入图片描述
scheme:协议方案名,固定为“dataability”,代表Data Ability所使用的协议类型。
authority:设备ID。如果为跨设备场景,则为目标设备的ID;如果为本地设备场景,则不需要填写。
path:资源的路径信息,代表特定资源的位置信息。
query:查询参数。
fragment:可以用于指示要访问的子资源。
URI示例:(官网抄的)
跨设备场景:dataability://device_id/com.domainname.dataability.persondata/person/10
本地设备:dataability:///com.domainname.dataability.persondata/person/10
注意:如需开放对其他程序可见,和Service一样,需要设置visible值的设置为true)

Data的实现

Data提供了文件存储和数据库存储两组接口供用户使用。

文件存储

开发者需要在Data中重写FileDescriptor openFile​(Uri uri, String mode)方法来操作文件

/**
     * 文件存储
     *
     * @param uri  客户端传入的请求目标路径
     * @param mode 开发者对文件的操作选项,可选方式包含“r”(读), “w”(写), “rw”(读写)等
     * @return
     * @throws FileNotFoundException
     */
    @Override
    public FileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
        //ohos.rpc.MessageParcel类提供了一个静态方法,用于获取MessageParcel实例。
        //开发者可通过获取到的MessageParcel实例,
        //使用dupFileDescriptor()函数复制待操作文件流的文件描述符,并将其返回,供远端应用访问文件。
        //创建messageParcel
        MessageParcel messageParcel = MessageParcel.obtain();
        //获取文件
        File file = new File(uri.getDecodedPathList().get(0));//get(0)是获取URI完整字段中查询参数字段。
        if (mode == null || !"rw".equals(mode)) {
            file.setReadOnly();
        }
        //通过文件获取文件输入流
        FileInputStream fileIs = new FileInputStream(file);
        FileDescriptor fd = null;
        try {
            //根据文件输入流获取文件描述符
            fd = fileIs.getFD();
        } catch (IOException e) {
            e.printStackTrace();
            HiLog.info(LABEL_LOG, "failed to getFD");
        }
        //绑定文件描述符
        return MessageParcel.dupFileDescriptor(fd);
    }

具体敬请期待参考本人的《data文件存储》文章(还未发布)

数据库存储(以对象关系映射数据库为例)

1、初始化数据库连接

系统会在应用启动时调用onStart()方法创建Data实例。在此方法中,开发者应该创建数据库连接,并获取连接对象,以便后续和数据库进行操作。为了避免影响应用启动速度,开发者应当尽可能将非必要的耗时任务推迟到使用时执行,而不是在此方法中执行所有初始化。
在这里插入图片描述

    private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD00201, "Data_Log");

    //添加全局变量
    private static final String DATABASE_NAME = "BookStore.db";//数据库文件名
    private static final String DATABASE_NAME_ALIAS = "BookStore";//创建一个别名
    private static OrmContext mOrmContext = null;//数据库操纵接口

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);

        //创建DataAbilityHelper并获取DataAbilityHelper对象实例
        //DataAbilityHelper为开发者提供了creator()方法来创建DataAbilityHelper实例
        //该方法为静态方法,有多个重载。最常见的方法是通过传入一个context对象来创建DataAbilityHelper对象。
        DatabaseHelper helper = new DatabaseHelper(this);//context入参类型为ohos.app.Context,注意不要使用slice.getContext()来获取context,请直接传入slice,否则会出现找不到类的报错。
        //通过对象数据操作接口OrmContext,创建一个别名为“BookStore”,数据库文件名为“BookStore.db”的数据库。
        mOrmContext = helper.getOrmContext(DATABASE_NAME_ALIAS, DATABASE_NAME, BookStore.class);//如果数据库已经存在,不会重复创建。
    }
2、编写数据库操作方法

Ability定义了6个方法供用户处理对数据库表数据的增删改查。这6个方法在Ability中已默认实现,开发者可按需重写。

    /**
     * 查询数据库
     *
     * @param uri        查询的目标路径
     * @param columns    查询的列名
     * @param predicates 查询条件 由类DataAbilityPredicates构建
     * @return 结果集
     */
    @Override
    public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
        if (mOrmContext == null) {
            HiLog.error(LABEL_LOG, "failed to query, ormContext is null");//查询失败,ormContext为空
            return null;
        }
        //查询数据库
        OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates, User.class);//目标路径
        ResultSet resultSet = mOrmContext.query(ormPredicates, columns);
        //如果数据库找不到这条记录
        if (resultSet.getRowCount() == 0) {//获取行计数
            HiLog.error(LABEL_LOG, "resultSet is null");//结果集为空
        }
        //返回结果
        return resultSet;
    }

    /**
     * 向数据库中插入单条数据
     *
     * @param uri   插入的目标路径
     * @param value 插入的数据值 插入的数据由ValuesBucket封装
     * @return 插入后的id
     */
    @Override
    public int insert(Uri uri, ValuesBucket value) {
        //参数校验
        if (mOrmContext == null) {
            HiLog.error(LABEL_LOG, "failed to insert, ormContext is null");//插入失败,ormContext为空
            return -1;
        }

        //请求不是插入User表的
        if (uri.getDecodedPath().equals("org.example.harmonydataabilitydemo.UserDataAbility")) {
            return -1;
        }

        //构造插入数据
        User user = new User();
        user.setUserId(value.getInteger("userId"));
        user.setFirstName(value.getString("firstName"));
        user.setLastName(value.getString("lastName"));
        user.setAge(value.getInteger("age"));
        user.setBalance(value.getDouble("balance"));

        //插入数据库
        boolean isSuccessful;
        isSuccessful = mOrmContext.insert(user);//直接传入OrmObject对象的增加接口
        if (!isSuccessful) {
            HiLog.error(LABEL_LOG, "failed to insert");//插入失败
            return -1;
        }
        //只有在flush()接口被调用后才会持久化到数据库中
        isSuccessful = mOrmContext.flush();
        if (!isSuccessful) {
            HiLog.error(LABEL_LOG, "failed to insert flush");//插入刷新flush()失败
            return -1;
        }
        //DataAbilityHelper是用于数据操作的帮助程序类
        //DataAbilityHelper它使用IDataAbilityObserver注册一个观察器以监视数据。
        //调用creator(ohos.app.Context,ohos.utils.net.Uri,boolean)创建一个具有给定Uri的实例
        DataAbilityHelper.creator(this, uri).notifyChange(uri);
        int id = Math.toIntExact(user.getRowId());//获取列id
        HiLog.error(LABEL_LOG, "UserDataAbility::insert id=" + id);//输出插入的数据id
        //此方法返回一个int类型的值用于标识结果
        return id;
    }

    /**
     * 向数据库中插入多条数据 批量插入
     * 接收一个ValuesBucket数组用于单次插入一组对象
     * 它的作用是提高插入多条重复数据的效率。该方法系统已实现,开发者可以直接调用。
     *
     * @param uri    插入的目标路径
     * @param values 插入的数据值
     * @return
     */
    @Override
    public int batchInsert(Uri uri, ValuesBucket[] values) {
        return super.batchInsert(uri, values);
    }

    /**
     * 删除一条或多条数据
     *
     * @param uri        目标路径
     * @param predicates 删除条件由类DataAbilityPredicates构建
     * @return 删除的结果
     */
    @Override
    public int delete(Uri uri, DataAbilityPredicates predicates) {
        if (mOrmContext == null) {
            HiLog.error(LABEL_LOG, "failed to delete, ormContext is null");//删除失败,ormContext为零
            return -1;
        }
        OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates, User.class);
        int value = mOrmContext.delete(ormPredicates);
        DataAbilityHelper.creator(this, uri).notifyChange(uri);
        HiLog.error(LABEL_LOG, "UserDataAbility::delete value=" + value);
        return value;
    }

    /**
     * 更新数据库
     *
     * @param uri        目标路径
     * @param value      用户可以在ValuesBucket参数中指定要更新的数据
     * @param predicates 在DataAbilityPredicates中构建更新的条件
     * @return 更新的结果
     */
    @Override
    public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
        if (mOrmContext == null) {
            HiLog.error(LABEL_LOG, "failed to update, ormContext is null");//更新失败,ormContext为零
            return -1;
        }
        OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates, User.class);
        int index = mOrmContext.update(ormPredicates, value);
        HiLog.error(LABEL_LOG, "UserDataAbility::update index=" + index);
        DataAbilityHelper.creator(this, uri).notifyChange(uri);
        return index;
    }

    /**
     * 批量操作数据库
     *
     * @param operations DataAbilityOperation中提供了设置操作类型、数据和操作条件的方法,用户可自行设置自己要执行的数据库操作
     * @return
     * @throws OperationExecuteException
     */
    @Override
    public DataAbilityResult[] executeBatch(ArrayList<DataAbilityOperation> operations) throws OperationExecuteException {
        return super.executeBatch(operations);
    }

注意!上述代码已经初始化了数据库类BookStore.class、实体类User.class,详情请参考本人文章《鸿蒙对象关系映射数据库》的 对象关系映射数据库存储开发步骤1、2、3

访问Data

开发者可以通过DataAbilityHelper类来访问当前应用或其他应用提供的共享数据。DataAbilityHelper作为客户端,与提供方的Data进行通信。Data接收到请求后,执行相应的处理,并返回结果。DataAbilityHelper提供了一系列与Data Ability对应的方法。

创建DataAbilityHelper

DataAbilityHelper为开发者提供了creator()方法来创建DataAbilityHelper实例。该方法为静态方法,有多个重载。最常见的方法是通过传入一个context对象来创建DataAbilityHelper对象。

DataAbilityHelper helper = DataAbilityHelper.creator(this);

访问Data Ability

访问文件

在访问文件的时候,需要先在配置文件中声明使用权限

	"reqPermissions": [
      {
        "name": "ohos.permission.READ_USER_STORAGE"
      }
    ]

然后需要动态申请权限(关于动态申请权限可以参考本人的《鸿蒙开发基础知识》文章)

    private void requestPermissions() {
        if (verifySelfPermission(SystemPermission.READ_USER_STORAGE) != IBundleManager.PERMISSION_GRANTED) {
            requestPermissionsFromUser(new String[]{SystemPermission.READ_USER_STORAGE}, 0);
        }
    }

DataAbilityHelper为开发者提供了FileDescriptor openFile​(Uri uri, String mode)方法来操作文件,该方法返回一个目标文件的FD(文件描述符),把文件描述符封装成流,开发者就可以对文件流进行自定义处理。

	private Image mImageImg;
	mImageImg = (Image) findComponentById(ResourceTable.Id_image_img);//图片

	//选择图片
    private void selectImage() {
        ResultSet resultSet = null;
        try {
            /**
             * 获取查询的结果集
             *      第一个参数为文件对应的Uri
             *      第二个参数为想要查询的字段,这里因为是查询所有的图片,所以填null即可
             *      第三个参数为查询条件,同样为null
             */
            //获取媒体外部存储中的图片URI需要使用的预定义字段是:AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI
            resultSet = mDataAbilityHelper.query(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, null, null);
            //因为这里只需要显示一张图片,所以就不遍历了,直接获取第一条即可
            resultSet.goToFirstRow();
            //通过指定的下标获取列索引,通过获得的列索引获取到该图片的资源id。
            int mediaId = resultSet.getInt(resultSet.getColumnIndexForName(AVStorage.Images.Media.ID));//通过AVStorage访问系统相册
            //将图片id转换为Uri类型
            Uri uri = Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, mediaId + "");
            HiLog.error(LABEL_LOG, "" + uri);
            //读取文件描述符
            FileDescriptor fileDescriptor = mDataAbilityHelper.openFile(uri, "r");
            //获取文件输入流
            FileInputStream fileInputStream = new FileInputStream(fileDescriptor);
            //使用文件描述符封装成的文件流,进行文件操作
            ImageSource imageSource = ImageSource.create(fileInputStream, null);
            PixelMap pixelMap = imageSource.createPixelmap(null);
            mImageImg.setPixelMap(pixelMap);
        } catch (DataAbilityRemoteException | FileNotFoundException e) {
            e.printStackTrace();
        }
    }
访问数据库

DataAbilityHelper为开发者提供了增、删、改、查以及批量处理等方法来操作数据库。
可以在MainAbilitySlice中,编写button调用方法来展示访问数据库
值得注意的是

	//下面的uriString要与config.json中DataAbility的uri保持一致
    //但是!!!!!他有三个斜杠!!!!!如果你写错了,恭喜你会看见paths is illegal错误
    private static final String BASE_URI = "dataability:///org.example.harmonydataabilitydemo.UserDataAbility";

paths is illegal错误
在这里插入图片描述
访问数据库方法如下:

    //插入单条数据
    private void insertData() {
        //构造插入数据
        ValuesBucket valuesBucket = new ValuesBucket();
        valuesBucket.putString("firstName", "一");
        valuesBucket.putString("lastName", "yi");
        valuesBucket.putInteger("age", 11);
        valuesBucket.putDouble("balance", 111.11);

        //构造uri
        Uri uri = Uri.parse(BASE_URI);

        //进行数据插入
        try {
            int id = mDataAbilityHelper.insert(uri, valuesBucket);
            HiLog.error(LABEL_LOG, "MainAbilitySlice::insert id=" + id);
        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        }
    }

    //查询
    private void queryData() {
        //构造查询条件
        DataAbilityPredicates predicates = new DataAbilityPredicates();
        //predicates提供了很多方法
        predicates.equalTo("firstName", "一");
        //查询年龄在1~50之间的数据
        //predicates.between("age",1,50);

        //构建查询字段
        String[] columns = new String[]{"age", "lastName"};

        //构造uri
        Uri uri = Uri.parse(BASE_URI);

        //进行查询
        try {
            //用一个结果集来接收查询返回的数据
            ResultSet resultSet = mDataAbilityHelper.query(uri, columns, predicates);
            if (resultSet.getRowCount() == 0) {
                HiLog.error(LABEL_LOG, "MainAbilitySlice::query找不到这条记录");
                return;
            }

            //从第一行开始
            resultSet.goToFirstRow();
            //处理每一行的数据
            do {
                //在此处理ResultSet中的记录
                HiLog.error(LABEL_LOG, "MainAbilitySlice::query lastName=" + resultSet.getString(1));
            } while (resultSet.goToNextRow());

//            //处理结果
//            while (resultSet.goToNextRow()) {
//                //在此处理ResultSet中的记录
//                HiLog.error(LABEL_LOG, "MainAbilitySlice::lastName=" + resultSet.getString(1));
//            }

        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        }

    }

    //更新
    private void updateData() {
        //构造更新条件
        DataAbilityPredicates predicates = new DataAbilityPredicates();
        predicates.equalTo("firstName", "一");

        //构造更新数据
        ValuesBucket valuesBucket = new ValuesBucket();
        valuesBucket.putString("lastName", "一");
        valuesBucket.putInteger("age", 20);

        //构造uri
        Uri uri = Uri.parse(BASE_URI);

        try {
            //更新数据
            int index = mDataAbilityHelper.update(uri, valuesBucket, predicates);
            HiLog.error(LABEL_LOG, "MainAbilitySlice::update index=" + index);//更新了几条数据
        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        }
    }

    //删除数据
    private void deleteData() {
        //构造删除条件
        DataAbilityPredicates predicates = new DataAbilityPredicates();
        predicates.equalTo("firstName", "一");
        //age在18~25的数据
        //predicates.between("age",18,25);

        //构造uri
        Uri uri = Uri.parse(BASE_URI);

        try {
            //删除
            mDataAbilityHelper.delete(uri, predicates);
        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        }
    }

    //插入多条数据
    private void insertMoreData() {
        //构造插入数据
        ValuesBucket[] valuesBuckets = new ValuesBucket[3];
        //构建第一条数据
        valuesBuckets[0] = new ValuesBucket();
        valuesBuckets[0].putString("firstName", "二");
        valuesBuckets[0].putString("lastName", "er");
        valuesBuckets[0].putInteger("age", 26);
        valuesBuckets[0].putDouble("balance", 120.5);
        //构建第二条数据
        valuesBuckets[1] = new ValuesBucket();
        valuesBuckets[1].putString("firstName", "三");
        valuesBuckets[1].putString("lastName", "san");
        valuesBuckets[1].putInteger("age", 27);
        valuesBuckets[1].putDouble("balance", 99.00);
        //构建第三条数据
        valuesBuckets[2] = new ValuesBucket();
        valuesBuckets[2].putString("firstName", "四");
        valuesBuckets[2].putString("lastName", "si");
        valuesBuckets[2].putInteger("age", 28);
        valuesBuckets[2].putDouble("balance", 200.00);

        //构造uri
        Uri uri = Uri.parse(BASE_URI);

        try {
            //批量插入数据
            mDataAbilityHelper.batchInsert(uri, valuesBuckets);
        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        }
    }

    //批量操作数据
    private void moreOperationData() {
        //构造uri
        Uri uri = Uri.parse(BASE_URI);

        //构造批量操作
        //第一个
        ValuesBucket valuesBucket1 = new ValuesBucket();
        valuesBucket1.putString("firstName", "五");
        valuesBucket1.putString("lastName", "wu");
        valuesBucket1.putInteger("age", 51);
        valuesBucket1.putDouble("balance", 220.00);
        //构建批量插入
        DataAbilityOperation operation1 = DataAbilityOperation.newInsertBuilder(uri).withValuesBucket(valuesBucket1).build();

        //第二个
        ValuesBucket valuesBucket2 = new ValuesBucket();
        valuesBucket2.putString("firstName", "一");
        valuesBucket2.putString("lastName", "yiyi");
        valuesBucket2.putInteger("age", 52);
        valuesBucket2.putDouble("balance", 120.00);
        DataAbilityOperation operation2 = DataAbilityOperation.newInsertBuilder(uri).withValuesBucket(valuesBucket2).build();

        //第三个
        //构造更新条件
        DataAbilityPredicates predicates = new DataAbilityPredicates();
        predicates.equalTo("firstName", "一");
        //构造更新数据
        ValuesBucket valuesBucket3 = new ValuesBucket();
        valuesBucket3.putString("lastName", "一一");
        DataAbilityOperation operation3 = DataAbilityOperation.newUpdateBuilder(uri).withValuesBucket(valuesBucket3).build();

        ArrayList<DataAbilityOperation> operations = new ArrayList<>();
        operations.add(operation1);
        operations.add(operation2);
        operations.add(operation3);

        try {
            //获取批量操作数据的结果
            DataAbilityResult[] results = mDataAbilityHelper.executeBatch(uri, operations);
            HiLog.error(LABEL_LOG, "MainAbilitySlice::批量操作" + results.length);
        } catch (DataAbilityRemoteException | OperationExecuteException e) {
            e.printStackTrace();
        }
    }

项目展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值