DevEco-PA(笔记)

一、介绍

 FA支持Page Ability:Page模板是FA唯一支持的模板,用于提供与用户交互的能力。一个Page实例可以包含一组相关页面,每个页面用一个AbilitySlice实例表示

PA支持Service AbilityData 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执行时

  1. 服务存在,则直接执行connect
  2. 若服务不存在,则会先先执行onstart.
  3. 返回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);
            }
        });

注意:

  1. 如果我们先点击按钮“连接服务”后,点击“断开服务”,会执行onStop方法。
  2. 但是,如果我们点击了“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);
        });

注意:

  1. 与 “断开服务”按钮一样(“断开服务”按钮仅能断开“连接服务”按钮对Service的调用),“终止服务”按钮也仅能断开“play”按钮对Service的调用
  2. 由于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 案例

  1. Service是用来提供后台服务的,而Data是用来访问文件数据与手机数据库(基本上,每一个应用都有一个数据库,该数据库一般是轻量级的,比如SQLite)。
  2. Data Ability间可以实现不同应用、设备间的数据共享。
  3. 注意,一般指的是本地数据库,服务器则只需要将需求按格式发送,后接收返回数据即可。

创建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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`vue-property-decorator` 是一个用于在 Vue 中使用装饰器语法的库,它提供了一些装饰器来简化 Vue 组件的开发过程。这些装饰器可以用于定义组件的属性、方法、计算属性、生命周期钩子等。 以下是一些常用的装饰器和它们的用法: - `@Component(options?: ComponentOptions)`:将一个类声明为一个 Vue 组件。可以传入一个可选的 `ComponentOptions` 对象来配置组件选项,例如 `template`、`props`、`computed` 等。 - `@Prop(options?: (Vue.PropOptions | Vue.Constructor[] | Vue.Constructor)[] | Vue.PropOptions)`:定义一个组件的 prop 属性。可以传入一个可选的 `PropOptions` 对象来配置 prop 的类型、默认值等。 - `@Watch(path: string, options?: WatchOptions)`:监听一个属性或表达式的变化,并在变化时执行相应的方法。可以传入一个可选的 `WatchOptions` 对象来配置监听选项,例如 `deep`、`immediate` 等。 - `@Emit(event?: string)`:将一个方法标记为触发事件的方法,并指定要触发的事件名。可以传入一个可选的事件名,默认为方法名。 - `@Ref(refKey?: string)`:获取子组件或 DOM 元素的引用,并将其赋值给指定的属性。可以传入一个可选的引用键,默认为属性名。 - `@Inject(key?: string | symbol)`:注入一个父组件提供的属性或方法。可以传入一个可选的注入键,默认为属性名。 - `@Provide(key?: string | symbol)`:在组件中提供属性或方法,以供子组件注入使用。可以传入一个可选的提供键,默认为属性名。 - `@Model(event?: string, options?: (PropOptions | Constructor[] | Constructor)[] | PropOptions)`:将一个 prop 属性设置为组件的 v-model。可以指定要触发的事件名和 prop 的配置选项。 这些装饰器可以与 `vue-class-component` 一起使用,帮助我们更清晰、简洁地定义 Vue 组件,并提供更好的类型支持和可读性。 希望这些笔记对你有所帮助!如有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值