文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JS FA 调用 PA—展示本地相册图片

2024-12-02 05:47

关注

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

一、效果展示

二、项目介绍

\qquad本项目以 ArkUI(JS)开发界面,利用 JS FA 调用 JAVA PA 的接口功能,通过 JAVA 端拉取本地相册,选择图片后返回,并将图片复制到 JS IMAGE 组件可访问的目录下以及将图片路径返回至 JS 端。最终实现 JS 界面展示本地相册图片的功能。

\qquad项目提供两种方案,Internal Ability 以及 LocalParticleAbility ,两种方案思路基本一致,最终效果也都一样。

\qquad通过这个项目来和大家分享我目前使用 JS FA 调用 JAVA PA 功能的经验以及 JS 展示相册照片的思路,同时也希望各位能指出我代码中的不足。项目中部分代码来源于各教程。

三、代码结构展示

Java 端

四、关键问题

4.1 权限问题

由于要访问用户媒体文件,需要申请媒体文件相关权限。

config.json 权限配置:

  "reqPermissions": [
{
"name": "ohos.permission.READ_MEDIA"
},
{
"name": "ohos.permission.WRITE_MEDIA"
},
{
"name": "ohos.permission.MEDIA_LOCATION"
}
]

可选择添加动态申请权限,添加则有权限提示。

// 动态申请权限
private void requestPermissions() {
String[] permissions =
{
SystemPermission.MEDIA_LOCATION,
SystemPermission.WRITE_MEDIA,
SystemPermission.READ_MEDIA,
};
List<String> permissionFiltered =
Arrays.stream(permissions)
.filter(permission -> verifySelfPermission(permission)
!= IBundleManager.PERMISSION_GRANTED).collect(Collectors.toList());
requestPermissionsFromUser(permissionFiltered.toArray(new String[permissionFiltered.size()]), 0);
}

4.2 页面跳转问题

与正常跳转其它 Ability 页面相同,我们需要通过 Intent 来指定启动目标。

此处我们根据需要选择图片的能力来构建 intent 并传入 startAbility,以达到跳转本地相册的目的。

Intent intent = new Intent();
Operation opt=new Intent.OperationBuilder()
.withAction(IntentConstants.ACTION_CHOOSE)
.build();
intent.setOperation(opt);
intent.addFlags(Intent.FLAG_NOT_OHOS_COMPONENT);
intent.setType("image
public static void register(AbilityContext abilityContext) {
instance = new getPhotoInternalAbility();
instance.onRegister(abilityContext);
}
private void onRegister(AbilityContext abilityContext) {
this.abilityContext = abilityContext;
...
}

MainAbility:

@Override
public void onStart(Intent intent) {
getPhotoInternalAbility.register(this);
super.onStart(intent);
}

4.2.2 LocalParticleAbility

getPhotoLocalParticleAbility:

// 重写注册方法即可
private AbilityContext abilityContext;
@Override
public void register(AceAbility ability) {
abilityContext = ability;
LocalParticleAbility.super.register(ability);
}

MainAbility:

@Override
public void onStart(Intent intent) {
super.onStart(intent);
getPhotoLocalParticleAbility.getInstance().register(this);
}

4.2.3 JS 端

JS 侧只需简单调用 JAVA 有关跳转页面的代码即可。

例:

makeAction(bundleName, abilityName, code, abilityType, data) {
const action = {};
action.bundleName = bundleName;
action.abilityName = abilityName;
action.messageCode = code;
action.abilityType = abilityType;
action.data = data;
action.syncOption = 0;
return action;
},
async testGetPhotoInternalAbility() {
const action = this.makeAction('cn.crcrc.arkui_example',
'cn.crcrc.arkui_example.ability.getPhotoInternalAbility', 1001, 1, {});
const result = await FeatureAbility.callAbility(action);
console.info(result)
if(result != null){
this.avatarURL = result
}
},
testGetPhotoLocalParticleAbility(){
this.javaInterface = createLocalParticleAbility('cn.crcrc.arkui_example.ability.getPhotoLocalParticleAbility');
this.javaInterface.getPhotoUri().then(result => {
console.info(result);
if(result != null){
this.avatarURL = result
}
}, error => {
console.error('testGetPhotoLocalParticleAbility error');
});
}

4.3 返回值问题

AbilityContext.startAbility 的结果是可以通过 Ability 的回调方法 onAbilityResult 来获得。通过以上分析,显而易见,由于是通过 MainAbility 来 startAbility 的,那么 onAbilityResult 自然是需在 MainAbility 重写以获得结果。

此时便要解决将相册返回值传递给 Internal Ability 或 LocalParticleAbility,进而返回所得图片地址给 JS 端的问题。

此处我选择通过有序公共事件来传播消息。基本思路即在 MainAbility 的 onAbilityResult 中发布事件,附带相册图片 Uri 值;在 Internal Ability 或 LocalParticleAbility 中订阅事件,得到 Uri 值后通过 Utils 类将图片经自定义处理后存入 JS IMAGE 组件可访问的地址中。

MainAbility:


private void orderlyEventPublish(String data) {
HiLog.info(LABEL, "发布有序公共事件开始");
//1.构建一个Intent对象,包含了自定义的事件的标识符
Intent intent = new Intent();
Operation oper = new Intent.OperationBuilder().withAction("cn.crcrc.arkui_example.event") //就是自定义的公共事件的标识
.build();
intent.setOperation(oper);
//2.构建CommonEventData对象
CommonEventData commonEventData = new CommonEventData(intent);
//仅仅只有有序的公共事件,才能携带的两个专用属性,可选的参数,不是必须的
commonEventData.setData(data);
commonEventData.setCode(1001);
//配置公共事件的对应权限
CommonEventPublishInfo publishInfo = new CommonEventPublishInfo();
publishInfo.setOrdered(true);
//3.核心的发布事件的动作,发布的公共事件,有序的公共事件
try {
CommonEventManager.publishCommonEvent(commonEventData, publishInfo);
HiLog.info(LABEL, "发布有序公共事件完成");
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
super.onAbilityResult(requestCode, resultCode, resultData);
try {
System.out.println("APP LOG resultData:" + resultData.getUriString());
// 通过发布有序公共事件传递信息
orderlyEventPublish(resultData.getUriString());
} catch (Exception e) {
orderlyEventPublish("fail");
e.printStackTrace();
}
}

Internal Ability 或 LocalParticleAbility 中订阅事件,例:


private void subscribeCommonEvent() {
if(!isSubscribe){
HiLog.info(LABEL,"订阅开始:");

//1.构建MatchingSkills对象
MatchingSkills matchingSkills = new MatchingSkills();
matchingSkills.addEvent("cn.crcrc.arkui_example.event"); //订阅自定义的公共事件
//2.构建订阅信息对象
CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);

//3.构建订阅者对象
subscriber = new CommonEventSubscriber(subscribeInfo){
// 回调方法
@Override
public void onReceiveEvent(CommonEventData commonEventData) {
HiLog.info(LABEL,"已接收公共事件");
Boolean setImaDataResult = false;
if(commonEventData.getData() != null)
{
System.out.println("接收到数据:" + commonEventData.getData());
if(!commonEventData.getData().equals("fail"))
{
Uri uri = Uri.parse(commonEventData.getData());
long l = System.currentTimeMillis();
String fileName = String.valueOf(l);
setImaDataResult = utils.setImaData(uri,fileName);
photoUriString = "internal://app/" + String.valueOf(l) + ".jpg";
HiLog.info(LABEL,"js 访问图片路径:" + photoUriString);
}
latch.countDown();
}else {
HiLog.info(LABEL,"已接收公共事件,但数据为空");
System.out.println("APP LOG 已接收公共事件,但数据为空");
}
// 目前 onReceiveEvent 只能在 ui 主线程上执行
// 耗时任务派发到子线程异步执行,保证不阻塞 ui 线程
final AsyncCommonEventResult result = goAsyncCommonEvent();
Boolean finalSetImaDataResult = setImaDataResult;
handler.postTask(new Runnable() {
@Override
public void run() {
if(finalSetImaDataResult){
HiLog.info(LABEL,"进行数据库操作");
// 存入数据库
}
HiLog.info(LABEL,"数据库操作完成");
result.finishCommonEvent();//结束事件
}
});
}
};
//4.订阅公共事件的核心动作
try {
CommonEventManager.subscribeCommonEvent(subscriber);
isSubscribe = true;
} catch (RemoteException e) {
e.printStackTrace();
}
}else{
HiLog.info(LABEL,"公共事件不能重复订阅");
}
}

可将订阅以及取消订阅动作分别放入注册与注销动作中,以 LocalParticleAbility 为例:

@Override
public void register(AceAbility ability) {
abilityContext = ability;
LocalParticleAbility.super.register(ability);
utils = new Utils(abilityContext); // 得到实例
// 注册公共事件
subscribeCommonEvent();
}
@Override
public void deregister(AceAbility ability) {
abilityContext = null;
LocalParticleAbility.super.deregister(ability);
// 取消订阅
try{
CommonEventManager.unsubscribeCommonEvent(subscriber);
}catch(RemoteException e) {
HiLog.error(LABEL,"Exception occurred during unsubscribeCommonEvent invocation.");
}
}

注意:

  1. JS 调用的 JAVA 端方法会在订阅事件中获取到图片地址之前返回,导致 JS 端获得空值。故此处我采取 CountDownLatch 方法以同步传递数据。
  2. 初始化订阅事件时,其中的回调方法会执行一次,故其中要加判断条件,防止 latch.countDown() 出错。
  3. 由于可能不选择图片直接返回,导致 onAbilityResult 的 resultData 为空,故 resultData.getUriString 应 catch 异常。

其中解决 JAVA 端提前返回值应该有其它解决方法,比如用 LocalParticleAbility 的 CallBack 方法开线程等图片返回值,并将其异步返回。或者 JAVA 端先将其存储下来, JS 端调 JAVA PA 相关函数查询对应键值即可。

五、调试日志

六、对比与总结

编写代码时,很容易体会到 LocalParticleAbility 方式较 Internal Ability 方便,尤其是 JS 端与 JAVA 端参数传递上。

LocalParticleAbility 方式就好像 JS 端直接调用其中方法实现功能一样,而 Internal Ability 需要在 onRemoteRequest 根据传递的 code 判断执行哪个方法。

此项目再结合下 JAVA 或 JS 端数据库功能,即可实现如下效果:

项目中有关图片转存的函数实现,查看上传的相应文件即可。

其实 JS 端亦可拉起图片选择能力,但需要返回值的对应方法要 API7 才支持。

var str = {
"want": {
"type": "image/*",
"action": "android.intent.action.GET_CONTENT",
"flags": wantConstant.Flags.FLAG_NOT_OHOS_COMPONENT
},
};
featureAbility.startAbilityForResult(str, (error, data) => {
if (error) {
console.error('Operation failed. Cause: ' + error);
return;
}
console.info('Operation succeeded: ' + data);
});

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

来源:鸿蒙社区内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯