鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)

鸿蒙开发核心之Ability详解

    • 一、Ability用途
    • 二 、Page Ability讲解
      • ① Page Ability 生命周期
      • ② AbilitySlice 生命周期
    • 三、Service Ability讲解
      • ① Service Ability 生命周期
      • ② 启动Service Ability
      • ③ 停止Service Ability
      • ③ 连接Service Ability
      • ④ 断开Service Ability
      • ⑤ 前台Service
    • 四、Data Ability讲解
      • ① URI 介绍
      • ② 访问 Data和声明使用权限
      • ③ 创建Data
    • 五、结语

一、Ability用途

  在知道用法之前,首先你是不是得知道这个Ability怎么读?对了,Ability (音译 :阿B了D),中文意思就是能力,不要给我扯什么音标啥的,不好使,你仔细想一下,你是因为英语学得好才来当程序员的吗?To young to simple!
  Ability 是应用所具备能力的抽象,也是应用程序的重要组成部分。一个应用可以具备多种能力(即可以包含多个 Ability),HarmonyOS 支持应用以 Ability 为单位进行部署。Ability 可以分为 FA(Feature Ability)和 PA(Particle Ability)两种类型,每种类型为开发者提供了不同的模板,以便实现不同的业务功能。
  从上面一段文字,去其糟粕,取其精华之后就是两点。FA(Feature Ability)PA(Particle Ability)

  FA(Feature Ability) (音译:非ture 阿B了D),中文意思是功能能力,它支持Page Ability 页面能力用于提供与用户交互的能力。一个Page 可以由一个或多个 AbilitySlice 构成,AbilitySlice 是指应用的单个页面及其控制逻辑的总和。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第1张图片
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第2张图片

  一个 Page 可以包含多个 AbilitySlice,但是 Page 进入前台时界面默认只展示一个AbilitySlice。默认展示的 AbilitySlice 是通过 setMainRoute() 方法来指定的。如果需要更改默认展示的 AbilitySlice,可以通过 addActionRoute() 方法为此 AbilitySlice 配置一条路由规则。此时,当其他 Page 实例期望导航到此 AbilitySlice 时,可以在 Intent 中指定 Action。addActionRoute() 方法中使用的动作命名,需要在应用配置文件(config.json)中注册:
比如
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第3张图片

  PA(Particle Ability) (音译:趴踢扣 阿B了D),这个里面也是支持两个能力, Service AbilityData Ability 我相信你知道它们的意思,就是服务能力和数据能力。Service用于提供后台运行任务的能力。Data 用于对外部提供统一的数据访问抽象。在配置文件(config.json)中注册 Ability 时,可以通过配置 Ability 元素中的“type”属性来指定 Ability 模板类型,示例如下。其中,“type”的取值可以为“page”、“service”或“data”,分别代表 Page 模板、Service 模板、Data 模板。结合下面这个图来看知道是怎么回事了,type的属性值取决于你创建Ability是选择的类型,当然你也可以后面再改。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第4张图片

二 、Page Ability讲解

现在我们知道这个Page Ability是主要负责页面交互的,那么就可以理解为Android 的Activity。那么都知道Activity有生命周期,同样的Page Ability也是的。下面来看看它的生命周期。

① Page Ability 生命周期

首先来看官方的一张图
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第5张图片
重点看蓝色方框的。粉红色的是当前应用的状态。
声明周期分别是onStart()onActive()onInactive()onBackground()onForeground()onStop()

下面来看看详细的解释

  • onStart() 当系统首次创建 Page Ability实例时,触发该回调。对于一个 Page Ability实例,该回调在其生命周期过程中仅触发一次,Page Ability在该逻辑后将进入 INACTIVE 状态。开发者必须重写该方法,并在此配置默认展示的 AbilitySlice。如下图所示
    鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第6张图片
    和onCreate有点像。

  • onActive()Page Ability会在进入 INACTIVE 状态后来到前台,然后系统调用此回调。Page Ability 在此之后进入ACTIVE 状态,该状态是应用与用户交互的状态。Page Ability将保持在此状态,除非某类事件发生导致 Page Ability失去焦点,比如用户点击返回键或导航到其他 Page Ability。当此类事件发生时,会触发Page Ability回到 INACTIVE 状态,系统将调用 onInactive() 回调。此后,Page Ability可能重新回到ACTIVE 状态,系统将再次调用 onActive() 回调。因此,开发者通常需要成对实现 onActive()onInactive(),并在 onActive() 中获取在 onInactive() 中被释放的资源。类似于Android的onResume。

  • onInactive()Page Ability失去焦点时,系统将调用此回调,此后 Page 进入 INACTIVE 状态。开发者可以在此回调中实现 Page 失去焦点时应表现的恰当行为。类似于Android的onPause和onStop的集合体。

  • onBackground() 如果 Page Ability不再对用户可见,系统将调用此回调通知开发者用户进行相应的资源释放,此后Page Ability进入 BACKGROUND 状态。开发者应该在此回调中释放 Page Ability 不可见时无用的资源,或在此回调中执行较为耗时的状态保存操作。

  • onForeground() 处于 BACKGROUND 状态的 Page Ability仍然驻留在内存中,当重新回到前台时(比如用户重新导航到此 Page Ability),系统将先调用 onForeground()回调通知开发者,而后 Page 的生命周期状态回到 INACTIVE 状态。开发者应当在此回调中重新申请在 onBackground()中释放的资源,最后 Page 的生命周期状态进一步回到 ACTIVE 状态,系统将通过 onActive()回调通知开发者用户。

  • onStop() 系统将要销毁 Page Ability时,将会触发此回调函数,通知用户进行系统资源的释放。销毁 Page 的可能原因包括以下几个方面:
    ▪ 用户通过系统管理能力关闭指定 Page Ability,例如使用任务管理器关闭 Page Ability
    ▪ 用户行为触发 Page Ability的 terminateAbility()方法调用,例如使用应用的退出功能。
    ▪ 配置变更导致系统暂时销毁 Page Ability并重建。
    ▪ 系统出于资源管理目的,自动触发对处于 BACKGROUND 状态 Page Ability的销毁。

OK,Page Ability 的生命周期就讲完了,具体要熟悉的话还是从实际开发中获取才行。

② AbilitySlice 生命周期

先来看下面这张图
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第7张图片
  说实话一开始创建项目的时候就只有这个MainAbilityHelloWorld以及slice包下的MainAbilitySlice,后来新建了一个SecondAbility,而SecondAbilitySlice是自动生成的,这说明一个问题,它们之间有不可告人的秘密。那么下面就戳穿这个秘密,摊牌了,它们是一对好基友。

解释:AbilitySlice 作为 Page Ability的组成单元,其生命周期是依托于其所属 Page Ability生命周期的。AbilitySlicePage Ability具有相同的生命周期状态和同名的回调,当 Page Ability生命周期发生变化时,它的 AbilitySlice 也会发生相同的生命周期变化。此外,AbilitySlice 还具有独立于 Page Ability的生命周期变化,这发生在同一 Page Ability中的 AbilitySlice 之间导航时,此时 Page Ability的生命周期状态不会改变。AbilitySlice 生命周期回调与 Page Ability的相应回调类似,因此不再赘述。由于 AbilitySlice 承载具体的页面,开发者必须重写 AbilitySlice 的 **onStart()**回调,并在此方法中通过 **setUIContent()**方法设置页面,如下所示:
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第8张图片
Page 与 AbilitySlice 生命周期关联

  当 AbilitySlice 处于前台且具有焦点时,其生命周期状态随着所属 Page Ability的生命周期状态的变化而变化。当一个 Page Ability
有多个 AbilitySlice 时,例如:MyAbility 下有 FooAbilitySliceBarAbilitySlice,当前 FooAbilitySlice 处于前台并获得焦点,并即将导航到 BarAbilitySlice,在此期间的生命周期状态变化顺序为:

  1. FooAbilitySlice 从 ACTIVE 状态变为 INACTIVE 状态。
  2. BarAbilitySlice 则从 INITIAL 状态首先变为 INACTIVE 状态,然后变为 ACTIVE 状态(假定此前 BarAbilitySlice 未曾
    启动)。
  3. FooAbilitySlice 从 INACTIVE 状态变为 BACKGROUND 状态。对应两个 slice 的生命周期方法回调顺序为:
    FooAbilitySlice.onInactive() --> BarAbilitySlice.onStart() --> BarAbilitySlice.onActive() -
    -> FooAbilitySlice.onBackground()
    在整个流程中,MyAbility 始终处于 ACTIVE 状态。但是,当 Page Ability被系统销毁时,其所有已
    实例化的 AbilitySlice 将联动销毁,而不仅是处于前台的 AbilitySlice。

三、Service Ability讲解

  先来看一下Service Ability的官方解释基于 Service 模板的 Ability(以下简称“Service”)主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service 可由其他应用或 Ability 启动,即使用户切换到其他应用,Service 仍将在后台继续运行。
  Service 是单实例的。在一个设备上,相同的 Service 只会存在一个实例。如果多个 Ability 共用这个实例,只有当与 Service 绑定的所有 Ability 都退出后,Service 才能够退出。由于Service 是在主线程里执行的,因此,如果在 Service 里面的操作时间过长,开发者必须在Service 里创建新的线程来处理,防止造成主线程阻塞,应用程序无响应。其实和Android的Service有点像。
下面创建一个Service,右键你的包名 → New → Ability → Empty Service Ability。如下图所示
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第9张图片
然后
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第10张图片
上面的这个Visible你如果勾选上就是你的这个Service对其他应用程序可见,而Enable background mode表示后台模式,如果你打开这个开关,就表示你的Service要在后台运行,还可以自己选择你要在后台干嘛。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第11张图片
这个我翻译一下
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第12张图片

这里你就必须要选一个,不选就不能创建这个Service Ability。下面我们就直接创建,不勾选,不打开。创建Service Ability不会生成AbilitySlice。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第13张图片
这个时候在config.json文件中会自动生成相关的属性。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第14张图片
可以看到相比于Page ,Service的属性要少一些,而且type的属性值是“service”。

① Service Ability 生命周期

下面看一下ServiceAbility的代码:

package com.llw.helloworld;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.rpc.IRemoteObject;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

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

    @Override
    public void onStart(Intent intent) {
     
        HiLog.error(LABEL_LOG, "ServiceAbility::onStart");
        super.onStart(intent);
    }

    @Override
    public void onBackground() {
     
        super.onBackground();
        HiLog.info(LABEL_LOG, "ServiceAbility::onBackground");
    }

    @Override
    public void onStop() {
     
        super.onStop();
        HiLog.info(LABEL_LOG, "ServiceAbility::onStop");
    }

    @Override
    public void onCommand(Intent intent, boolean restart, int startId) {
     
    }

    @Override
    public IRemoteObject onConnect(Intent intent) {
     
        return null;
    }

    @Override
    public void onDisconnect(Intent intent) {
     
    }
}

生命周期:onStart()onCommand()onConnect()onDisconnect()onStop()
单个讲解

  • onStart() 该方法在创建 Service 的时候调用,用于 Service 的初始化,在 Service 的整个生命周期只会调用一次。
  • onCommand() 在 Service 创建完成之后调用,该方法在客户端每次启动该 Service 时都会调用,用户可以在该方法中做一些调用统计、初始化类的操作。
  • onConnect() 在 Ability 和 Service 连接时调用,该方法返回 IRemoteObject 对象,用户可以在该回调函数中生成对应 Service 的 IPC 通信通道,以便 Ability 与 Service 交互。Ability 可以多次连接同一个 Service,系统会缓存该 Service 的 IPC 通信对象,只有第一个客户端连接 Service 时,系统才会调用 Service 的 onConnect 方法来生成 IRemoteObject 对象,而后系统会将同一个RemoteObject 对象传递至其他连接同一个 Service 的所有客户端,而无需再次调用onConnect 方法。
  • onDisconnect() 在 Ability 与绑定的 Service 断开连接时调用。
  • onStop() 在 Service 销毁时调用。Service 应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。

② 启动Service Ability

  Ability 为开发者提供了startAbility() 方法来启动另外一个 Ability。因为 Service 也是 Ability的一种,开发者同样可以通过将 Intent 传递给该方法来启动 Service。不仅支持启动本地Service,还支持启动远程 Service。
  开发者可以通过构造包含 DeviceId、BundleName 与 AbilityName 的 Operation 对象来设置目标 Service 信息。这三个参数的含义如下:

  • DeviceId:表示设备 ID。如果是本地设备,则可以直接留空;如果是远程设备,可以通过ohos.distributedschedule.interwork.DeviceManager 提供的 getDeviceList 获取设备列表。
  • BundleName:表示包名称。
  • AbilityName:表示待启动的 Ability 名称。

下面用代码来实践一下,比如我现在要在MainAbilitySlice的onStart方法中启动ServiceAbility。就可以这么写

	/**
     * 启动本地服务
     */
    private void startupLocalService() {
     
        Intent intent = new Intent();
        //构建操作方式
        Operation operation = new Intent.OperationBuilder()
                // 设备id
                .withDeviceId("")
                // 应用的包名
                .withBundleName("com.llw.helloworld")
                // 跳转目标的路径名  通常是包名+类名
                .withAbilityName("com.llw.helloworld.ServiceAbility")
                .build();
        //设置操作
        intent.setOperation(operation);
        startAbility(intent);
    }

然后在onStart中调用即可。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第15张图片
那么怎么证明ServiceAbility是启动了呢?很简单,我们只要在ServiceAbility的onStart方法中打印一个日志就可以了。进入到ServiceAbility,你会发现创建的时候就给你写好了日志。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第16张图片
那么现在启动远程模拟器,然后运行HelloWorld。进入到主页
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第17张图片
那么这个时候Service已经启动了,通过日志来看看,点击编译器下面的HiLog栏目,然后输入Demo,就能找到这个日志了。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第18张图片
那么现在我们就启动了这个本地的Service,那么如何启动远程的Service呢?

	private void startupRemotelyService() {
     
        Intent intent = new Intent();
        Operation operation = new Intent.OperationBuilder()
                .withDeviceId("deviceId")
                .withBundleName("com.huawei.hiworld.himusic")
                .withAbilityName("com.huawei.hiworld.himusic.entry.ServiceAbility")
                // 设置支持分布式调度系统多设备启动的标识
                .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
                .build();
        intent.setOperation(operation);
        startAbility(intent);
    }

  远程启动Service可以这么写,但是有一点你要确认,那就是你启动的这个服务是否允许其他应用程序发现?否则你就算知道这个服务的包名和类名也是白搭。还记得刚才在创建Service Ability的时候的Visible吗?勾选就是允许,默认是没有勾选的。那么我又想去勾选了咋办?难道我现在重新创建一个再勾选上?感觉这样是可以的,但是太蠢了。不够优雅。既然你也不知道怎么搞,我也不知道怎么搞,那么就实验一下,比如我再创建一个ServiceAbility。这里设置名为ServiceAbility2,然后勾选一下Visible,然后我们到config.json配置文件中去看之前的没有勾选的Service有啥不同。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第19张图片
  现在你是不是就有种恍然大明白的感觉了。只要通过加一个visible的属性,设置为true,就可以了,如果没有这个属性,就是默认为false。OK,那么这就解决了这个启动Service的问题。

通过 startAbility() 方法来启动 Service。

  • 如果 Service 尚未运行,则系统会先调用 onStart()来初始化 Service,再回调 Service 的 onCommand()方法来启动Service。刚才我们并没有看到有打印onCommand,是因为它里面没有方法。那么现在我在这个onCommand方法里面也加一个日志,然后重新运行一下
	@Override
    public void onCommand(Intent intent, boolean restart, int startId) {
     
        HiLog.error(LABEL_LOG, "ServiceAbility::onCommand");
    }

鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第20张图片

  • 如果 Service 正在运行,则系统会直接回调 Service 的 onCommand()方法来启动 Service。这个场景需要先返回到设备主页面,然后再打开这个应用,首先返回主页面,点击右边的圆形按钮
    鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第21张图片
    设备主页,这时候Service在后台运行,然后再点一下圆形按钮,进入到应用页面。
    鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第22张图片
    这里是应用页面,目前只有一个新增的应用,其他两个是系统应用,这里是一个列表,你可以通过鼠标按住左键上下进行拖动。然后点击这个HelloWorld。
    鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第23张图片
    回到应用的主页面。这个时候你看日志
    鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第24张图片
    系统直接回调 Service 的 onCommand()方法来启动 Service。这样实际操作一下是不是印象更深刻呢?为了使这个操作更加易懂,我决定安装一个电脑录屏软件,然后再把录得视频转GIF,再贴到文章里,这样看起来就更加的易懂了。刚才说了启动,那么下面说停止。

③ 停止Service Ability

  • 停止 Service
      Service 一旦创建就会一直保持在后台运行,除非必须回收内存资源,否则系统不会停止或销毁 Service。开发者可以在 Service 中通过 terminateAbility()停止本 Service 或在其他 Ability调用 stopAbility()来停止 Service。
      停止 Service 同样支持停止本地设备 Service 和停止远程设备 Service,使用方法与启动Service 一样。一旦调用停止 Service 的方法,系统便会尽快销毁 Service。

有两种停止Service的方法,在Page Ability中停止,和在本Service中停止,先试一下第一种。
下面我们在MainAbilitySlice中增加一个停止服务的方法。

	/**
     * 停止本地服务  在Page Ability中停止Service
     */
    private void stopLocalService() {
     
        Intent intent = new Intent();
        //构建操作方式
        Operation operation = new Intent.OperationBuilder()
                // 设备id
                .withDeviceId("")
                // 应用的包名
                .withBundleName("com.llw.helloworld")
                // 跳转目标的路径名  通常是包名+类名
                .withAbilityName("com.llw.helloworld.ServiceAbility")
                .build();
        //设置操作
        intent.setOperation(operation);
        //停止服务
        stopAbility(intent);
    }

然后再点击按钮的时候调用。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第25张图片
然后先运行一下进入到主页面,然后点击Next按钮,看下面的日志。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第26张图片

可以看到当我们从其他的Page Ability中停止Service时,会先回调onBackground。因为这个时候服务是在前台运行的,系统会把服务放到后台,然后再通过stop来停止这个服务。
下面再看看在本Service中停止这个服务。可以通过一个延时服务来操作,下面来看看代码怎么写的。

	/**
     * 创建一个线程池
     */
    final static ScheduledExecutorService service = Executors.newScheduledThreadPool(4);

    private void stopService() {
     
        // 延时任务
         service.schedule(threadFactory.newThread(new Runnable() {
     
            @Override
            public void run() {
     
                //停止服务当前服务
                terminateAbility();
            }
            //延时三秒执行
        }), 3, TimeUnit.SECONDS);
    }

    /**
     * 线程工厂
     */
    private ThreadFactory threadFactory = new ThreadFactory() {
     
        @Override
        public Thread newThread(final Runnable r) {
     
            return new Thread() {
     
                @Override
                public void run() {
     
                    r.run();
                }
            };
        }
    };

  为什么要这么写呢?因为DS里面推荐使用ScheduledExecutorService ,不然我就直接用Timer或者Thread就可以了。创建了一个线程池,然后创建一个线程工厂,在进行延时操作的时候,传入了三个参数,一个是线程工厂,里面有一个Runnable(),第二个参数代表数量,第三个参数是单位,上面的代码就是3秒。
 下面直接运行到模拟器,然后等待三秒就会自动调用terminateAbility();停止Service。你会发现和通过其他的Page Ability停止服务的执行流程是一样的。鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第27张图片

③ 连接Service Ability

  如果 Service 需要与 Page Ability 或其他应用的 Service Ability 进行交互,则应创建用于连接的 Connection。Service 支持其他 Ability 通过 connectAbility()方法与其进行连接。
  在使用 connectAbility()处理回调时,需要传入目标 Service 的 Intent 与 IAbilityConnection的实例。IAbilityConnection 提供了两个方法供开发者实现:onAbilityConnectDone() 用来处理连接的回调,onAbilityDisconnectDone() 用来处理断开连接的回调。

  在MainAbilitySlice中添加如下代码:

	/**
     * 连接服务
     */
    private void connectService(){
     
        // 连接 Service
        Intent intent = new Intent();
        //构建操作方式
        Operation operation = new Intent.OperationBuilder()
                // 设备id
                .withDeviceId("")
                // 应用的包名
                .withBundleName("com.llw.helloworld")
                // 跳转目标的路径名  通常是包名+类名
                .withAbilityName("com.llw.helloworld.ServiceAbility")
                .build();
        //设置操作
        intent.setOperation(operation);
        //连接到服务
        connectAbility(intent,connection);

    }

    // 创建连接回调实例
    private IAbilityConnection connection = new IAbilityConnection() {
     
        // 连接到 Service 的回调
        @Override
        public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
     
            // 在这里开发者可以拿到服务端传过来 IRemoteObject 对象,从中解析出服务端传过来的信息
        }

        // 断开与连接的回调
        @Override
        public void onAbilityDisconnectDone(ElementName elementName, int i) {
     
           
        }
    };

然后在点击的时候调用
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第28张图片
别Service的onConnect方法中加入日志打印
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第29张图片
下面运行一下:
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第30张图片
连接成功。

④ 断开Service Ability

断开服务其实就比较的简单了,调用**disconnectAbility()**方法即可,而且不用传intent,但是要传IAbilityConnection进入,所以可以可以这样来测试,在连接到Service之后马上断开连接。

//断开服务
disconnectAbility(connection);

鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第31张图片
然后运行起来,进入应用页面,然后点击Next。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第32张图片
OK,到这一步,相信你已经会基本操作了。而Service的生命周期根据调用方法的不同,其生命周期有以下两种路径:

  • 启动 Service 该 Service 在其他 Ability 调用 startAbility()时创建,然后保持运行。其他 Ability 通过调用stopAbility()来停止 Service,Service 停止后,系统会将其销毁。
  • 连接 Service 该 Service 在其他 Ability 调用 connectAbility()时创建,客户端可通过调用disconnectAbility()断开连接。多个客户端可以绑定到相同 Service,而且当所有绑定全部取消后,系统即会销毁该 Service。

看一下官网的图片
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第33张图片

⑤ 前台Service

  刚才我们说的都是后台的Service,那么怎么到前台来呢?最通用的前台服务就是音乐播放了,用手机的时候它会在通知栏创建,然后播放音乐,那么在鸿蒙中需要怎么使用前台服务呢?使用前台 Service 并不复杂,开发者只需在 Service 创建的方法里,调用keepBackgroundRunning()将 Service 与通知绑定。调用 keepBackgroundRunning()方法前需要在配置文件中声明 ohos.permission.KEEP_BACKGROUND_RUNNING 权限,该权限是 normal 级别,同时还需要在配置文件中添加对应的 backgroundModes 参数。在onStop()方法中调用 cancelBackgroundRunning()方法可停止前台 Service。

  说这么多没啥用,下面来实际操作一下:
在connectService方法中注释断开服务
在这里插入图片描述
然后进入到ServiceAbility中,新一个启动前台服务的方法。

	/**
     * 启动前台服务
     */
    private void startupForegroundService(){
     
        //创建通知请求 设置通知id为9527
        NotificationRequest request = new NotificationRequest(1005);
        //创建普通通知
        NotificationRequest.NotificationNormalContent content =
                new NotificationRequest.NotificationNormalContent();
        //设置通知的标题和内容
        content.setTitle("Title").setText("Text");
        //创建通知内容
        NotificationRequest.NotificationContent notificationContent = new
                NotificationRequest.NotificationContent(content);
        //设置通知
        request.setContent(notificationContent);
        keepBackgroundRunning(1005,request);
        HiLog.error(LABEL_LOG, "ServiceAbility::startupForegroundService");
    }

然后在onStart中调用。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第34张图片
别忘了在config.json中给相关的代码配置:
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第35张图片

然后直接运行到主页面,之后会先启动Service,然后将Service变成前台服务。运行之后如下:
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第36张图片
说实话目前也就只是日志打印出来了,但是我也不知道当前这个服务是不是在前台。
然后在onCommand中取消前台服务:

	@Override
    public void onCommand(Intent intent, boolean restart, int startId) {
     
        HiLog.error(LABEL_LOG, "ServiceAbility::onCommand");

        cancelBackgroundRunning();
        HiLog.error(LABEL_LOG, "ServiceAbility::cancelBackgroundRunning");

    }

再运行一次。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第37张图片

四、Data Ability讲解

  使用 Data 模板的 Ability(以下简称“Data”)有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data 既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。

  数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。Data 对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供。说起来和Android的ContentProvider有些像。

① URI 介绍

  Data 的提供方和使用方都通过 URI(Uniform Resource Identifier)来标识一个具体的数据,例如数据库中的某个表或磁盘上的某个文件。HarmonyOS 的 URI 仍基于 URI 通用标准,格式如下:

  • scheme:协议方案名,固定为“dataability”,代表 Data Ability 所使用的协议类型。
  • authority:设备 ID,如果为跨设备场景,则为目的设备的 IP 地址;如果为本地设备场景,则不需要填写。
  • path:资源的路径信息,代表特定资源的位置信息。
  • query:查询参数。
  • fragment:可以用于指示要访问的子资源。
    URI 示例:
  • 跨设备场景:dataability://device_id/com.huawei.dataability.persondata/person/10
  • 本地设备:dataability:///com.huawei.dataability.persondata/person/10

② 访问 Data和声明使用权限

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

如果待访问的 Data 声明了访问需要权限,则访问此 Data 需要在配置文件中声明需要此权限。比如
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第38张图片
reqPermissions 表示应用运行时向系统申请的权限。
说了这么多还是来创建一个Data Ability吧,鼠标右键包名 → New → Ability → Empty Data Ability
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第39张图片
这个的Visible和Service的Visible是同样的意思,勾选上就是运行其他应用程序访问数据。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第40张图片

然后打开config.json,看创建DataAbility时,自动生成了那些代码。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第41张图片
可以看到type为“data”,另外还自带一个提供给外部数据的权限,已经访问这个DataAbility的uri。

然后看一下DataAbility的代码:

package com.llw.helloworld;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.data.resultset.ResultSet;
import ohos.data.rdb.ValuesBucket;
import ohos.data.dataability.DataAbilityPredicates;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.net.Uri;
import ohos.utils.PacMap;

import java.io.FileDescriptor;

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, "ProviderAbility onStart");
    }

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

    @Override
    public int insert(Uri uri, ValuesBucket value) {
     
        HiLog.info(LABEL_LOG, "ProviderAbility 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;
    }

    @Override
    public String getType(Uri uri) {
     
        return null;
    }
}

在创建的时候就生成了一些代码,基本的增删改查、打开文件、获取URI类型、获取文件类型、还有一个回调。再加上一个onStart方法,总共是9个,乍一看比较多。下面先来介绍 DataAbilityHelper 具体的使用步骤。
创建 DataAbilityHelper

  DataAbilityHelper 为开发者提供了 creator()方法来创建 DataAbilityHelper 实例。该方法为静态方法,有多个重载。最常见的方法是通过传入一个 context 对象来创建DataAbilityHelper 对象。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第42张图片
DataAbilityHelper 为开发者提供了一系列的接口来访问不同类型的数据(文件、数据库等)。

  • 访问文件
    DataAbilityHelper 为开发者提供了 FileDescriptor openFile(Uri uri, String mode)方法来操作文件。此方法需要传入两个参数,其中 uri 用来确定目标资源路径,mode 用来指定打开文件的方式,可选方式包含“r”(读), “w”(写), “rw”(读写),“wt”(覆盖写),“wa”(追加写),“rwt”(覆盖写且可读)。该方法返回一个目标文件的 FD(文件描述符),把文件描述符封装成流,开发者就可以对文件流进行自定义处理。比如:
		// 读取文件描述符
        try {
     
            //通过文件描述符 读取指定uri的文件 ,“r”(读), “w”(写), “rw”(读写),“wt”(覆盖写),“wa”(追加写),“rwt”(覆盖写且可读)
            FileDescriptor fileDescriptor = helper.openFile(Uri.parse("dataability://com.llw.helloworld.DataAbility"),"r");
            //获取文件输入流
            FileInputStream fileInputStream = new FileInputStream(fileDescriptor);
        } catch (DataAbilityRemoteException e) {
     
            e.printStackTrace();
        } catch (FileNotFoundException e) {
     
            e.printStackTrace();
        }
  • 访问数据库
    DataAbilityHelper 为开发者提供了增、删、改、查以及批量处理等方法来操作数据库。
    下面代码来说明一下:
    • query 查询方法,其中 uri 为目标资源路径,columns 为想要查询的字段。开发者的查询条件可以通过 DataAbilityPredicates 来构建。查询用户表中 id 在 1-10 之间的用户的年龄,并把结果打印出来,代码示例如下:
	/**
     * 查询
     */
    private void queryData(DataAbilityHelper helper) {
     
        //构建uri
        Uri uri = Uri.parse("dataability://com.llw.helloworld.DataAbility");
        //构建查询字段
        String[] column = {
     "age"};
        // 构造查询条件
        DataAbilityPredicates predicates = new DataAbilityPredicates();
        //查询用户id在1~10之间的数据
        predicates.between("userId",1,10);

        //进行查询
        try {
     
            //用一个结果集来接收查询返回的数据
            ResultSet resultSet = helper.query(uri,column,predicates);
            //从第一行开始
            resultSet.goToFirstRow();
            //处理每一行的数据
            do {
     
                // 在此处理 ResultSet 中的记录
                HiLog.info(LABEL_LOG, resultSet.toString());
            }while (resultSet.goToNextRow());

        } catch (DataAbilityRemoteException e) {
     
            e.printStackTrace();
        }
    }
  • insert 插入方法,其中 uri 为目标资源路径,ValuesBucket 为要新增的对象。插入一条用户信息的代码示例如下:
	/**
     * 插入 单条数据
     */
    private void insertData(DataAbilityHelper helper) {
     
        //构建uri
        Uri uri = Uri.parse("dataability://com.llw.helloworld.DataAbility");
        // 构造插入数据
        ValuesBucket valuesBucket = new ValuesBucket();
        valuesBucket.putString("name","KaCo");
        valuesBucket.putInteger("age",24);
        try {
     
            helper.insert(uri,valuesBucket);
        } catch (DataAbilityRemoteException e) {
     
            e.printStackTrace();
        }
    }
  • batchInsert 批量插入方法,和 insert()类似。批量插入用户信息的代码示例如下:
	/**
     * 插入 多条数据
     * @param helper 数据帮助类
     */
    private void batchInsertData(DataAbilityHelper helper) {
     
        //构建uri
        Uri uri = Uri.parse("dataability://com.llw.helloworld.DataAbility");
        // 构造插入数据
        ValuesBucket[] valuesBuckets = new ValuesBucket[3];
        //构建第一条数据
        valuesBuckets[0] = new ValuesBucket();
        valuesBuckets[0].putString("name","Jim");
        valuesBuckets[0].putInteger("age",18);
        //构建第二条数据
        valuesBuckets[1] = new ValuesBucket();
        valuesBuckets[1].putString("name","Tom");
        valuesBuckets[1].putInteger("age",20);
        //构建第三条数据
        valuesBuckets[2] = new ValuesBucket();
        valuesBuckets[2].putString("name","Kerry");
        valuesBuckets[2].putInteger("age",24);
        try {
     
            //批量插入数据
            helper.batchInsert(uri,valuesBuckets);
        } catch (DataAbilityRemoteException e) {
     
            e.printStackTrace();
        }
    }
  • delete 删除方法,其中删除条件可以通过 DataAbilityPredicates 来构建。删除用户表中 id 在 1-10 之间的用户,代码示例如下:
	/**
     * 删除数据
     * @param helper 数据帮助类
     */
    private void deleteData(DataAbilityHelper helper) {
     
        //构建uri
        Uri uri = Uri.parse("dataability://com.llw.helloworld.DataAbility");
        // 构造删除条件
        DataAbilityPredicates predicates = new DataAbilityPredicates();
        //用户id在1~10的数据
        predicates.between("userId",1,10);
        try {
     
            //删除
            helper.delete(uri,predicates);
        } catch (DataAbilityRemoteException e) {
     
            e.printStackTrace();
        }
    }
  • update 更新方法,更新数据由 ValuesBucket 传入,更新条件由 DataAbilityPredicates 来构建。更新 id 为 2 的用户,代码示例如下:
	/**
     * 更新数据
     * @param helper 数据帮助类
     */
    private void updateData(DataAbilityHelper helper) {
     
        //构造uri
        Uri uri = Uri.parse("dataability://com.llw.helloworld.DataAbility");
        //构造更新数据
        ValuesBucket valuesBucket = new ValuesBucket();
        valuesBucket.putString("name","Aoe");
        valuesBucket.putInteger("age",66);
        //构造更新条件
        DataAbilityPredicates predicates = new DataAbilityPredicates();
        //userId为2的用户
        predicates.equalTo("userId",2);
        try {
     
            //更新数据
            helper.update(uri,valuesBucket,predicates);
        } catch (DataAbilityRemoteException e) {
     
            e.printStackTrace();
        }
    }
  • executeBatch 此方法用来执行批量操作。DataAbilityOperation 中提供了设置操作类型、数据和操作条件的方法,开发者可自行设置自己要执行的数据库操作。插入多条数据的代码示例如下:
	/**
     * 批量操作数据
     * @param helper 数据帮助类
     */
    private void executeBatchData(DataAbilityHelper helper) {
     
        //构造uri
        Uri uri = Uri.parse("dataability://com.llw.helloworld.DataAbility");
        //构造批量操作
        //第一个
        ValuesBucket valuesBucket1 = new ValuesBucket();
        valuesBucket1.putString("name","Karen");
        valuesBucket1.putInteger("age",24);
        //构建批量插入
        DataAbilityOperation operation1 = DataAbilityOperation.newInsertBuilder(uri).withValuesBucket(valuesBucket1).build();
        //第二个
        ValuesBucket valuesBucket2 = new ValuesBucket();
        valuesBucket2.putString("name","Leo");
        valuesBucket2.putInteger("age",48);
        DataAbilityOperation operation2 = DataAbilityOperation.newInsertBuilder(uri).withValuesBucket(valuesBucket2).build();
        
        ArrayList<DataAbilityOperation> operations = new ArrayList<>();
        operations.add(operation1);
        operations.add(operation2);
        try {
     
            //获取批量操作数据的结果
            DataAbilityResult[] results = helper.executeBatch(uri,operations);
            HiLog.debug(LABEL_LOG,results.length+"");
        } catch (DataAbilityRemoteException e) {
     
            e.printStackTrace();
        } catch (OperationExecuteException e) {
     
            e.printStackTrace();
        }
    }

③ 创建Data

确定数据存储方式
确定数据的存储方式,Data 支持以下两种数据形式:

  • 文件数据:如文本、图片、音乐等。
  • 结构化数据:如数据库等。
    下面创建一个UserDataAbility,注意勾选上Visible
    鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第43张图片
    实现 UserDataAbility
      UserDataAbility 接收其他应用发送的请求,提供外部程序访问的入口,从而实现应用间的数据访问。Data 提供了文件存储和数据库存储两组接口供用户使用。
    文件存储
      开发者需要在 Data 中重写 FileDescriptor openFile(Uri uri, String mode)方法来操作文件:uri 为客户端传入的请求目标路径;mode 为开发者对文件的操作选项,可选方式包含“r”(读), “w”(写), “rw”(读写)等。
      MessageParcel 类提供了一个静态方法,用于获取 MessageParcel 实例。通过dupFileDescriptor()函数复制待操作文件流的文件描述符,并将其返回,供远端应用使用。示例,根据传入uri打开对应的文件,在UserDataAbility中写入如下方法
	/**
     * uri 打开对应的文件
     */
    private void openUriFile() {
     
        //构建uri
        Uri uri = Uri.parse("dataability://com.llw.helloworld.UserDataAbility");
        //获取文件  通过uri获取解码路径列表的第2条数据
        File file = new File(uri.getDecodedPathList().get(1));
        //只读
        file.setReadOnly();
        try {
     
            //文件输入流
            FileInputStream fileInputStream = new FileInputStream(file);
            //得到文件描述符
            FileDescriptor fileDescriptor = fileInputStream.getFD();
            //绑定文件描述符
            MessageParcel.dupFileDescriptor(fileDescriptor);
        } catch (FileNotFoundException e) {
     
            e.printStackTrace();
        } catch (IOException e) {
     
            e.printStackTrace();
        }
    }

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

  • 初始化的时候连接数据库。
    首先要创建一个数据实体bean
package com.llw.helloworld;

import ohos.data.orm.OrmObject;

public class BookStore extends OrmObject {
     
    private int id;
    private String bookName;
    private double price;
    private int page;
    private String author;

    public int getId() {
     
        return id;
    }

    public void setId(int id) {
     
        this.id = id;
    }

    public String getBookName() {
     
        return bookName;
    }

    public void setBookName(String bookName) {
     
        this.bookName = bookName;
    }

    public double getPrice() {
     
        return price;
    }

    public void setPrice(double price) {
     
        this.price = price;
    }

    public int getPage() {
     
        return page;
    }

    public void setPage(int page) {
     
        this.page = page;
    }

    public String getAuthor() {
     
        return author;
    }

    public void setAuthor(String author) {
     
        this.author = author;
    }
}

然后在UserDataAbility中如下:
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第44张图片

上面的代码是官方文档里面的,可以看到这里是有一个地方报错的,因为少了一个参数,然后看一下getOrmContext方法少什么参数。
在这里插入图片描述
然后来看一下OrmMigration的源码
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第45张图片
这是一个抽象类,可以通过继承的方式去实现它里面的方法。
下面我创建一个TestOrmContext1继承OrmMigration,里面的代码如下:

package com.llw.helloworld;

import ohos.data.orm.OrmMigration;
import ohos.data.rdb.RdbStore;

public class TestOrmContext1 extends OrmMigration {
     
    /**
     * 此处用于配置数据库版本迁移的开始版本和结束版本,
     * super(startVersion, endVersion)即数据库版本号从 1 升到 2。
     */
    public TestOrmContext1() {
     
        super(1, 2);
    }

    /**
     * 迁移时
     *
     * @param rdbStore
     */
    @Override
    public void onMigrate(RdbStore rdbStore) {
     
        rdbStore.executeSql("ALTER TABLE `BookStore` ADD COLUMN `addColumn1` INTEGER");
    }
}

其实这个方法的意思就是在连接数据库的时候查询数据库的版本,决定是否要升级。
因为加了也报错,那么我为什么不加上去呢?你以为加上去就不报错了吗?我是不得其解,也许是我才疏学浅吧。
鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第46张图片

  • 编写数据库操作方法
    Ability 定义了 6 个方法供用户处理对数据库表数据的增删改查。
    先创建一个用户的实体
package com.llw.helloworld;

public class User extends OrmObject {
     
    private int id;
    private String name;
    private int age;

    public int getId() {
     
        return id;
    }

    public void setId(int id) {
     
        this.id = id;
    }

    public String getName() {
     
        return name;
    }

    public void setName(String name) {
     
        this.name = name;
    }

    public int getAge() {
     
        return age;
    }

    public void setAge(int age) {
     
        this.age = age;
    }
}

  • query 该方法接收三个参数,分别是查询的目标路径,查询的列名,以及查询条件,查询条件由类DataAbilityPredicates 构建。根据传入的列名和查询条件查询用户表的代码示例如下:
    鸿蒙 Ability 讲解(页面生命周期、后台服务、数据访问)_第47张图片
    可以看到创建Data Ability的时候就会自动生成这个方法,下面的代码就在这个方法里面写:
	/**
     * 查询数据库
     *
     * @param uri        目标uri
     * @param columns    查询的字段
     * @param predicates 查询的条件
     * @return 结果集
     */
    @Override
    public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
     
        if(ormContext == null){
     
            HiLog.error(LABEL_LOG,"failed to query, ormContext is null");
            return null;
        }
        //查询数据库
        OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates,User.class);
        ResultSet resultSet = ormContext.query(ormPredicates,columns);
        if (resultSet == null){
     
            HiLog.info(LABEL_LOG,"resultSet is null");
        }
        return resultSet;
    }
  • insert 该方法接收两个参数,分别是插入的目标路径和插入的数据值。其中,插入的数据由ValuesBucket 封装,服务端可以从该参数中解析出对应的属性,然后插入到数据库中。此方法返回一个 int 类型的值用于标识结果。接收到传过来的用户信息并把它保存到数据库中的代码示例如下:
	/**
     * 插入单条数据
     * @param uri 目标uri
     * @param value 插入的数据
     * @return 插入后的id
     */
    @Override
    public int insert(Uri uri, ValuesBucket value) {
     
        if (ormContext == null) {
     
            HiLog.info(LABEL_LOG, "failed to insert, ormContext is null");
            return -1;
        }
        //获取uri解码路径
        String path = uri.getDecodedPath();
        PathMatcher pathMatcher = new PathMatcher();
        if (pathMatcher.getPathId(path) == PathMatcher.NO_MATCH) {
     
            HiLog.info(LABEL_LOG, "UserDataAbility insert path is not matched");
            return -1;
        }
        // 构造插入数据
        User user = new User();
        user.setId(value.getInteger("id"));
        user.setName(value.getString("name"));
        user.setAge(value.getInteger("age"));
        //插入数据库
        boolean isSuccessed = true;
        isSuccessed = ormContext.insert(user);

        if (!isSuccessed) {
     
            HiLog.error(LABEL_LOG, "failed to insert");
            return -1;
        }
        isSuccessed = ormContext.flush();
        if (!isSuccessed) {
     
            HiLog.error(LABEL_LOG, "failed to insert flush");
            return -1;
        }
        DataAbilityHelper.creator(this, uri).notifyChange(uri);
        int id = Math.toIntExact(user.getRowId());
        return id;
    }
  • batchInsert 该方法为批量插入方法,接收一个 ValuesBucket 数组用于单次插入一组对象。它的作用是提高插入多条重复数据的效率。该方法系统已实现,开发者可以直接调用。
  • delete 该方法用来执行删除操作。删除条件由类 DataAbilityPredicates 构建,服务端在接收到该参数之后可以从中解析出要删除的数据,然后到数据库中执行。根据传入的条件删除用户表数据的代码示例如下:
	/**
     * 删除
     * @param uri 目标uri
     * @param predicates 删除条件
     * @return 删除的结果
     */
    @Override
    public int delete(Uri uri, DataAbilityPredicates predicates) {
     

        if (ormContext == null) {
     
            HiLog.error(LABEL_LOG, "failed to delete, ormContext is null");
            return -1;
        }
        OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates, User.class);
        int value = ormContext.delete(ormPredicates);
        DataAbilityHelper.creator(this, uri).notifyChange(uri);
        return value;
    }
  • update 此方法用来执行更新操作。用户可以在 ValuesBucket 参数中指定要更新的数据,在DataAbilityPredicates 中构建更新的条件等。更新用户表的数据的代码示例如下:
	/**
     * 更新数据
     * @param uri 目标uri
     * @param value 更新的数据
     * @param predicates 更新条件
     * @return 更新的结果
     */
    @Override
    public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
     
        if (ormContext == null) {
     
            HiLog.error(LABEL_LOG, "failed to update, ormContext is null");
            return -1;
        }

        OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates, User.class);
        int index = ormContext.update(ormPredicates, value);
        HiLog.info(LABEL_LOG, "UserDataAbility update value:" + index);
        DataAbilityHelper.creator(this, uri).notifyChange(uri);
        return index;
    }
  • executeBatch 此方法用来批量执行操作。DataAbilityOperation 中提供了设置操作类型、数据和操作条件的方法,用户可自行设置自己要执行的数据库操作。该方法系统已实现,开发者可以直接调用。

五、结语

  说实话写这一篇文章花费了一番功夫,不断的浏览官网上的文档然后结合实际来写,写的不是很好,请勿见怪,另外就是觉得官网的教程只是一部分,更多的需要开发者自行去探索和发现,正所谓师傅领进门,修行在个人,鸿蒙需要成长,我们开发者同样也要成长,也许不会前进的路上会很坎坷,但经历过后就会发现另一番风景,我是初学者,保持初学的态度和动力,感谢您的阅读,山高水长,后会有期!

你可能感兴趣的:(鸿蒙,Ability生命周期,Ability后台服务,Ability数据访问)