一、介绍
FA支持Page Ability:Page模板是FA唯一支持的模板,用于提供与用户交互的能力。一个Page实例可以包含一组相关页面,每个页面用一个AbilitySlice实例表示
PA支持Service Ability和Data Ability:
- Service模板:用于提供后台运行任务的能力。
- Data模板:用于对外部提供统一的数据访问抽象。
如我前面笔记所写,FA是有交互界面的,而PA没有交互界面,是用于后台执行操作。
二、编写一个Service PA 的用例
Service是单实例的。在一个设备上,相同的Service只会存在一个实例。如果多个Ability共用这个实例,只有当与Service绑定的所有Ability都退出后,Service才能够退出。由于Service是在主线程里执行的,因此,如果在Service里面的操作时间过长,开发者必须在Service里创建新的线程来处理(详见线程间通信),防止造成主线程阻塞,应用程序无响应。
换言之
- 创建:同一个Service只会出现一个(类似单态)
- 调用:其他FA也可以调用同一个Service
- 关闭:所有的FA都调用完成后,Service会被关闭,或用户手动关闭
现模拟一个音乐播放器
创建播放列表与播放页面
将Main布局的内容作如下修改
<?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:alignment="center"
ohos:orientation="vertical">
<Text
ohos:id="$+id:songList"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="歌曲列表"
ohos:text_size="20vp"
/>
<Text
ohos:id="$+id:musicText1"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="歌名1"
ohos:text_size="20vp"
/>
<Text
ohos:id="$+id:musicText2"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="歌名2"
ohos:text_size="20vp"
/>
<Text
ohos:id="$+id:musicText3"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="歌名3"
ohos:text_size="20vp"
/>
</DirectionalLayout>
随后新增一页面切片
为它添加别名,并在MainAbility中注册
package com.example.myapplication;
import com.example.myapplication.slice.MainAbilitySlice;
import com.example.myapplication.slice.PlayAbilitySlice;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
public class MainAbility extends Ability {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
super.addActionRoute("play_slice",PlayAbilitySlice.class.getName());
}
}
以及对应的布局:
其布局代码如下:
<?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:orientation="vertical">
<Text
ohos:id="$+id:player"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="音乐播放页面"
ohos:text_size="20vp"
/>
<Text
ohos:id="$+id:musicName"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="未知歌名1"
ohos:text_size="20vp"
/>
<Button
ohos:id="$+id:play"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="play"
ohos:text_size="30fp"
ohos:background_element="#0aabbc"
/>
</DirectionalLayout>
先修改MainAbilitySlice:
package com.example.myapplication.slice;
import com.example.myapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
//点击歌曲名称跳转到歌曲播放页面,添加监听器
Text text1 = (Text) findComponentById(ResourceTable.Id_musicText1);
Text text2 = (Text) findComponentById(ResourceTable.Id_musicText2);
Text text3 = (Text) findComponentById(ResourceTable.Id_musicText3);
//点击后执行跳转
text1.setClickedListener(component -> navigateToPlay(component));
text2.setClickedListener(component -> navigateToPlay(component));
text3.setClickedListener(component -> navigateToPlay(component));
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
//跳转方法实现
private void navigateToPlay(Component component){
Text text = (Text) component;
String musicName = text.getText();
Intent intent = new Intent();
intent.setParam("musicName",musicName);
this.present(new PlayAbilitySlice(),intent);
}
}
接着修改PlayAbilitySlice页面:
package com.example.myapplication.slice;
import com.example.myapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Text;
public class PlayAbilitySlice extends AbilitySlice {
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
this.setUIContent(ResourceTable.Layout_ability_main_play);
if(intent != null){
String musicName = (String) intent.getParams().getParam(("musicName"));
Text text = (Text)findComponentById(ResourceTable.Id_musicName);
text.setText(musicName);
}
}
}
至此完成准备工作
创建Service
选择Service Ability
enable background mode:允许的后台模式,需要在此勾选我们需要的权限
(此处我们模拟的权限已勾选)
通过IDE注册的PA会自动在config.json中注册,如下图,我们可以与主页做个比较
音频相关代码可以参考开发手册《音频开发概述-音频-媒体-开发-HarmonyOS应用开发》
MusicServiceAbility
package com.example.myapplication;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.media.audio.AudioRenderer;
import ohos.media.audio.AudioRendererInfo;
import ohos.media.audio.AudioStreamInfo;
import ohos.rpc.IRemoteObject;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
public class MusicServiceAbility extends Ability {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
private AudioRenderer audioRenderer;//音频播放器资源
@Override
// 当第一次创建时,会被调用
public void onStart(Intent intent) {
HiLog.error(LABEL_LOG, "MusicServiceAbility::onStart");
super.onStart(intent);
System.out.println("onStart");
//调用音频设备
//音频信息
AudioStreamInfo audioStreamInfo = new AudioStreamInfo.Builder().sampleRate(44100) // 44.1kHz
.audioStreamFlag(AudioStreamInfo.AudioStreamFlag.AUDIO_STREAM_FLAG_MAY_DUCK) // 混音
.encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT) // 16-bit PCM
.channelMask(AudioStreamInfo.ChannelMask.CHANNEL_OUT_STEREO) // 双声道输出
.streamUsage(AudioStreamInfo.StreamUsage.STREAM_USAGE_MEDIA) // 媒体类音频
.build();
//播放的参数
AudioRendererInfo audioRendererInfo = new AudioRendererInfo.Builder().audioStreamInfo(audioStreamInfo)
.audioStreamOutputFlag(AudioRendererInfo.AudioStreamOutputFlag.AUDIO_STREAM_OUTPUT_FLAG_DIRECT_PCM)
.bufferSizeInBytes(100)//设定缓存大小
.isOffload(false)// false表示分段传输buffer并播放,true表示整个音频流一次性传输到HAL层播放
.build();
audioRenderer = new AudioRenderer(audioRendererInfo,AudioRenderer.PlayMode.MODE_STREAM );//通过流的方式播放资源
audioRenderer.setVolume(1.0f);
audioRenderer.setSpeed(1.0f);
}
@Override
public void onBackground() {
super.onBackground();
HiLog.info(LABEL_LOG, "MusicServiceAbility::onBackground");
}
@Override
//销毁时,调用该方法
public void onStop() {
super.onStop();
HiLog.info(LABEL_LOG, "MusicServiceAbility::onStop");
System.out.println("onStop");
audioRenderer.release();//释放音频播放器资源
}
@Override
//先执行onStart,后执行onCommand。onStart仅在创建时初始化,onCommand则每一个startAbility都会执行一次。
public void onCommand(Intent intent, boolean restart, int startId) {
System.out.println("onCommand");
audioRenderer.start();
}
@Override
//当应用连接到此服务时,会触发执行该方法
public IRemoteObject onConnect(Intent intent) {
return null;
}
@Override
//当应用断开与此服务的连接时,会触发执行该方法
public void onDisconnect(Intent intent) {
}
}
启动Service
继续修改PlayAbilitySlilce页面:
package com.example.myapplication.slice;
import com.example.myapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
public class PlayAbilitySlice extends AbilitySlice {
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
this.setUIContent(ResourceTable.Layout_ability_main_play);
if(intent != null){
String musicName = (String) intent.getParams().getParam(("musicName"));
Text text = (Text)findComponentById(ResourceTable.Id_musicName);
text.setText(musicName);
}
Button but = (Button)findComponentById(ResourceTable.Id_play);
but.setClickedListener(component -> {
//启动Service
Intent intent1 = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")//当前设备
.withBundleName("com.example.myapplication")//当前引用名,config.json下可找到
.withAbilityName("com.example.myapplication.MusicServiceAbility")//使用的Ability名,config.json下可找到
.build();
intent1.setOperation(operation);
//启动服务
this.startAbility(intent1);
} );
}
}
若有打印不了日志,信息无法输出等问题,建议不要使用本地模拟,而是使用远程模拟(DevEco3.2,笔者的本地模拟器无法正确执行,换用远程后可以执行)。
连接Service
如果Service需要与Page Ability或其他应用的Service Ability进行交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。
connect执行时
- 服务存在,则直接执行connect
- 若服务不存在,则会先先执行onstart.
- 返回Service远程对象
为PlayAbilitySlice页面布局添加一个新的按钮作为连接操作按钮
<?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:orientation="vertical">
<Text
ohos:id="$+id:player"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="音乐播放页面"
ohos:text_size="20vp"
/>
<Text
ohos:id="$+id:musicName"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="未知歌名1"
ohos:text_size="20vp"
/>
<Button
ohos:id="$+id:play"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="play"
ohos:text_size="30fp"
ohos:background_element="#0aabbc"
/>
<Button
ohos:id="$+id:con"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="连接服务"
ohos:text_size="30fp"
ohos:background_element="#0aabbc"
/>
</DirectionalLayout>
在为其添加代码
package com.example.myapplication.slice;
import com.example.myapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.IAbilityConnection;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import ohos.bundle.ElementName;
import ohos.rpc.IRemoteObject;
public class PlayAbilitySlice extends AbilitySlice {
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
this.setUIContent(ResourceTable.Layout_ability_main_play);
if(intent != null){
String musicName = (String) intent.getParams().getParam(("musicName"));
Text text = (Text)findComponentById(ResourceTable.Id_musicName);
text.setText(musicName);
}
Button but = (Button)findComponentById(ResourceTable.Id_play);
but.setClickedListener(component -> {
//启动Service
Intent intent1 = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")//当前设备
.withBundleName("com.example.myapplication")//当前引用名,config.json下可找到
.withAbilityName("com.example.myapplication.MusicServiceAbility")//使用的Ability名,config.json下可找到
.build();
intent1.setOperation(operation);
//启动服务
this.startAbility(intent1);
} );
//连接服务
Button but2 = (Button)findComponentById(ResourceTable.Id_con);
IAbilityConnection iAbilityConnection = new IAbilityConnection() {
@Override
public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
//连接到目标ServiceAbilit成功后的回调方法
//向Service发送连接请求后,就会执行oConect方法,onConnect就会返回我们要连接的Service的远程对象(IRemoteObject)
//i就是结果号
//elementName就是我们AbilityName的name
System.out.println(elementName.getAbilityName());
}
@Override
public void onAbilityDisconnectDone(ElementName elementName, int i) {
//失败
}
};
//发送连接请求
but2.setClickedListener(component ->
{
Intent intent2 = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName("com.example.myapplication")
.withAbilityName("com.example.myapplication.MusicServiceAbility")
.build();
intent2.setOperation(operation);
this.connectAbility(intent2,iAbilityConnection);
});
}
}
PA里面connect也要返回一个对象
@Override
//当应用连接到此服务时,会触发执行该方法
public IRemoteObject onConnect(Intent intent) {
System.out.println("onConnect");
return new LocalRemoteObject() {};//是一个抽象类(空的),因此带括号表示实现。
}
断开Service
为PlayAbilitySlice页面布局添加一个新的按钮,用于断开
<DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal"
ohos:alignment="center"
ohos:top_margin="20vp"
>
<Button
ohos:id="$+id:discon"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="断开服务"
ohos:text_size="30fp"
ohos:background_element="#0aabbc"
/>
</DirectionalLayout>
为该页面添加按钮3的监听器
Button but3 = (Button)findComponentById(ResourceTable.Id_discon);
but3.setClickedListener(component -> {
if(iAbilityConnection!=null){
this.disconnectAbility(iAbilityConnection);
}
});
注意:
- 如果我们先点击按钮“连接服务”后,点击“断开服务”,会执行onStop方法。
- 但是,如果我们点击了“play”按钮,也点击按钮“连接服务”(与顺序无关)。则我们点击“断开服务”,不会执行onstop。因为我们仅解除了“连接服务”按钮对其的调用,未解除“play”按钮对其的调用。
终止服务
为PlayAbilitySlilce页面布局添加一个新的按钮作为终止服务
<DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal"
ohos:alignment="center"
ohos:top_margin="20vp"
>
<Button
ohos:id="$+id:stop"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="终止服务"
ohos:text_size="30fp"
ohos:background_element="#0aabbc"
/>
</DirectionalLayout>
再为该按钮添加对应的监听器
Button but4 = (Button) findComponentById(ResourceTable.Id_stop);
but4.setClickedListener(component -> {
Intent intent4 = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName("com.example.myapplication")
.withAbilityName("com.example.myapplication.MusicServiceAbility")
.build();
intent4.setOperation(operation);
//终止服务
this.stopAbility(intent4);
});
注意:
- 与 “断开服务”按钮一样(“断开服务”按钮仅能断开“连接服务”按钮对Service的调用),“终止服务”按钮也仅能断开“play”按钮对Service的调用
- 由于onStop方法仅在Service不再被调用时执行,因此,两个按钮单独使用都不一定会让onStop方法执行。
三、前台Service的使用
指的是当我们启动一个服务后,该服务会有一个通知或标志,展示在手机的状态栏,用来告诉用户我们启动了一个Servcie。
设置前台Service
在Service的onstart中,将当前ServcieAbility绑定到系统的状态栏中。
《前台Service-Service Ability-Ability-Ability框架-开发-HarmonyOS应用开发》
我们先对PlayAbilitySlice的部分进行改动,让其传递歌曲名给Service
public class PlayAbilitySlice extends AbilitySlice {
private String musicName = "";//全局用来向通知传参。
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
this.setUIContent(ResourceTable.Layout_ability_main_play);
if(intent != null){
musicName = (String) intent.getParams().getParam("musicName");
Text text = (Text)findComponentById(ResourceTable.Id_musicName);
text.setText(musicName);
}
Button but = (Button)findComponentById(ResourceTable.Id_play);
but.setClickedListener(component -> {
//启动Service
Intent intent1 = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")//当前设备
.withBundleName("com.example.myapplication")//当前引用名,config.json下可找到
.withAbilityName("com.example.myapplication.MusicServiceAbility")//使用的Ability名,config.json下可找到
.build();
intent1.setParam("musicName",musicName);
intent1.setOperation(operation);
//启动服务
this.startAbility(intent1);
} );
//后面代码此处省略
}
对Service的onStart添加代码:
package com.example.myapplication;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.LocalRemoteObject;
import ohos.aafwk.content.Intent;
import ohos.event.notification.NotificationRequest;
import ohos.media.audio.AudioRenderer;
import ohos.media.audio.AudioRendererInfo;
import ohos.media.audio.AudioStreamInfo;
import ohos.rpc.IRemoteBroker;
import ohos.rpc.IRemoteObject;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
public class MusicServiceAbility extends Ability {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
private AudioRenderer audioRenderer;//音频播放器资源
@Override
// 当第一次创建时,会被调用
public void onStart(Intent intent) {
HiLog.error(LABEL_LOG, "MusicServiceAbility::onStart");
super.onStart(intent);
System.out.println("onStart");
//调用音频设备
//音频信息
AudioStreamInfo audioStreamInfo = new AudioStreamInfo.Builder().sampleRate(44100) // 44.1kHz
.audioStreamFlag(AudioStreamInfo.AudioStreamFlag.AUDIO_STREAM_FLAG_MAY_DUCK) // 混音
.encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT) // 16-bit PCM
.channelMask(AudioStreamInfo.ChannelMask.CHANNEL_OUT_STEREO) // 双声道输出
.streamUsage(AudioStreamInfo.StreamUsage.STREAM_USAGE_MEDIA) // 媒体类音频
.build();
//播放的参数
AudioRendererInfo audioRendererInfo = new AudioRendererInfo.Builder().audioStreamInfo(audioStreamInfo)
.audioStreamOutputFlag(AudioRendererInfo.AudioStreamOutputFlag.AUDIO_STREAM_OUTPUT_FLAG_DIRECT_PCM)
.bufferSizeInBytes(100)//设定缓存大小
.isOffload(false)// false表示分段传输buffer并播放,true表示整个音频流一次性传输到HAL层播放
.build();
audioRenderer = new AudioRenderer(audioRendererInfo,AudioRenderer.PlayMode.MODE_STREAM );//通过流的方式播放资源
audioRenderer.setVolume(1.0f);
audioRenderer.setSpeed(1.0f);
//将当前前ServiceAbility绑定到系统状态栏
//创建通知,其中1005为notificationId,即我们当前Service通知的ID
NotificationRequest notificationrequest = new NotificationRequest(1005);
//构造普通通知的内容
NotificationRequest.NotificationNormalContent content = new NotificationRequest.NotificationNormalContent();
String musicName = (intent == null)?"没有歌曲在播放:":"正在播放"+(String) intent.getParams().getParam("musicName");
content.setTitle("音乐播放器").setText(musicName);
//将普通通知,初始化通知
NotificationRequest.NotificationContent notificationContent = new NotificationRequest.NotificationContent(content);
//将通知放入通知请求中
notificationrequest.setContent(notificationContent);
//绑定通知,1005为创建通知时传入的notificationId,使得其被加入状态栏中
keepBackgroundRunning(1005, notificationrequest);
}
//后续代码此处省略
}
对于keepBackgroundRunning,IDE可以帮我们生成申请的代码
再在onStop处,为其添加
//销毁时,调用该方法
public void onStop() {
super.onStop();
HiLog.info(LABEL_LOG, "MusicServiceAbility::onStop");
System.out.println("onStop");
audioRenderer.release();//释放音频播放器资源
cancelBackgroundRunning();
}
在config.json中我们可以看到reqPermissions中(整个软件需要的权限)。
详见《权限开发概述-权限-安全-开发-HarmonyOS应用开发》。
我们这里用最简格式为Service申请权限即可。
此外,若需已实现的可用的整个播放代码,可以参考官方给出的详细实例
疑问
我们在前台Service中,想要让Page把歌曲名称传给Service,但是通过intent传输会发现,在Service中,intent会变为空的。
四、Data PA 案例
- Service是用来提供后台服务的,而Data是用来访问文件数据与手机数据库(基本上,每一个应用都有一个数据库,该数据库一般是轻量级的,比如SQLite)。
- Data Ability间可以实现不同应用、设备间的数据共享。
- 注意,一般指的是本地数据库,服务器则只需要将需求按格式发送,后接收返回数据即可。
创建Data Ability
文件的打开
现在创建一个Data用于读取文件(歌曲信息)
openFile:若我们读取的数据库,则不需要该方法。
我们可以将官方给的代码直接复制到该方法中。《创建Data-Data Ability-Ability-Ability框架-开发-HarmonyOS应用开发》
对于文件流的操作方法可以参考开发手册
/*
* uri:指的是路径,可以在config.json中找到,指的是访问我们这个DataAbility的路径
比如当前的为:dataability://ip:port(设备IP与设备端口,本地设备可省略)/com.example.myapplication.FileDataAbility
* mode:指的是权限,“r”(读), “w”(写), “rw”(读写)等
* */
@Override
public FileDescriptor openFile(Uri uri, String mode) {
File file = new File(uri.getDecodedPathList().get(0)); //get(0)是获取URI完整字段中查询参数字段。
if (mode == null || !"rw".equals(mode)) {
file.setReadOnly();
}
FileInputStream fileIs = null;
FileDescriptor fd = null;
try {
fileIs = new FileInputStream(file);
fd = fileIs.getFD();
} catch (IOException e) {
HiLog.info(LABEL_LOG, "failed to getFD");
}
// 绑定文件描述符
return MessageParcel.dupFileDescriptor(fd);
}//文件读取,数据库则不需要
数据库的DAO
新建一个Data,通常一个Data对应一张表,你可以更据自己的需求进行修改。
鸿蒙有两种连接数据库方式OrmContext(对象数据库),RdbStore(关系数据库) ,本文使用其中的RdbStore ,另一种可自行查询手册。
我们需要重写所有的增删改查。
package com.example.myapplication;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.data.DatabaseHelper;
import ohos.data.dataability.DataAbilityUtils;
import ohos.data.rdb.*;
import ohos.data.resultset.ResultSet;
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 UserDataAbility extends Ability {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
//RdbStore数据的操作对象
private RdbStore rdbStore;
//关联数据文件配置,UserStore.db是我们的数据库文件
private StoreConfig config = StoreConfig.newDefaultConfig("UserStore.db");
//每当连接操作完成后,进行回调
//其中的方法我们可以用来对数据库进行一个最开始的操作。
private RdbOpenCallback callback = new RdbOpenCallback() {
@Override
public void onCreate(RdbStore rdbStore) {//当连接创建完成时操作
rdbStore.executeSql(//此处检查数据库是否存在
"create table if not exists " +
"users(userID integer primary key autoincrement," +
"userName text," +
"userTel text not null unique," +
"userAddr text");
}
@Override
public void onUpgrade(RdbStore rdbStore, int i, int i1) {//当连接更新时执行的操作
}
};
@Override
public void onStart(Intent intent) {
super.onStart(intent);
HiLog.info(LABEL_LOG, "UserDataAbility onStart");
try {
DatabaseHelper helper = new DatabaseHelper(this);//初始化与数据库的连接
//数据库文件配置,版本号
rdbStore = helper.getRdbStore(config,1,callback);
}
catch (RdbException rdbException){
rdbException.printStackTrace();
}
catch(Exception exception){
exception.printStackTrace();
}
}
//column:记录需要返回的属性,类似于select a,b,c
//predicates:记录的查询语句,类似于where a = 1
@Override
public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
//一般是“dataability://com.example.myapplication.slice.UserDataAbility/表名”,因此获取最后一个即可
String path = uri.getLastPath();
ResultSet resultSet = null;
try {
RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, path);
resultSet = rdbStore.query(rdbPredicates, columns);
}catch (RdbException rdbException){
rdbException.printStackTrace();
}finally {
return resultSet;
}
}
//value这是映射关系,比如:value.putString("属性名","值");
@Override
public int insert(Uri uri, ValuesBucket value) {
int i = -1;
String path = uri.getLastPath();
try{
rdbStore.insert(path,value);
i = 0 ;
}catch (RdbException rdbException){
i = -1;
rdbException.printStackTrace();
}finally {
return i;
}
}
@Override
public int delete(Uri uri, DataAbilityPredicates predicates) {
int i = -1;
String path = uri.getLastPath();
try{
RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, path);
i = rdbStore.delete(rdbPredicates);
}catch (RdbException rdbException){
i = -1;
rdbException.printStackTrace();
}
finally {
return i;
}
}
@Override
public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
int i = -1;
String path = uri.getLastPath();
try{
RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, path);
i = rdbStore.update(value,rdbPredicates);
}catch (RdbException rdbException){
i = -1;
rdbException.printStackTrace();
}
finally {
return i;
}
}
@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;
}
}
创建MainAbility
我们做一个联系人列表案例.
创建如下页面以及布局
现在为主页面布局
<?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:alignment="center"
ohos:orientation="vertical">
<Text
ohos:id="$+id:app"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="通讯录"
ohos:text_size="20vp"
/>
<Button
ohos:id="$+id:add"
ohos:width="match_content"
ohos:height="match_content"
ohos:text="添加联系人"
ohos:layout_alignment="horizontal_center"
ohos:text_size="20vp"
ohos:background_element="#00aacc"
ohos:top_margin="20fp"
/>
<Button
ohos:id="$+id:queryList"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="联系人列表"
ohos:text_size="20vp"
/>
</DirectionalLayout>
主页代码
package com.example.myapplication.slice;
import com.example.myapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
//点击“保存联系人”按钮,将输入的信息存储到users表中
Button saveBtn = (Button) findComponentById(ResourceTable.Id_add);
saveBtn.setClickedListener(component -> present(new UserAddAbility(),new Intent()));
//点击“联系人列表”按钮
Button queryList = (Button) findComponentById(ResourceTable.Id_queryList);
queryList.setClickedListener(component -> present(new UserListAbility(),new Intent()));
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
UserAddAbility 页面
布局
<?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:alignment="center"
ohos:orientation="vertical">
<Text
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="添加联系人"
ohos:text_size="20vp"
/>
<TextField
ohos:id="$+id:Id_Name"
ohos:height="30vp"
ohos:width="match_parent"
ohos:padding="3fp"
ohos:text_size="15vp"
ohos:top_margin="10fp"
ohos:background_element="lightblue"
/>
<TextField
ohos:id="$+id:Addr"
ohos:height="30vp"
ohos:width="match_parent"
ohos:padding="3fp"
ohos:text_size="15vp"
ohos:top_margin="10fp"
ohos:background_element="lightblue"
/>
<TextField
ohos:id="$+id:Phone"
ohos:height="30vp"
ohos:width="match_parent"
ohos:padding="3fp"
ohos:text_size="15vp"
ohos:top_margin="10fp"
ohos:background_element="lightblue"
/>
<Button
ohos:id="$+id:add"
ohos:width="match_content"
ohos:height="match_content"
ohos:text="保存联系人"
ohos:text_size="20vp"
ohos:background_element="#00aacc"
ohos:top_margin="20fp"
/>
</DirectionalLayout>
该页代码,
我们会在此页调用数据的读写
package com.example.myapplication.slice;
import com.example.myapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.TextField;
import ohos.data.rdb.ValuesBucket;
import ohos.hiviewdfx.HiLog;
import ohos.utils.net.Uri;
public class UserAddAbility extends AbilitySlice {
private DataAbilityHelper dataAbilityHelper;//data 连接对象
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_user_add);
//保存输入信息
Button saveBtn = (Button)findComponentById(ResourceTable.Id_add);
//获取输入信息
TextField name =(TextField) findComponentById(ResourceTable.Id_Id_Name);
TextField addr =(TextField) findComponentById(ResourceTable.Id_Addr);
TextField phone =(TextField) findComponentById(ResourceTable.Id_Phone);
saveBtn.setClickedListener(component -> {
//获取数据
String Name = name.getText();
String Phone = phone.getText();
String Addr = addr.getText();
//访问调用UserDataAbility完成保存
//我们调用的方法是插入,因此,此处用插入
//uri即我们之前注释所解释的,代表我们调用的Data以及需要的表,参数可以参考我们重写的部分
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("userName",Name);
valuesBucket.putString("userTel",Phone);
valuesBucket.putString("userAddr",Addr);
int i = 0;
try {
dataAbilityHelper = DataAbilityHelper.create(this);//创建对象
i = dataAbilityHelper.insert(Uri.parse("dataability:///com.example.myapplication.slice.UserDataAbility/users"), valuesBucket);
}catch (DataAbilityRemoteException dataAbilityRemoteException) {
dataAbilityRemoteException.printStackTrace();
}catch (Exception exception){
exception.printStackTrace();
}
finally {
System.out.println("result:"+i);
}
});
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
UseListAbility页面
我们会用之前组件中用到的ListContainer列表容器组件,用来动态生成内容。用法见前文《DevEco组件(笔记)_yjx23332的博客-CSDN博客》
我们用UserInfo来存储数据库读出来的数据
package com.example.myapplication.JavaBean;
public class UserInfo {
private String name;
private String addr;
private String phone;
public void setName(String name){
this.name = name;
}
public void setAddr(String addr){
this.addr = addr;
}
public void setPhone(String phone){
this.phone = phone;
}
public String getName(){
return this.name;
}
public String getPhone(){
return this.phone;
}
public String getAddr(){
return this.addr;
}
}
我们每一个联系人的布局则是
<?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:alignment="center"
ohos:orientation="vertical">
<Text
ohos:id="$+id:infoText"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="center"
ohos:text=""
ohos:text_size="10vp"
/>
</DirectionalLayout>
在设置一个provider管理所有信息
其实现如下
package com.example.myapplication.Provider;
import com.example.myapplication.JavaBean.UserInfo;
import ohos.agp.components.*;
import com.example.myapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import java.util.ArrayList;
public class ItemProvider extends BaseItemProvider {
private ArrayList<UserInfo> list;//用于存储所有Item对象
private AbilitySlice as;
public ItemProvider( ArrayList<UserInfo> list,AbilitySlice as) {
this.list = list;
this.as = as;
}
public ArrayList<UserInfo> getList(){
return list;
}
public void setList(ArrayList<UserInfo> list){
this.list = list;
}
public AbilitySlice getAs(AbilitySlice as){
return as;
}
public void setAs(AbilitySlice as){
this.as = as;
}
//返回总数
@Override
public int getCount() {
return list.size();
}
//索引
@Override
public Object getItem(int i) {
if(list!=null&&i>=0&&i<list.size()){
return list.get(i);
}
return null;
}
//返回某一项的ID
@Override
public long getItemId(int i) {
return i;
}
@Override
public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
DirectionalLayout dl;
if(component!=null){
dl = (DirectionalLayout) component;
}
else{
dl =(DirectionalLayout)LayoutScatter.getInstance(as).parse(ResourceTable.Layout_ability_user_list,null,false);
}
UserInfo userInfo = list.get(i);
//将布局中的text拿出
Text text = (Text) dl.findComponentById(ResourceTable.Id_infoName);
//设置文本内容
text.setText(userInfo.getName());
text = (Text) dl.findComponentById(ResourceTable.Id_infoAddr);
text.setText(userInfo.getAddr());
text = (Text) dl.findComponentById(ResourceTable.Id_infoTel);
text.setText(userInfo.getAddr());
return dl;
}
}
UserListAbility页面
布局
<?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:alignment="center"
ohos:orientation="vertical">
<Text
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:text="联系人列表"
ohos:text_size="20vp"
/>
<ListContainer
ohos:id="$+id:listContainer"
ohos:height="match_content"
ohos:width="match_content"
ohos:layout_alignment="horizontal_center"/>
</DirectionalLayout>
其页面代码
package com.example.myapplication.slice;
import com.example.myapplication.Provider.ItemProvider;
import com.example.myapplication.JavaBean.UserInfo;
import com.example.myapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.aafwk.content.Intent;
import ohos.agp.components.ListContainer;
import ohos.data.dataability.DataAbilityPredicates;
import ohos.utils.net.Uri;
import ohos.data.resultset.ResultSet;
import java.util.ArrayList;
public class UserListAbility extends AbilitySlice {
private DataAbilityHelper dataAbilityHelper;
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_user_list_title);
ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_listContainer);
ArrayList<UserInfo> dataList = getData();
ItemProvider itemProviderp = new ItemProvider(dataList,this);
listContainer.setItemProvider(itemProviderp);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
public ArrayList<UserInfo> getData(){
ArrayList<UserInfo> list = new ArrayList<>();
String columns[] = {"userName","userTel","userAddr"};
DataAbilityPredicates dataAbilityPredicates = new DataAbilityPredicates();
ResultSet resultSet = null;
try {
dataAbilityHelper = DataAbilityHelper.create(this);
resultSet = dataAbilityHelper.query(Uri.parse("dataability:///com.example.myapplication.slice.UserDataAbility/users"), columns, dataAbilityPredicates);
}catch(DataAbilityRemoteException dataAbilityRemoteException){
dataAbilityRemoteException.printStackTrace();
}catch (Exception exception){
exception.printStackTrace();
}
int count = resultSet.getRowCount();
if(count == 0){
System.out.println("无数据");
}
else{
resultSet.goToFirstRow();//从第一行开始
do{
UserInfo userInfo = new UserInfo();
userInfo.setName(resultSet.getString(0));
userInfo.setPhone(resultSet.getString(1));
userInfo.setAddr(resultSet.getString(2));
list.add(userInfo);
}while(resultSet.goToNextRow());
}
return list;
}
}
参考文献:
1.参考鸿蒙开发手册《HarmonyOS系统开发指导_HarmonyOS系统能力API参考 - HarmonyOS应用开发官网》。
2.千锋教育-《全网首套鸿蒙HarmonyOS 2.0应用开发实战教程》全网首套鸿蒙HarmonyOS 2.0应用开发实战教程丨锋迷商城项目,鸿蒙系统APP开发入门+实战教学_哔哩哔哩_bilibili