自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

HarmonyOS JS FA調(diào)用PA新方式

開發(fā) OpenHarmony
JS FA調(diào)用Java PA一般通過(guò)FeatureAbility這種傳統(tǒng)方式調(diào)用。本文先回顧傳統(tǒng)方式,在傳統(tǒng)方式的痛點(diǎn)上引入兩種新方式。

[[431269]]

想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

簡(jiǎn)介

​ JS FA調(diào)用Java PA一般通過(guò)FeatureAbility這種傳統(tǒng)方式調(diào)用。本文先回顧傳統(tǒng)方式,在傳統(tǒng)方式的痛點(diǎn)上引入兩種新方式。

傳統(tǒng)方式

方法說(shuō)明

常見的JS FA調(diào)用Java Pa一般通過(guò)以下三個(gè)方法

(1) FeatureAbility.callAbility(OBJECT):調(diào)用PA能力。

(2) FeatureAbility.subscribeAbilityEvent(OBJECT, Function):訂閱PA能力。

(3) FeatureAbility.unsubscribeAbilityEvent(OBJECT):取消訂閱PA能力。

1.能常用于同步調(diào)用方法

比如:

  1. //實(shí)現(xiàn)(Java) 
  2. public int add(int a, int b) { 
  3.     return a + b; 
  4.  
  5. //調(diào)用: 
  6. add: async function (data) { 
  7.     let params = getRequestAction(ACTION_MESSAGE_CODE_ADD); 
  8.     params.data = data; 
  9.     var ret = await FeatureAbility.callAbility(params) 
  10.     console.log("result:" + JSON.parse(ret) + "," + ret) 
  11.     return JSON.parse(ret) 

2.常用于調(diào)用異步方法,因?yàn)?2)是通過(guò)監(jiān)聽的方式實(shí)現(xiàn)異步回調(diào),所以在不需要監(jiān)聽時(shí)最好調(diào)用(3)取消

比如:

  1. //實(shí)現(xiàn)(Java) 
  2. public void addAsync(int a, int b, Callback cb) { 
  3.     new Thread(() -> { 
  4.         int ret = a + b;//假設(shè)這里是一個(gè)耗時(shí)操作 
  5.         cb.reply(ret ); 
  6.     }).start(); 
  7.  
  8. //調(diào)用(JS): 
  9. addAsync: async function (data,func) { 
  10.     let params = getRequestAction(ACTION_MESSAGE_CODE_ADDAsync); 
  11.     params.data = data; 
  12.     FeatureAbility.subscribeAbilityEvent(params, (callbackData) => { 
  13.         var callbackJson = JSON.parse(callbackData); 
  14.         //console.log('getAirlineOpenCity is: ' + JSON.stringify(callbackJson.data)); 
  15.         func(callbackJson.data) 
  16.     }) 
  17. }, 

實(shí)現(xiàn)步驟

在開發(fā)過(guò)程中應(yīng)用中常用Internal Ability,現(xiàn)以Internal Ability說(shuō)明。

Internal Ability需要實(shí)現(xiàn)的步驟:

(1)在java中創(chuàng)建java類繼承AceInternalAbility,設(shè)置setInternalAbilityHandler回調(diào),在回調(diào)方法onRemoteRequest中通過(guò)命令碼code匹配對(duì)應(yīng)的方法名,Parcel中解析方法參數(shù);

(2)在AbilityPackage或者Ability的生命周期對(duì)java類對(duì)象進(jìn)行注冊(cè)和反注冊(cè);

(3)在JS中創(chuàng)建對(duì)應(yīng)JS類,調(diào)用FeatureAbility.callAbility,F(xiàn)eatureAbility.subscribeAbilityEvent傳遞命令碼和方法參數(shù);

痛點(diǎn)

從實(shí)現(xiàn)步驟看出,建立Js FA到Java PA的通道,比較繁瑣,以下操作如果能夠由系統(tǒng)自動(dòng)完成,那么用戶只關(guān)注java端方法實(shí)現(xiàn)、js端調(diào)用,使用就簡(jiǎn)便很多。

(1)進(jìn)行注冊(cè),反注冊(cè);

(2)解析方法名,方法參數(shù);

(3)返回值需要通過(guò)parcel進(jìn)行回傳;

注意點(diǎn)

1.java給js端返回結(jié)果(回消息)時(shí),僅支持String。

  1. public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { 
  2.   switch (code) { 
  3.     case PLUS: { 
  4.       // 返回結(jié)果當(dāng)前僅支持String,對(duì)于復(fù)雜結(jié)構(gòu)可以序列化為ZSON字符串上報(bào) 
  5.       reply.writeString(ZSONObject.toZSONString(result)); 

雖然reply擁有writeInt,writeBoolean等等方法,但目前支持writeString。

2.被調(diào)用的java方法運(yùn)行在非UI線程。

  1. public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { 
  2.     LogUtil.info(TAG, "isMain:"+(EventRunner.current() == EventRunner.getMainEventRunner())); 

通過(guò)在onRemoteRequest處打印當(dāng)前線程是否UI線程,我們可以得出js調(diào)用過(guò)來(lái)在java代碼處是非UI線程的,如果在此處進(jìn)行UI操作,就會(huì)有一定的風(fēng)險(xiǎn)。

如果存在UI操作,可以context.getUITaskDispatcher().syncDispatch(Runnable)或者new EventHandler(EventRunner.getMainEventRunner()).postTask方式處理。

  1. //方式1:uiTaskDispatcher,需要context 
  2. context.getUITaskDispatcher().syncDispatch(task); 
  3.  
  4. //方式2:EventHandler 
  5. new EventHandler(EventRunner.getMainEventRunner()).postTask(task); 

Parcel的大小是200kb,如果超過(guò)200kb(實(shí)際沒這么多),會(huì)拋異常,如下:

官網(wǎng)對(duì)于Parcel的介紹

  • The default capacity of a Parcel instance is 200KB. If you want more or less, use setCapacity(int) to change it.
  • Note: Only data of the following data types can be written into or read from a Parcel: byte, byteArray, short, shortArray, int, intArray, long, longArray, float, floatArray, double, doubleArray, boolean, booleanArray, char, charArray, String, StringArray, PlainBooleanArray, Serializable, Sequenceable, and SequenceableArray.

解決辦法:增大parcel的容量

  1. if (message.length() > 100 * 1000) { 
  2.     int capacity = (int) (message.length() * 2.1f); 
  3.     boolean setFlag = data.setCapacity(capacity); 
  4.     LogUtil.info(TAG, "SYNC capacity:" + capacity + " " + setFlag); 

4.CallAbility的TF_ASYNC 與Subscribe的區(qū)別

雖然兩個(gè)看上去都像異步,但是從調(diào)用上看CallAbility的TF_ASYNC仍然是同步的,只是沒有使用onRemoteRequest的reply方式進(jìn)行回傳。

  1. private boolean replyResult(MessageParcel reply, MessageOption optionint code, String data) { 
  2.     if (option.getFlags() == MessageOption.TF_SYNC) { 
  3.         // SYNC 
  4.         if (data.length() > 100 * 1000) { 
  5.             int capacity = (int) (data.length() * 2.1f); 
  6.             boolean setFlag = reply.setCapacity(capacity); 
  7.             LogUtil.info(TAG, "SYNC capacity:" + capacity + " " + setFlag); 
  8.         } 
  9.         reply.writeString(data); 
  10.     } else { 
  11.         // ASYNC 
  12.         MessageParcel responseData = MessageParcel.obtain(); 
  13.         if (data.length() > 100 * 1000) { 
  14.             int capacity = (int) (data.length() * 2.1f); 
  15.             boolean setFlag = responseData.setCapacity(capacity); 
  16.             LogUtil.info(TAG, "ASYNC capacity:" + capacity + " " + setFlag); 
  17.         } 
  18.         responseData.writeString(data); 
  19.         IRemoteObject remoteReply = reply.readRemoteObject(); 
  20.         try { 
  21.             remoteReply.sendRequest(code, responseData, MessageParcel.obtain(), new MessageOption()); 
  22.         } catch (RemoteException exception) { 
  23.             LogUtil.error(TAG, "RemoteException", exception); 
  24.             return false
  25.         } finally { 
  26.             responseData.reclaim(); 
  27.         } 
  28.     } 
  29.     return true
  30.      
  31.      
  32.   private void replySubscribeMsg(int code, String message) { 
  33.     MessageParcel data = MessageParcel.obtain(); 
  34.     MessageParcel reply = MessageParcel.obtain(); 
  35.     MessageOption option = new MessageOption(); 
  36.     if (message.length() > 100 * 1000) { 
  37.         int capacity = (int) (message.length() * 2.1f); 
  38.         boolean setFlag = data.setCapacity(capacity); 
  39.         LogUtil.info(TAG, "SYNC capacity:" + capacity + " " + setFlag); 
  40.     } 
  41.     data.writeString(message); 
  42.     // 如果僅支持單FA訂閱,可直接觸發(fā)回調(diào):remoteObjectHandler.sendRequest(100, data, reply, option); 
  43.     try { 
  44.         remoteObject.sendRequest(code, data, reply, option); 
  45.     } catch (RemoteException e) { 
  46.         e.printStackTrace(); 
  47.     } 
  48.     reply.reclaim(); 
  49.     data.reclaim(); 

新方式一: js2java-codegen

從上面實(shí)現(xiàn)java,js通信的步驟可以看出,我們?cè)趈ava端對(duì)js傳過(guò)來(lái)的方法、方法參數(shù)進(jìn)行解析,解析完值后如果有返回值還需要通過(guò)Parcel回傳。要是能夠系統(tǒng)自動(dòng)幫我們實(shí)現(xiàn)對(duì)方法名,方法參數(shù)的解析,那就省去一大部分工作。

正好,鴻蒙推出了js2java-codegen這個(gè)注解。

條件

Toolchains的2.2.0.3以上

實(shí)現(xiàn)步驟

1.在module下的gradle開啟js2java-codegen。

  1. ohos { 
  2.     ... 
  3.     defaultConfig { 
  4.         .... 
  5.         javaCompileOptions { 
  6.             annotationProcessorOptions { 
  7.                 arguments = ['jsOutputDir': rootDir.path+'./entry/src/main/js/default/generated'] // 方式1設(shè)置生成路徑 
  8.                 //arguments = ['jsOutputDir': project.file("src/main/js/default/generated")] //方式2設(shè)置路徑 
  9.             } 
  10.         } 
  11.     } 
  12.     compileOptions { 
  13.         f2pautogenEnabled  true // 此處為啟用js2java-codegen工具的開關(guān) 
  14.     } 

 注:jsOutputDir的路徑一定要配置好,否則下面的編譯階段會(huì)報(bào)以下錯(cuò)誤。

2.編寫提供給js調(diào)用的java類。

  1. @InternalAbility(registerTo = "com.freesonwill.facallpa.MainAbility"
  2. public class JsBridgeX { 
  3.     private static final String TAG = "JsBridgeX"
  4.      
  5.     @ContextInject 
  6.     AbilityContext abilityContext; 
  7.  
  8.     public int add(int a, int b) { 
  9.         return a + b; 
  10.     } 

解釋:

1)使用InternalAbility注解java類,注解處理器會(huì)根據(jù)此類生成對(duì)應(yīng)JsBridgeXStub.java和JsBridgeX.js,這兩個(gè)類幫我們建立了通信通道;

2)屬性registerTo設(shè)為想要注冊(cè)到的Ability類的全稱。因?yàn)殚_發(fā)中我們可能用到context相關(guān)的方法,比如啟動(dòng)一個(gè)Ability。這里的registerTo和下面的@ContextInject配合使用,使被修飾的abilityContext指向MainAbility;

3.編譯

點(diǎn)擊Build -> Build HAP(s)/APP(s) -> Build HAP(s),js2java-codegen工具會(huì)為我們生成通信通道,JsBridgeXStub.java和JsBridgeX.js。

JsBridgeXStub.java

  1. public class JsBridgeXStub extends AceInternalAbility { 
  2.  public static final String BUNDLE_NAME = "com.freesonwill.facallpa"
  3.  
  4.  public static final String ABILITY_NAME = "com.freesonwill.facallpa.JsBridgeXStub"
  5. ... 
  6.  
  7.  private AbilityContext abilityContext; 
  8.  
  9.  public JsBridgeXStub() { 
  10.    super(BUNDLE_NAME, ABILITY_NAME); 
  11.  } 
  12.  
  13.  public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, 
  14.      MessageOption option) { 
  15.    Map<String, Object> result = new HashMap<String, Object>(); 
  16.    switch(code) { 
  17.      case OPCODE_add: { 
  18.      java.lang.String zsonStr = data.readString(); 
  19.      ZSONObject zsonObject = ZSONObject.stringToZSON(zsonStr); 
  20.      int a = zsonObject.getObject("a"int.class); //解析add方法第1個(gè)參數(shù) 
  21.      int b = zsonObject.getObject("b"int.class); //解析add方法第2個(gè)參數(shù) 
  22.      result.put("code", SUCCESS); 
  23.      result.put("abilityResult", service.add(a, b));//OPCODE_add 對(duì)應(yīng)servcie.add方法 
  24.      break;} 

點(diǎn)開JsBridgeXStub.java,我們看到,在onRemoteRequest這里仍然是方法名,方法參數(shù)解析。

JsBridgeX.js

  1.  const BUNDLE_NAME = 'com.freesonwill.facallpa'
  2. const ABILITY_NAME = 'com.freesonwill.facallpa.JsBridgeXStub'
  3. ... 
  4. const sendRequest = async (opcode, data) => { 
  5.     var action = {}; 
  6.     action.bundleName = BUNDLE_NAME; 
  7.     action.abilityName = ABILITY_NAME; 
  8.     action.messageCode = opcode; 
  9.     action.data = data; 
  10.     action.abilityType = ABILITY_TYPE_INTERNAL; 
  11.     action.syncOption = ACTION_SYNC; 
  12.     return FeatureAbility.callAbility(action);  //仍然是熟悉的FeatureAbility.callAbility方法 
  13. class JsBridgeX { 
  14.     async add(a, b) { 
  15.         if (arguments.length != 2) { 
  16.             throw new Error("Method expected 2 arguments, got " + arguments.length); 
  17.         } 
  18.         let data = {}; 
  19.         data.a = a; 
  20.         data.b = b; 
  21.         const result = await sendRequest(OPCODE_add, data); 
  22.         return JSON.parse(result); 
  23.     } 

我們看到,這里使用了FeatureAbility.callAbility,action的data屬性中包含了要調(diào)用的方法名(add)和方法參數(shù)(a,b)。

4.使用

  1. import JsBridgeX from '../../generated/JsBridgeX.js'
  2. const bridge = new JsBridgeX() 
  3.  
  4. ... 
  5. //使用方式1:promise+then 
  6. bridge.add(a, b).then(ret => { 
  7.     prompt.showToast({ 
  8.         message: `${a}+$ = ${ret.abilityResult}` 
  9.     }) 
  10. }) 
  11. //使用方式2:await 
  12. var ret = await bridge.add(a, b); 
  13. prompt.showToast({ 
  14.     message: `${a}+$ = ${ret.abilityResult}` 
  15. }) 

注意點(diǎn)

1.返回值從abilityResult屬性獲取,如上ret.abilityResult。

  1. prompt.showToast({ 
  2.     message: `${a}+$ = ${ret.abilityResult}` 
  3. }) 

2.只支持同步方法FeatureAbility.callAbility,不支持FeatureAbility.subscribeAbilityEvent、FeatureAbility.unsubscribeAbilityEvent。從目前官網(wǎng)資料看,生成的方法都是FeatureAbility.callAbility方式。

3.void方法,private,protected,default訪問(wèn)權(quán)限的方法不會(huì)生成。

  1. public void add(int a, int b) {//add 方法不會(huì)生成 
  2.     return a + b; 
  3.  
  4. int add(int a, int b) {//private,protected,default的方法不會(huì)生成 
  5.     return a + b; 

4.生成對(duì)應(yīng)的js方法都是async的。

  1. async add(a, b) { 
  2.     if (arguments.length != 2) { 
  3.         throw new Error("Method expected 2 arguments, got " + arguments.length); 
  4.     } 
  5.     let data = {}; 
  6.     data.a = a; 
  7.     data.b = b; 
  8.     const result = await sendRequest(OPCODE_add, data); 
  9.     return JSON.parse(result); 

 5.非public方法通過(guò)編譯不會(huì)暴露給js,即生成的js代碼沒有對(duì)應(yīng)方法,如果想要public方法也不想暴露給js用,可以使用**@ExportIgnore**。

  1. @ExportIgnore 
  2. public boolean helloChar(){ 
  3.     return true

 6.只支持文件中public的頂層類,不支持接口類和注解類。

7.跟傳統(tǒng)的調(diào)用方式不同,js端必須new 一個(gè)實(shí)例,通過(guò)實(shí)例調(diào)用方法。

  1. const bridge = new JsBridgeX() 
  2. bridge.add(a, b) 

新方式二:LocalParticleAbility

不同于上面的js2java-codegen這種方式,LocalParticleAbility在系統(tǒng)內(nèi)部已經(jīng)建立好通道,不需要編譯生成額外的代碼,在java端實(shí)現(xiàn),在js端調(diào)用就行,比js2java-codegen更加簡(jiǎn)單。

條件

從API Version 6 開始支持。

實(shí)現(xiàn)步驟

1.java端實(shí)現(xiàn)接口LocalParticleAbility,添加方法。

  1. package com.freesonwill.facallpa.biz; 
  2. public class MyLocalParticleAbility implements LocalParticleAbility { 
  3.     //interface 
  4.     public int add(int a, int b) { 
  5.         return a + b; 
  6.     } 
  7.  
  8.     public void addAsync(int a, int b, Callback cb) { 
  9.         new Thread(() -> { 
  10.             int ret = a + b;//假設(shè)這里是一個(gè)耗時(shí)操作 
  11.             cb.reply(ret ); 
  12.         }).start(); 
  13.     } 

 注:這里列舉了同步和異步的兩種方式,異步需要LocalParticleAbility.Callback的reply方法返回。

2.注冊(cè)。

  1. public class MainAbility extends AceAbility { 
  2.  
  3.     @Override 
  4.     public void onStart(Intent intent) { 
  5.         super.onStart(intent); 
  6.         MyLocalParticleAbility.getInstance().register(this); 
  7.     } 
  8.     @Override 
  9.     public void onStop() { 
  10.         super.onStop(); 
  11.         MyLocalParticleAbility.getInstance().deregister(this); 
  12.     } 

 3.js端調(diào)用。

a) 創(chuàng)建LocalParticleAbility對(duì)象

  1. this.javaInterface = createLocalParticleAbility('com.freesonwill.facallpa.biz.MyLocalParticleAbility'); 

 b)調(diào)用方法

調(diào)用同步方法

  1. add(a, b) { 
  2.     this.javaInterface.add(a, b).then(ret => { 
  3.         prompt.showToast({ 
  4.             message: `${a}+$ = ${ret}` 
  5.         }) 
  6.     }) 
  7. }, 
  8. 或者   
  9. async add(a, b) { 
  10.     let ret = await this.javaInterface.add(a, b); 
  11.     console.log("rst:" + JSON.stringify(ret)) 
  12. }, 

 調(diào)用異步方法

  1. addAsync(a, b) {  
  2.     this.javaInterface.addAsync(1, 2, rst => { 
  3.         console.log("rst:" + JSON.stringify(rst)) 
  4.     }) 
  5. }, 

從上面可以看出LocalParticleAbility既支持同步又支持異步,彌補(bǔ)了js2java-codegen的不足。

注意點(diǎn)

  1. 目前從官網(wǎng)中js端的createLocalParticleAbility找不到,目前調(diào)試不行。
  2. API Version 6 開始支持。
  3. 需要注冊(cè)和反注冊(cè)。

總結(jié)

傳統(tǒng)JS FA的調(diào)用方式需要關(guān)注方法名,方法參數(shù)的傳遞解析,比較復(fù)雜,后續(xù)大概率會(huì)被LocalParticleAbility這種簡(jiǎn)潔方式替換。

js2java-codegen提供了APT技術(shù)幫我們生成通道代碼,但是受限于不能實(shí)現(xiàn)異步,函數(shù)必須有返回值等,實(shí)用性不強(qiáng)。

LocalParticleAbility優(yōu)勢(shì)很大,是未來(lái)的方向,目前雖然提供了文檔,但是在DevEco Studio 3.0端 SDK 6仍然不能成功。

想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

 

責(zé)任編輯:jianghua 來(lái)源: 鴻蒙社區(qū)
相關(guān)推薦

2021-09-10 15:13:41

鴻蒙HarmonyOS應(yīng)用

2022-03-07 14:58:10

ArkUIJS FAJava

2022-02-17 21:28:08

AbilityJSFA鴻蒙

2021-12-20 10:07:39

鴻蒙HarmonyOS應(yīng)用

2021-07-16 09:54:49

鴻蒙HarmonyOS應(yīng)用

2022-02-17 21:19:35

JSFA操作系統(tǒng)鴻蒙

2022-06-02 14:27:05

UI框架JS

2022-02-24 16:00:59

Ability鴻蒙JS

2021-11-15 15:11:14

鴻蒙HarmonyOS應(yīng)用

2022-09-08 15:18:51

Ability鴻蒙

2022-05-20 10:56:54

AbilityeTS FA調(diào)用

2009-04-25 09:30:55

Firefox瀏覽器

2018-09-11 11:03:45

存儲(chǔ)新方式DNA存儲(chǔ)

2017-07-17 11:12:07

大數(shù)據(jù)醫(yī)療戴爾

2009-12-25 16:40:33

DDN接入

2022-05-24 15:06:57

AbilityeTS FA鴻蒙

2010-11-25 10:07:01

Ubuntu滾動(dòng)更新

2013-08-15 09:54:02

2011-09-07 14:30:35

MPLSMPLS故障

2022-02-17 21:05:26

AbilityJS FAJava PA
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)