HarmonyOS Ability

HarmonyOS核心理念之一就是Ability,下面简单介绍它。

概述

Ability是应用所具备能力的抽象,也是应用程序的重要组成部分。一个应用可以具备多种能力(即可以包含多个Ability),HarmonyOS支持应用以Ability为单位进行部署。Ability可以分为FA(Feature Ability)和PA(Particle Ability)两种类型,每种类型,为开发者提供了不同的模板。

  • FA支持Page Ability:Page模板是FA唯一支持的模板,用于提升与用户交互的能力。一个Page实例可以包含多个相关页面,每个页面使用AbilitySlice实例表示。
  • PA支持Service Ability和Data Ability:
    Service模板:用于提供后台运行任务的能力。
    Data模板:用于对外部提供统一的数据访问抽象。

Page Ability

  • 生命周期
    在这里插入图片描述

  • 添加路由
    添加启动导航到的路由使用setMainRoute()(注意要在ability中设置路由,而不是abilitySlice),使用addActionRounte()添加别的页面导航。如下:

public class MyAbility extends Ability {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        // 设置主页面
        setMainRoute(MainSlice.class.getName());

        // 设置其它页面导航
        addActionRoute("action.pay", PaySlice.class.getName());
    }
}

当然,这些action都要现在config.json中注册:

"abilities": [
      {
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.pay"
            ]
          }
        ],
        "orientation": "unspecified",
        "name": "com.example.demo.MainAbility",
        "icon": "$media:icon",
        "description": "$string:mainability_description",
        "label": "$string:entry_MainAbility",
        "type": "page",
        "launchType": "standard"
      }
    ]
  • 导航
    同一page内导航
    就是处于同一Page Ability下。可以使用present()方法实现导航。如果希望从导航目标返回时,获得返回结果,可以使用presentForResult()实现导航,返回时使用onResult()回调来接收和处理返回结果。如下:
@Override
protected void onStart(Intent intent) {

    ...
    Button button = ...;
    button.setClickedListener(listener -> present(new TargetSlice(), new Intent()));
    button.setClickedListener(listener -> presentForResult(new TargetSlice(), new Intent()));
    ...

}

不同page内导航
可以通过配置Intent的Action导航到目标的AbilirySlice,page间的导航可以使用startAbility()或startAbilityForResult()方法,获得返回结果的回调为onAbilityResult()。在Ability中可以调用setResult()设置结果,和Android类似。

Service Ability

主要用于运行后台任务。

  • 创建Service
    代码示例:
public class ServiceAbility extends Ability {
    // 创建Service调用,初始化,整个生命周期只会调用一次,调用时传入的Intent应该为空
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
    }
    // Service初始化调用,Service每次启动都会调用
    @Override
    public void onCommand(Intent intent, boolean restart, int startId) {
        super.onCommand(intent, restart, startId);
    }
    // Ability连接服务时调用,类似Android中的bind
    @Override
    public IRemoteObject onConnect(Intent intent) {
        return super.onConnect(intent);
    }
    // 断开连接
    @Override
    public void onDisconnect(Intent intent) {
        super.onDisconnect(intent);
    }
    // Service销毁时调用
    @Override
    public void onStop() {
        super.onStop();
    }
}

注册:

{
    "module": {
        "abilities": [         
            {    
                "name": ".ServiceAbility",
                "type": "service",
                "visible": true
                ...
            }
        ]
        ...
    }
    ...
}
  • 启动Service
    1.启动
    使用startAbility()方法来启动另一个Ability。不仅支持启动本地Service,还支持启动远程Service。开发者可以通过构造包含DeviceId、BundleName与AbilityName的Operation对象来设置目标Service信息。
    DeviceId:表示设备ID,如果是本地设备可以留空;如果是远程设备,可以通过
    ohos.distributedschedule.interwork.DeviceManager提供的getDeviceList获取设备列表。
    BundleName:表示包名称。
    AbilityName:表示待启动的Ability名称。
    启动本地:
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
        .withDeviceId("")
        .withBundleName("com.domainname.hiworld.himusic")
        .withAbilityName("com.domainname.hiworld.himusic.ServiceAbility")
        .build();
intent.setOperation(operation);
startAbility(intent);

启动远程:

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

2.停止
可在服务中通过terminateAbilty()停止本Service或在其他Ability调用stopAbility()来停止Service。

  • 连接Service
    和Andrid如出一辙,连接Service需要创建IAbilityConnection来建立连接。在使用connectAbility()处理回调时,需要传入目标Service的Intent与IAbilityConnection的实例,它拥有两个回调:onAbilityConnectionDone()连接成功,onAbilityDisconnectionDone()是处理Service异常死亡的回调。
    创建Service回调实例:
// 创建连接Service回调实例
private IAbilityConnection connection = new IAbilityConnection() {
    // 连接到Service的回调
    @Override
    public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) {
        // Client侧需要定义与Service侧相同的IRemoteObject实现类。开发者获取服务端传过来IRemoteObject对象,并从中解析出服务端传过来的信息。
    }

    // Service异常死亡的回调
    @Override
    public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
    }
};

连接Service代码实例:

// 连接Service
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
        .withDeviceId("deviceId")
        .withBundleName("com.domainname.hiworld.himusic")
        .withAbilityName("com.domainname.hiworld.himusic.ServiceAbility")
        .build();
intent.setOperation(operation);
connectAbility(intent, connection);

同时,Service也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象,如下:

// 创建自定义IRemoteObject实现类
private class MyRemoteObject extends LocalRemoteObject {
    MyRemoteObject(){
    }
}

// 把IRemoteObject返回给客户端
@Override
protected IRemoteObject onConnect(Intent intent) {
    return new MyRemoteObject();
}
  • 生命周期
    两种Service:
  • 启动Service,调用startAbility创建Service,调用stopAbility()停止Service。
  • 连接Service,其他Ability调用connectionAbility()时创建,可以使用disconnectionAbility()断开连接,多个可绑定到同一个Service上,全部断开连接,Service销毁,好熟啊哈哈。
    在这里插入图片描述
  • 前台服务
    使用前台Service并不复杂,开发者只需在Service创建的方法里,调用keepBackgroundRunning()将Service与通知绑定。调用keepBackgroundRunning()方法前需要在配置文件中声明ohos.permission.KEEP_BACKGROUND_RUNNING权限,同时还需要在配置文件中添加对应的backgroundModes参数。在onStop()方法中调用cancelBackgroundRunning​()方法可停止前台Service。
    在onStart()中设置:
// 创建通知,其中1005为notificationId
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);

// 绑定通知,1005为创建通知时传入的notificationId
keepBackgroundRunning(1005, request);

配置文件:

{    
    "name": ".ServiceAbility",
    "type": "service",
    "visible": true,
    "backgroundModes": ["dataTransfer", "location"]
}

Data Ability(就是Android中的ContentProvider)

  • 概念
    使用Data模板的Ability有助于应用管理自身和其它应用存储数据的访问,并提供与其他应用共享数据的方法。
    URI介绍
    Scheme://[authority]/[path][?query][#fragement]
    Scheme:协议方案名,固定为“dataability”,代表Data Ability所使用的协议类型。
    authority:设备ID。本地不用填写。
    path:资源的路径信息。
    query:查询参数。
    fragment:可以用于指示要访问的子资源。
    示例:dataability://device_id/com.example.app/user/10
  • 创建Data
  1. 确定数据存储方式
    一般来说有两种:
  • 文件数据:如文本、图片、音乐。
  • 结构化数据:如数据库等。
  1. 实现UserrDataAbility
    UserDataAbility用来接收其他应用发送的请求,提供外部程序访问的入口,从而实现应用间的数据访问。File > New > Ability > Empty Data Ability,设置name之后,完成创建UserDataAbility。
    Data提供了文件存储和数据库存储两组接口供用户使用。
    文件存储
    开发者需要在Data中重写FileDescriptor openFile​(Uri uri, String mode)方法来操作文件:uri为客户端传入的请求目标路径;mode为开发者对文件的操作选项,可选方式包含“r”(读), “w”(写), “rw”(读写)等。
    可通过MessageParcel静态方法dupFileDescriptor复制待操作文件流的文件描述符,并将其返回,供远端应用访问。
    示例:根据传入的uri打开对应的文件。
private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD00201, "Data_Log");

@Override
public FileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    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) {
        HiLog.info(LABEL_LOG, "failed to getFD");
    }

    // 绑定文件描述符
    return MessageParcel.dupFileDescriptor(fd);
}

数据库存储
1.初始化数据连接
在onStart()中创建Data实例,创建数据库连接,并获取到连接对象。不可设置耗时过长的任务。

private static final String DATABASE_NAME = "UserDataAbility.db";
private static final String DATABASE_NAME_ALIAS = "UserDataAbility";
private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD00201, "Data_Log");
private OrmContext ormContext = null;

@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    DatabaseHelper manager = new DatabaseHelper(this);
    ormContext = manager.getOrmContext(DATABASE_NAME_ALIAS, DATABASE_NAME, BookStore.class);
}

2.编写数据库操作方法。
Ability默认定义了6个方法供用户使用,默认已经实现,可以按需重写。
在这里插入图片描述
query():接收三个参数分别是查询的目标路径,查询的列名。以及查询条件,查询条件由DataAbilityPredicates构成。

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():两个参数,分别是插入的路径和插入的数据值。

public int insert(Uri uri, ValuesBucket value) {
    // 参数校验
    if (ormContext == null) {
        HiLog.error(LABEL_LOG, "failed to insert, ormContext is null");
        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 = ormContext.insert(user);
    if (!isSuccessful) {
        HiLog.error(LABEL_LOG, "failed to insert");
        return -1;
    }
    isSuccessful = ormContext.flush();
    if (!isSuccessful) {
        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构建。

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中构建更新的条件等。

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中提供了设置操作类型、数据和操作条件的方法,用户可自行设置自己要执行的数据库操作。

  1. 注册UserDataAbility
    type:类型设置为data
    uri:对外提供的访问路径,全局唯一
    permissions:访问该data ability时需要申请的访问权限
{
    "name": ".UserDataAbility",
     "type": "data",
     "visible": true,
     "uri": "dataability://com.example.myapplication5.DataAbilityTest",
     "permissions": [
        "com.example.myapplication5.DataAbility.DATA"
     ]
}
  • 访问Data
    可以通过DataAbilityHelper类来访问当前应用或其他应用提供的共享数据。DataAbilityHelper作为客户端,与提供方的Data进行通信。
    1.声明权限
    如果访问的Data声明了访问需要权限,则访问此Data需要在配置文件需要此权限。
"reqPermissions": [
    {
        "name": "com.example.myapplication5.DataAbility.DATA"
    },
    // 访问文件还需要添加访问存储读写权限
    {
        "name": "ohos.permission.READ_USER_STORAGE"
    },
    {
        "name": "ohos.permission.WRITE_USER_STORAGE"
    }
]

2.创建DataAbilityHelper
DataAbilityHelper为开发者提供了creator()方法来创建DataAbilityHelper实例。静态方法。

DataAbilityHelper helper = DataAbilityHelper.creator(this);

访问文件:

// 读取文件描述符
FileDescriptor fd = helper.openFile(uri, "r");
FileInputStream fis = new FileInputStream(fd);

// 使用文件描述符封装成的文件流,进行文件操作

访问数据库:
上面说过各个方法创建时的应用。
query():

DataAbilityHelper helper = DataAbilityHelper.creator(this);

// 构造查询条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);

// 进行查询
ResultSet resultSet = helper.query(uri, columns, predicates);

// 处理结果
resultSet.goToFirstRow();
do {
    // 在此处理ResultSet中的记录;
} while(resultSet.goToNextRow());

insert():

DataAbilityHelper helper = DataAbilityHelper.creator(this);

// 构造插入数据
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.insert(uri, valuesBucket);

batchInsert():

DataAbilityHelper helper = DataAbilityHelper.creator(this);

// 构造插入数据
ValuesBucket[] values = new ValuesBucket[2];
values[0] = new ValuesBucket();
values[0].putString("name", "Tom");
values[0].putInteger("age", 12);
values[1] = new ValuesBucket();
values[1].putString("name", "Tom1");
values[1].putInteger("age", 16);
helper.batchInsert(uri, values);

delete():

DataAbilityHelper helper = DataAbilityHelper.creator(this);

// 构造删除条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
helper.delete(uri, predicates);

update():

DataAbilityHelper helper = DataAbilityHelper.creator(this);

// 构造更新条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.equalTo("userId", 102);

// 构造更新数据
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.update(uri, valuesBucket, predicates);

executeBatch():

DataAbilityHelper helper = DataAbilityHelper.creator(abilityObj, insertUri);

// 构造批量操作
ValuesBucket value1 = initSingleValue();
DataAbilityOperation opt1 = DataAbilityOperation.newInsertBuilder(insertUri).withValuesBucket(value1).build();
ValuesBucket value2 = initSingleValue2();
DataAbilityOperation opt2 = DataAbilityOperation.newInsertBuilder(insertUri).withValuesBucket(value2).build();
ArrayList<DataAbilityOperation> operations = new ArrayList<DataAbilityOperation>();
operations.add(opt1);
operations.add(opt2);
DataAbilityResult[] result = helper.executeBatch(insertUri, operations);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>