Ability(能力)是鸿蒙软件的基础单位,一个应用可以包含多个Ability。鸿蒙支持应用以Ability为单位进行部署

Page Ability

Page提供页面管理和页面跳转的能力。例如外卖提供联系商家的业务入口,当用户使用此功能时,会跳转到通话应用的拨号页面。鸿蒙支持Page之间的跳转,并且指定跳转到某个AbilitySlice

创建

右键就可以进行创建,并且它的配置文件如下

{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [//路由名称
"action.system.home"
]
}
],
"orientation": "unspecified",
"name": "com.example.learn123.MainAbility",
"icon": "$media:icon",
"description": "$string:mainability_description",
"label": "$string:entry_MainAbility",//标题栏上的文字
"type": "page",//page、service、data三种
"launchType": "standard",//或者是singleton单例模式
"visible": true
}

页面导航

统一page页面导航

首先在Ability中注册路由,如

super.addActionRoute("abilityslice1", AbilitySlice1.class.getName());
super.addActionRoute("abilityslice2", AbilitySlice2.class.getName());

名字需要在config.json中的actions中注册

然后使用present进行跳转

Intent intent = new Intent();
present(new AbilitySlice1(), intent);

如果希望带返回值的跳转,则使用presentForResult

Intent intent = new Intent();
presentForResult(new AbilitySlice(), intent, 100);//最后一个参数是一个标识,标识是哪个页面发起请求

然后在跳转到的页面中
Intent intent = new Intent();
intent.setParam("back", "abcd");
setResult(intent);
terminate();//关闭本页面,跳转到先前的页面

然后原页面可以使用onResult处理返回值

@Override
protected void onResult(int requestCode, Intent resultIntent)
{
super.onResult(requestCode, resultIntent);
if(requestCode == 100)
{
btn2.append("resultIntent.getStringParam("back"));
}
}

鸿蒙管理不同的slice是通过栈的方式,跳转时如果在栈中已经存在,那么会弹出在他上面的。

例如:

Ability1进栈
Ability2进栈
Ability3进栈

现在栈中: Abiilty3 Ability2 Ability1

然后跳转到Ability1,栈的情况变为: Ability1
也就是说Ability1中的内容还是老的内容

Page之间的跳转

page之间的跳转可以通过startAbility()或startAbilityForResult()方法,获得返回结果的回调为onAbilityResult()

Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName("com.charjedu.router_demo")
.withAbilityName(".SecondAbility")
.build();
intent.setOperation(operation);
startAbility(intent);

此外,如果某个名字使用addActionRoute("name", ...)全局注册,那么我们可以直接跨Ability跳转

intent.setAction("secondslice1");
startAbility(intent);

生命周期

  • onStart(): 首次创建时触发
  • onActive(): 获得焦点时
  • onInactive(): 失去焦点时
  • onBackground(): 转到后台时
  • onStop(): 销毁时

AbilitySlice和page具有相同的生命周期。

Service Ability

启动Service有两种方式

  1. 通过startAbility()启动
  2. 通过connectAbility()启动,客户端可以通过disconnectAbility()断开连接

Service和Page不同的是它分为本地和远程两端。启动本地的方式和Page一样,启动远程的方式为

Operation operation = new Intent.OperationBuilder()
.withDeviceId("deviceId")
.withBundleName("com.huawei.himusic")
.withAbilityName"com.huawei.hiworld.ServiceAbility")
.withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
.build();
Intent intent = new Intent();
intent.setOperation(operation);
startAbility(intent);

远程连接Service

startAbility和stopAbility适合于时间很短并且交互量不大的场景,例如按钮的开关。而长时间则需要connect进行连接,如遥控器调节音量

步骤:

  1. 在config.json中申请权限,将reqPermissions添加到module中

    "module":{
    "package": "com.cangjie.jsabilitydemo",
    "name": ".MyApplication",
    "deviceType": [...],
    "distro": {"deliveryWithInstall": true},
    "abilities": [...],
    "js": [...],
    "reqPermissions": [{
    {
    //访问分布式数据
    "name": "ohos.permission.DISTRIBUTED_DATASYNC"
    },
    {
    //访问服务总线的绑定服务
    "name": "ohos.permission.servicebus.ACCESS_SERVICE"
    },
    {
    //访问服务总线的服务接入权限
    "name": "ohos.permission.servicebus.BIND_SERVICE"
    },
    {
    //访问分布式设备状态相关权限
    "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
    },
    {
    //分布式设备信息获取权限
    "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
    },
    {
    //获取Bundle信息权限
    "name": "ohos.permission.GET_BUNDLE_INFO"
    }
    }]//添加
    }

    同时我们还需要在MainAbility中获取权限

    @Override
    public void onStart(Intent intent)
    {
    requestPermissionsFromUser(new String[]{
    "ohos.permission.DISTRIBUTED_DATASYNC",
    "ohos.permission.servicebus.BIND_SERVICE",
    "ohos.permission.servicebus.ACCESS_SERVICE"
    ], 0);
    super.onStart(intent);
    super.setMainRoute(MainAbilitySlice.class.getName());
    }
  2. 获取设备信息

    public class DeviceUtil
    {
    public interface ISelectResult
    {
    void onSelectResult(String deviceId);
    }

    //获得设备列表并进行相关操作
    public static void getOnlineDeviceInfo(ISelectResult listener)
    {
    List<DeviceInfo> onlineDevices = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);

    if(onlineDevices.isEmpty())
    {
    listener.onSelectResult(null);
    return;
    }
    int numDevices = onlineDevices.size();
    ArrayList<String> deviceIds = new ArrayList<>(numDevices);
    ArrayList<String> deviceNames = new ArrayList<>(numDevices);
    onlineDevices.forEach((device)->{
    deviceIds.add(device.getDeviceId());
    deviceNames.add(device.getDeviceName());//获得设备名
    });
    String selectDeviceId = deviceIds.get(0);
    listener.onSelectResult(selectDeviceId);//选择第一个设备
    }
    }

    //使用
    DeviceUtil.getOnlineDeviceInfo(new DeviceUtil.ISelectResult(){
    @Override
    public void onSelectResult(String deviceId)
    {
    Intent intent = new Intent();
    Operation operation = new Intent.OperationBuilder()
    .withDeviceId(deviceId)
    .withBundleName("com.charjedu.ability_demo")
    .withAbilityName(".RemoteServiceAbility")
    .build();
    intnet.setOperation(operation);
    connectAbility(intent, conn);
    }
    });
  3. 创建本地代理

    public class MyRemoteProxy implements IRemoteBroker
    {
    private static final int COMMAND_PLUS = IRemoteObject.MAX_TRANSACTION_ID;
    private static final int ERR_OK = 0;
    private final IRemoteObject remote;

    public MyRemoteProxy(IRemoteObject remote)
    {
    this.remote = remote;
    }

    //返回远程对象,需要实现
    @Override
    public IRemoteObject asObject()
    {
    return remote;
    }

    //自定义的Service Ability方法
    public int plus(int a, int b)
    {
    MessageParce1 data = MessageParce1.obtain();
    data.writeInt(a);
    data.writeInt(b);

    MessageParce1 reply = MessageParce1.obtain();
    //同步接收
    MessageOption option = new MessageOption(MessageOption.TF_SYNC);
    int result = 0;
    try
    {
    //发送信息到远程,reply是远程返回的结果
    //COMMAND_PLUS是用来标识当前是什么操作
    remote.sendRequest(COMMAND_PLUS, data, reply, option);
    int ec = reply.readInt();
    if(ec != ERR_OK)
    {
    throw new RemoteException();
    }
    result = reply.readInt();
    }catch(RemoteException e)
    {
    e.printStackTrace();
    }
    return result;
    }
    }

    IRemoteObject是connectAbility的回调中

    connectAbility(new Intent(), new IAbilityConnection() {
    @Override
    public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {

    }

    @Override
    public void onAbilityDisconnectDone(ElementName elementName, int i) {

    }
    });
  4. 创建远程代理

    public class MyRemote extends RemoteObject implements IRemoteBroker
    {
    private static final int COMMAND_PLUS = IRemoteObject.MAX_TRANSACTION_ID;
    private static final int ERR_OK = 0;
    private static final int ERROR = -1

    public MyRemote()
    {
    super("my_remote");
    }

    @Override
    public IRemoteObject asObject()
    {
    return this;
    }

    @Override
    public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option)throws RemoteException
    {
    if(code != COMMAND_PLUS)
    {
    reply.writeInt(ERR_OK);
    return false;
    }
    int v1 = data.readInt();
    int v2 = data.readInt();
    int rs = v1 + v2;
    reply.writeInt(ERR_OK);
    reply.writeInt(rs);
    return true;
    }
    }
  5. 创建远程Service Ability
    public class RemoteServiceAbility extends Ability
    {
    private MyRemote remote = new MyRemote();
    //返回远程对象
    @Override IRemoteObject onConnect(Intent intent)
    {
    return remote;
    }
    }

    前台Service Ability

一般情况下,Service都是在后台运行的,并且后台的优先级都是比较低的,资源不足时可能回收后台运行的Service

如果希望应用一直运行,便需要使用前台Service。前台Service会有一个保持运行的图标在前台显示。

想要启动前台服务,需要开启ohos.permission.KEEP_BACKGROUND_RUNNING。并且在配置文件中添加backgroundModes参数,最后在onStart中调用keepBackgroundRunning并且在onStop中调用cancelBackgroundRunning停止前台Service

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

NotificationRequest request = new NotificationRequest(1005);
NotificationRequest.NotificationNormalContent content = new NotificationRequest.NotificationNormalContent();
content.setTitle("title").setText("前台服务通知");
NotificationRequest.NotificationContent notificationContent = new NotificationRequest.NotificationContent(content);
request.setContent(notificationContent);
keepBackgroundRunning(1005, request);
}

Data Ability

url

鸿蒙url的组成为Scheme://[authority]/[path][?query][#fragment]

  • scheme: 协议名,固定为dataability
  • authority: 设备id,如果为跨设备场景,则为目标设备的id,如果是本地设备,则无需填写
  • path: 路径

DataAbilityHelper

DataAbilityHelper可以访问当前应用和其他应用提供的共享数据,它需要ohos.permission.READ_USER_STORAGEohos.permission.WRITE_USER_STORAGE权限。

  • DataAbilityHelper.creator(this): 创建DataAbilityHelper
  • openFile(Uri, String mode): 打开文件,mode有r、w、rw、wt(覆盖写)、wa(追加写)、rwt
  • query(Uri uri, String[] columns, DataAbilityPerdicates): 查询
  • insert(Uri uri, ValuesBucket value): 向数据库中插入单条数据
  • batchInsert(Uri uri, ValuesBucket[] values)
  • delete(Uri uri, DataAbilityPredicates predicates)
  • update(…)
  • executeBatch(ArrayList\ operations): 批量操作