HarmonyOS鸿蒙Next中Js调用Java,实现定位,调用,订阅,取消订阅 API要求4~7,远程模拟器P40
HarmonyOS鸿蒙Next中Js调用Java,实现定位,调用,订阅,取消订阅 API要求4~7,远程模拟器P40
一、建立Ability页,名称JsCallJavaServerAbility.java
二、config中设置如下
1、权限
"reqPermissions": [
{
"name": "ohos.permission.LOCATION_IN_BACKGROUND"
},
{
"name": "ohos.permission.LOCATION"
}
]
2、服务
"type": "service",
"name": "work.yjzll.seesat11.JsCallJavaServiceAbility",
"icon": "$media:icon",
"description": "$string:jscalljavaserviceability_description",
"type": "service"
三、Js文件
import router from '@system.router';
import prompt from '@system.prompt';
// abilityType: 0-Ability; 1-Internal Ability
const ABILITY_TYPE_EXTERNAL = 0;
const ABILITY_TYPE_INTERNAL = 1; // 选用0
// syncOption(Optional, default sync): 0-Sync; 1-Async
const ACTION_SYNC = 0;
const ACTION_ASYNC = 1;
// CODE需要自定义,多个业务就有多个CODE,处理:调用1001 / 订阅1002 / 取消订阅1003
const ACTION_GET = 1001;
const SUBSCRIBE_EVENT = 1002;
const UNSUBSCRIBE_EVENT = 1003;
var mylongitude = 123.4;
var mylatitude = 11.1;
export default {
data: {
title: 'World'
},
getAction: async function () {
console.info('03B00 回调前,经纬度:' + mylongitude + " " + mylatitude);
var actionData = {};
// 此处的两个数据,格式 double要与 RequestParam.java 中的格式一致。比如:int,double,String,int顺序一致! 格式相对应!
actionData.firstNum = mylongitude; //顺序一致! 格式相对应!
actionData.secondNum = mylatitude; //顺序一致! 格式相对应!
var action = {};
action.bundleName = 'work.yjzll.seesat11';
action.abilityName = 'JsCallJavaServiceAbility';
action.messageCode = ACTION_GET; // 需要PA端约定一致
action.data = actionData; //发送到 Ability 的数据,数据字段名称需要PA端约定。只有 FeatureAbility.callAbility 接口有
action.abilityType = ABILITY_TYPE_EXTERNAL; // 0:Ability調用方式 // 1:Internal Ability調用方式 ABILITY_TYPE_INTERNAL
action.syncOption = ACTION_SYNC; // 0:同步方式,默认方式 // 1:异步方式
var result = await FeatureAbility.callAbility(action);
var ret = JSON.parse(result);
if (ret.code == 0) {
//以字符串形式显示调用返回的结果,abilityResult1和abilityResult2,名称和数据类型,要与myLocationAbility.java中的一致
mylongitude = JSON.stringify(ret.abilityResult1); //名称和数据类型,要与myLocationAbility.java中的一致
mylatitude = JSON.stringify(ret.abilityResult2); //名称和数据类型,要与myLocationAbility.java中的一致
console.info('03B00 回调结果,经纬度:' + mylongitude + " " + mylatitude);
} else {
console.error('03B00 回调结果,错误码:' + JSON.stringify(ret.code));
}
},
mySubscribe: async function () {
var action = {};
action.bundleName = 'work.yjzll.seesat11';
action.abilityName = 'JsCallJavaServiceAbility';
action.messageCode = SUBSCRIBE_EVENT;
action.abilityType = ABILITY_TYPE_EXTERNAL;
action.syncOption = ACTION_SYNC;
var result = await FeatureAbility.subscribeAbilityEvent(action, function (resultFunc) {
var r1 = JSON.parse(resultFunc);
var r2 = JSON.stringify(r1.data);
// r2 = {"msg1":114.535703,"msg2":30.495864}
var r3 = JSON.parse(r2);
mylongitude = r3.msg1;
mylatitude = r3.msg2;
var ret = JSON.parse(result);
if (ret.code == 0) {
console.info('03B00 订阅公共事件 经度=' + mylongitude + " 纬度=" + mylatitude);
} else {
console.error('03B00 订阅公共事件 错误 ,结果 = ' + result);
}
});
},
myUnSubscribe: async function () {
var action = {};
action.bundleName = 'work.yjzll.seesat11';
action.abilityName = 'JsCallJavaServiceAbility';
action.messageCode = UNSUBSCRIBE_EVENT;
action.abilityType = ABILITY_TYPE_EXTERNAL;
action.syncOption = ACTION_SYNC;
var result = await FeatureAbility.unsubscribeAbilityEvent(action);
var ret = JSON.parse(result);
if (ret.code == 0) {
console.info('unsubscribe success, result: ' + result);
} else {
console.error('unsubscribe error, result: ' + result);
}
},
showToast: function (msg) {
prompt.showToast({
message: msg,
duration: 1000,
});
}
}
1、action.bundleName = ‘work.yjzll.seesat11’;
action.abilityName = 'JsCallJavaServiceAbility';
2、action.abilityType = ABILITY_TYPE_EXTERNAL; // 0:Ability調用方式
3、数据类型和名称要与RequestParamEvent.java和JsCallJavaServiceAbility.java保持一致
四、RequestParamEvent.java
package work.yjzll.seesat11;
public class RequestParamEvent {
//此处的数据,格式 double要与 RequestParam.java 中的格式一致。比如:int,double,String,int顺序一致! 格式相对应!
// myLocationAbility.java RequestParam.java pageSetting.js 三者数据要全部对应,顺序一致! 格式相对应!
private double firstNum; //顺序一致! 格式相对应!
private double secondNum; //顺序一致! 格式相对应!
public double getFirstNum() {
return firstNum;
}
public void setFirstNum(double firstNum) {
this.firstNum = firstNum;
}
public double getSecondNum() {
return secondNum;
}
public void setSecondNum(double secondNum) {
this.secondNum = secondNum;
}
}
五、JsCallJavaServiceAbility.java
package work.yjzll.seesat11;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.window.dialog.ToastDialog;
import ohos.app.Context;
import ohos.bundle.IBundleManager;
import ohos.event.commonevent.CommonEventSubscriber;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.location.Locator;
import ohos.location.LocatorCallback;
import ohos.location.Location;
import ohos.location.LocationBean;
import ohos.location.LocatorResult;
import ohos.location.GeoAddress;
import ohos.location.GeoConvert;
import ohos.location.RequestParam;
import ohos.location.SubAdministrative;
import ohos.location.RoadName;
import ohos.location.Locality;
import ohos.location.Administrative;
import ohos.location.CountryName;
import ohos.location.IRemoteObject;
import ohos.location.RemoteObject;
import ohos.location.IRemoteBroker;
import ohos.location.MessageParcel;
import ohos.location.MessageOption;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.zson.ZSONObject;
import java.io.IOException;
import java.util.*;
public class JsCallJavaServiceAbility extends Ability {
private static final String TAG = JsCallJavaServiceAbility.class.getSimpleName();
// 定义日志标签
private static final HiLogLabel LABEL_LOG =
new HiLogLabel(HiLog.LOG_APP, 0xD003B00, TAG);
// 定位使用-------------------------------------------------------
private static final int EVENT_ID = 0x12;
private static final String PERM_LOCATION = "ohos.permission.LOCATION";
private final LocatorResult locatorResult = new LocatorResult();
private Context context;
private Locator locator;
private GeoConvert geoConvert;
private List<GeoAddress> gaList;
private LocationBean locationDetails;
//--------------------------------------------------------------
double mylongitude = 7.0;
double mylatitude = 0.0;
@Override
public void onStart(Intent intent) {
HiLog.info(LABEL_LOG, "JsCallJavaServiceAbility::onStart");
super.onStart(intent);
// 初始化定位的参数
registerLocation(this);
// 启动定位功能
registerLocationEvent();
}
@Override
public void onBackground() {
super.onBackground();
HiLog.info(LABEL_LOG, "JsCallJavaServiceAbility::onBackground");
}
@Override
public void onStop() {
super.onStop();
// 停止定位
unregisterLocationEvent();
HiLog.info(LABEL_LOG, "JsCallJavaServiceAbility::onStop");
}
@Override
public void onCommand(Intent intent, boolean restart, int startId) {
}
@Override
public IRemoteObject onConnect(Intent intent) {
super.onConnect(intent);
// 指定订阅功能的主管是谁?
return remote.asObject();
}
@Override
public void onDisconnect(Intent intent) {
}
//-------------------------定位功能---------------------------------------------------------
private final EventHandler handler = new EventHandler(EventRunner.current()) {
// 定位更新的事件处理
@Override
protected void processEvent(InnerEvent event) {
if (event.eventId == EVENT_ID) {
HiLog.info(LABEL_LOG, "processEvent消息接收到,并处理");
//定位数据的去更新其他内容,本次没有要处理的
notifyLocationChange(locationDetails);
//调用订阅功能,实现数据回传和订阅消息发送,很重要
remote.startNotify();
}
}
};
public void registerLocation(Context ability) {
this.context = ability;
// 定位权限检查
requestPermission();
}
private void requestPermission() {
// 检查定位权限
if (context.verifySelfPermission(PERM_LOCATION) != IBundleManager.PERMISSION_GRANTED) {
context.requestPermissionsFromUser(new String[]{PERM_LOCATION}, 0);
}
}
private void registerLocationEvent() {
if (hasPermissionGranted()) {
// 启动定位功能
int timeInterval = 0;
int distanceInterval = 0;
locator = new Locator(context);
RequestParam requestParam = new RequestParam(RequestParam.PRIORITY_ACCURACY, timeInterval, distanceInterval);
locator.startLocating(requestParam, locatorResult);
}
}
private void unregisterLocationEvent() {
if (locator != null) {
// 停止定位
locator.stopLocating(locatorResult);
}
}
private boolean hasPermissionGranted() {
return context.verifySelfPermission(PERM_LOCATION) == IBundleManager.PERMISSION_GRANTED;
}
private class LocatorResult implements LocatorCallback {
@Override
public void onLocationReport(Location location) {
// 定位数据更新
mylongitude = location.getLongitude();
mylatitude = location.getLatitude();
HiLog.info(LABEL_LOG, "onLocationReport 报告: " + mylongitude + " 和 " + mylatitude);
setLocation(location);
}
@Override
public void onStatusChanged(int statusCode) {
HiLog.info(LABEL_LOG, "MyLocatorCallback 状态改变:" + statusCode);
}
@Override
public void onErrorReport(int errorCode) {
HiLog.info(LABEL_LOG, "MyLocatorCallback 异常 : " + errorCode);
}
}
private void notifyLocationChange(LocationBean locationDetails) {
update(locationDetails);
}
private void update(LocationBean locationDetails) {
// 取得定位数据
//locationDetails.getLatitude();
//locationDetails.getLongitude();
//locationDetails.getSpeed();
//locationDetails.getDirection();
//locationDetails.getTime();
//SubAdministrative = locationDetails.getSubAdministrative();
//RoadName = locationDetails.getRoadName();
//Locality = locationDetails.getLocality();
//Administrative = locationDetails.getAdministrative();
//CountryName = locationDetails.getCountryName();
}
private void setLocation(Location location) {
// 更新地理信息
if (location != null) {
Date date = new Date(location.getTimeStamp());
locationDetails = new LocationBean();
locationDetails.setTime(date.toString());
locationDetails.setLatitude(location.getLatitude());
locationDetails.setLongitude(location.getLongitude());
locationDetails.setPrecision(location.getAccuracy());
locationDetails.setSpeed(location.getSpeed());
locationDetails.setDirection(location.getDirection());
fillGeoInfo(locationDetails, location.getLatitude(), location.getLongitude());
// 定位改变后,由handler发送自定义消息,processEvent处理该消息
handler.sendEvent(EVENT_ID);
gaList.clear();
} else {
HiLog.info(LABEL_LOG, "EventNotifier 或 Location response 为空");
//new ToastDialog(context).setText("EventNotifier 或 Location response 为空").show();
}
}
private void fillGeoInfo(LocationBean locationDetails, double geoLatitude, double geoLongitude) {
if (geoConvert == null) {
geoConvert = new GeoConvert();
}
if (geoConvert.isGeoAvailable()) {
try {
gaList = geoConvert.getAddressFromLocation(geoLatitude, geoLongitude, 1);
if (!gaList.isEmpty()) {
GeoAddress geoAddress = gaList.get(0);
setGeo(locationDetails, geoAddress);
}
} catch (IllegalArgumentException | IOException e) {
HiLog.error(LABEL_LOG, "fillGeoInfo 异常");
}
}
}
private void setGeo(LocationBean locationDetails, GeoAddress geoAddress) {
locationDetails.setRoadName(checkIfNullOrEmpty(geoAddress.getRoadName()));
locationDetails.setLocality(checkIfNullOrEmpty(geoAddress.getLocality()));
locationDetails.setSubAdministrative(checkIfNullOrEmpty(geoAddress.getSubAdministrativeArea()));
locationDetails.setAdministrative(checkIfNullOrEmpty(geoAddress.getAdministrativeArea()));
locationDetails.setCountryName(checkIfNullOrEmpty(geoAddress.getCountryName()));
}
private String checkIfNullOrEmpty(String value) {
if (value == null || value.isEmpty()) {
return "NA";
}
return value;
}
// -------------------------------订阅----------------------------------------------------
private CommonEventSubscriber subscriber; // 订阅公共事件使用,此处不使用
// 这里的 remote 是订阅功能的主管
private MyRemote remote = new MyRemote("JsCallJavaServiceAbility");
private static final int SUCCESS = 0;
private static final int ERROR = 1;
class MyRemote extends RemoteObject implements IRemoteBroker {
private static final int ACTION_GET = 1001; // JS端发送的业务请求编码,与本PA端的定义保持一致
private static final int SUBSCRIBE_EVENT = 1002;
private static final int UNSUBSCRIBE_EVENT = 1003;
// 支持多FA订阅,如果仅支持单FA订阅,可直接使用变量存储:private IRemoteObject remoteObjectHandler;
private Set<IRemoteObject> remoteObjectHandlers = new HashSet<>();
private Thread thread = null; // 每隔0.5秒自动发送方式使用,此处不使用
public MyRemote(String descriptor) {
super(descriptor);
}
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
double num1 = 0, num2 = 0;
switch (code) {
case ACTION_GET: {
// 读JSON格式字符串,如{"code":0,"data": {"firstNum":123.4,"secondNum":11.1}}
String dataStr = data.readString();
// dataStr = {"firstNum":123.4,"secondNum":11.1}
// RequestParamEvent 指的是 RequestParamEvent.java 文件中的类
RequestParamEvent param = new RequestParamEvent();
try {
//此处已获得传入的值,两个double数,存入param
param = ZSONObject.stringToClass(dataStr, RequestParamEvent.class);
num1 = param.getFirstNum();
num2 = param.getSecondNum();
HiLog.info(LABEL_LOG, "传入二个数:" + num1 + " 和:" + num2);
} catch (RuntimeException e) {
HiLog.info(LABEL_LOG, "传入失败");
}
// 此处通过其他计算,得到计算结果,准备返回num1,num2
// 返回结果当前仅支持String,对于复杂结构可以序列化为ZSON字符串上报
Map<String, Object> result = new HashMap<>();
result.put("code", SUCCESS);
//以字符串形式显示调用返回的结果,abilityResult1和abilityResult2,名称和数据类型,与pageSetting.js中的一致
//更多数据,一条条接着加上即可
result.put("abilityResult1", mylongitude); //名称和数据类型,与js中的一致
result.put("abilityResult2", mylatitude); //名称和数据类型,与js中的一致
// SYNC
if (option.getFlags() == MessageOption.TF_SYNC) {
reply.writeString(ZSONObject.toZSONString(result));
} else {
// ASYNC
MessageParcel responseData = MessageParcel.obtain();
responseData.writeString(ZSONObject.toZSONString(result));
IRemoteObject remoteReply = reply.readRemoteObject();
try {
remoteReply.sendRequest(SUCCESS, responseData, MessageParcel.obtain(), new MessageOption());
} catch (RemoteException exception) {
return false;
} finally {
responseData.reclaim();
}
}
break;
}
case SUBSCRIBE_EVENT: {
// 如果仅支持单FA订阅,可直接覆盖:remoteObjectHandler = data.readRemoteObject();
remoteObjectHandlers.add(data.readRemoteObject());
// 此处接受数据没有处理,如果需要,可按上面的方式处理:num1,num2
startNotify();
Map<String, Object> result = new HashMap<>();
result.put("code", SUCCESS);
reply.writeString(ZSONObject.toZSONString(result));
break;
}
case UNSUBSCRIBE_EVENT: {
// 如果仅支持单FA订阅,可直接置空:remoteObjectHandler = null;
remoteObjectHandlers.clear();
Map<String, Object> result = new HashMap<>();
result.put("code", SUCCESS);
reply.writeString(ZSONObject.toZSONString(result));
break;
}
default: {
Map<String, Object> result = new HashMap<>();
result.put("FA调用PA错误", ERROR);
reply.writeString(ZSONObject.toZSONString(result));
return false;
}
}
return true;
}
}
// 此方法是根据自定义消息,被handler的processEvent调用 remote.startNotify(),即本方法,传送sendRequest并返回订阅数据
public void startNotify() {
try {
testReportEvent();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void testReportEvent() throws RemoteException {
HiLog.info(LABEL_LOG, "testReportEvent 开始处理");
MessageParcel data = MessageParcel.obtain();
MessageParcel reply = MessageParcel.obtain();
MessageOption option = new MessageOption();
Map<String, Object> event = new HashMap<>();
event.put("msg1", mylongitude); //订阅返回值
event.put("msg2", mylatitude); //订阅返回值
data.writeString(ZSONObject.toZSONString(event));
// 如果仅支持单FA订阅,可直接触发回调:remoteObjectHandler.sendRequest(SUCCESS, data, reply, option);
for (IRemoteObject item : remoteObjectHandlers) {
item.sendRequest(SUCCESS, data, reply, option);
}
reply.reclaim();
data.reclaim();
}
@Override
public IRemoteObject asObject() {
return this;
}
}
1、订阅数据返回经纬度有三种方法 A、目前文档中使用,通过
private final EventHandler handler = new EventHandler(EventRunner.current()) {
// 定位更新的事件处理
@Override
protected void processEvent(InnerEvent event) {
if (event.eventId == EVENT_ID) {
HiLog.info(LABEL_LOG, "processEvent消息接收到,并处理");
//定位数据的去更新其他内容,本次没有要处理的
notifyLocationChange(locationDetails);
//调用订阅功能,实现数据回传和订阅消息发送,很重要
remote.startNotify();
}
}
};
中的 remote.startNotify();实现订阅数据返回
B、第二种方法,是接收公共事件CommonEventSupport.COMMON_EVENT_LOCATION_MODE_STATE_CHANGED 目前看好像无效,可能测试版吧
C、第三种方法,是每隔0.5秒自动返回订阅数据,不管数据有没有变化
更多关于HarmonyOS鸿蒙Next中Js调用Java,实现定位,调用,订阅,取消订阅 API要求4~7,远程模拟器P40的实战教程也可以访问 https://www.itying.com/category-93-b0.html
很详细
更多关于HarmonyOS鸿蒙Next中Js调用Java,实现定位,调用,订阅,取消订阅 API要求4~7,远程模拟器P40的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
你好,我在订阅时遇到一个问题,debug调试断点走到onRemoteRequest方法时,第一次进来code值为1079135572(重启再debug仍然是这种数字),执行default代码块,F8放开,第二次进断点,code值又是我想要的1001,不明白debug为啥要这样操作两次,才得到想要的结果,求大佬指点一下
我是新手中的新手,
哈哈,大佬谦虚了,一起学习,
学习了,赞一个!
补充mainAbility权限申请
```java
package work.yjzll.seesat11;
import ohos.ace.ability.AceAbility;
import ohos.aafwk.content.Intent;
import java.util.ArrayList;
import java.util.List;
public class MainAbility extends AceAbility {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
// AceAbility 不需要注册
requestPermissions();
}
@Override
public void onStop() {
super.onStop();
}
private void requestPermissions(){
// 需要处理定位等敏感权限
String[] permissions = {
"ohos.permission.INTERNET",
"ohos.permission.LOCATION",
"ohos.permission.LOCATION_IN_BACKGROUND"
};
// 可动态授权的权限
List<String> permissionsToProcess = new ArrayList<>();
// 遍历需要处理的权限
for (String permission : permissions){
if ( verifySelfPermission(permission) != 0 && canRequestPermission(permission)){
permissionsToProcess.add(permission);
}
}
// 弹窗申请权限
requestPermissionsFromUser(permissionsToProcess.toArray(new String[0]), 0);
}
}
在HarmonyOS鸿蒙Next中,通过Js调用Java实现定位、调用、订阅、取消订阅功能,可以使用鸿蒙提供的JS FA(Feature Ability)与Java PA(Particle Ability)交互机制。具体步骤如下:
-
定位功能:在Java PA中实现定位逻辑,使用鸿蒙的
LocationManager
API获取设备位置信息。Js FA通过FeatureAbility.callAbility
方法调用Java PA的定位接口,获取位置数据。 -
调用功能:在Java PA中定义需要调用的方法,Js FA通过
FeatureAbility.callAbility
方法传递参数并调用Java PA中的方法,获取返回结果。 -
订阅功能:在Java PA中实现订阅逻辑,使用鸿蒙的
CommonEventManager
API订阅系统事件。Js FA通过FeatureAbility.callAbility
方法调用Java PA的订阅接口,注册事件监听。 -
取消订阅功能:在Java PA中实现取消订阅逻辑,Js FA通过
FeatureAbility.callAbility
方法调用Java PA的取消订阅接口,移除事件监听。
在远程模拟器P40上测试时,确保模拟器已配置正确的网络和位置权限,以便正常调用和测试相关API。
在HarmonyOS鸿蒙Next中,通过Js调用Java实现定位、调用、订阅和取消订阅功能,需使用API 4~7。首先,在Java层创建定位服务类,并通过@JSExport
注解暴露方法。在Js中,使用import
导入Java类,并调用相应方法。对于远程模拟器P40,确保设备连接正常,通过IDE配置模拟器环境。具体实现包括初始化定位服务、调用定位方法、订阅位置更新及取消订阅。详细步骤可参考官方文档和示例代码。