Binder链接池实现有哪些方法?

Binder链接池实现

HarmonyOS
2024-05-26 11:36:13
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
day_night

本文再次介绍IDL,原因是IDL是一种最常见的进程通信方式,是日常开发中涉及进程通信时首选,所以我们需要额外的强调一下。

如何使用IDL,在这里先回顾下IDL大致流程:首先创建一个service和IDL接口,接着创建一个类继承一个类继承自IDL接口中Stub类并实现Stud中抽象方法,在Service的onConnect方法中返回这个类的对象,然后客户端就可以绑定服务端Service,建立链接后就可以方位远程服务端的方法。

上诉流程即使典型的IDL的使用流程。实际开发中,项目越来越庞大,如果每个IDL都创建service进行绑定,这种操作会无限量的消耗手机资源。针对上诉问题,我们需要减少Service的数量,将所有的IDL放在同一个Service处理。

在这种模式下,整个工作机制是这样的:每个业务模块创建自己的IDL接口并实现此接口,这个时候不同的业务模块之间不能有耦合,所有实现细节我们要单独分开来,然后向服务端提供自己的唯一标识和其他对应的Binder对象;对于服务端来说,只需要一个Service就可以,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块根据拿到所需的binder对象后就可以进行远程方法调用。由此可见,Binder链接池主要作用就是将每个业务模块的Binder请求统一转发到远程Service中执行,从而避免了重复创建Service的过程。

使用的核心API

RPC通信

核心代码解释

首先我们先定义两个IDL接口(IHuksCenter.idl和ICompute.idl),其中IHuksCenter接口提供加解密功能。

interface OHOS.IHuksCenter { 
    String encrypt([in] String data); 
    String decrypt([in] String data); 
}

而ICompute提供计算加法的功能。

interface OHOS.ICompute { 
    int add([in] Map<int, int> data); 
}

现在业务模块的IDL接口定义和实现都已完成,注意这里并没有位每个模块的IDL单独创建Service,接下来就是服务端和BInder链接池的工作了。

首先,为Binder链接池创建IDL接口IRemotePool.idl。

sequenceable OHOS.rpc.RemoteObject; 
interface OHOS.IRemotePool { 
    RemoteObject queryBinder([in] int binderCode); 
}

在idl的可执行文件所在文件夹下执行命令 idl -gen-ts -d dir -c dir/IRemotePool.idl。通过命令中生成接口文件、Stub文件、Proxy文件。

import rpc from '@ohos.rpc'; 
export default interface IRemotePool { 
    queryBinder(binderCode: number, callback: queryBinderCallback): void; 
} 
export type queryBinderCallback = (errCode: number, returnValue: rpc.RemoteObject) => void; 
import {queryBinderCallback} from "./i_remote_pool"; 
import IRemotePool from "./i_remote_pool"; 
import rpc from "@ohos.rpc"; 
  
export default class RemotePoolProxy implements IRemotePool { 
    constructor(proxy) { 
        this.proxy = proxy; 
    } 
  
    queryBinder(binderCode: number, callback: queryBinderCallback): void 
    { 
        let _option = new rpc.MessageOption(); 
        let _data = rpc.MessageSequence.create(); 
        let _reply = rpc.MessageSequence.create(); 
        _data.writeInt(binderCode); 
        this.proxy.sendMessageRequest(RemotePoolProxy.COMMAND_QUERY_BINDER, _data, _reply, _option).then(function(result) { 
            if (result.errCode === 0) { 
                let _errCode = result.reply.readInt(); 
                if (_errCode != 0) { 
                    let _returnValue = undefined; 
                    callback(_errCode, _returnValue); 
                    return; 
                } 
                let _returnValue = result.reply.readRemoteObject(); 
                callback(_errCode, _returnValue); 
            } else { 
                console.log("sendMessageRequest failed, errCode: " + result.errCode); 
            } 
        }) 
    } 
  
    static readonly COMMAND_QUERY_BINDER = 1; 
    private proxy 
}
import {queryBinderCallback} from "./i_remote_pool"; 
import IRemotePool from "./i_remote_pool"; 
import rpc from "@ohos.rpc"; 
  
export default class RemotePoolStub extends rpc.RemoteObject implements IRemotePool { 
    constructor(des: string) { 
        super(des); 
    } 
  
    async onRemoteMessageRequest(code: number, data, reply, option): Promise<boolean> { 
        console.log("onRemoteMessageRequest called, code = " + code); 
        switch(code) { 
            case RemotePoolStub.COMMAND_QUERY_BINDER: { 
                let _binderCode = data.readInt(); 
                this.queryBinder(_binderCode, (errCode, returnValue) => { 
                    reply.writeInt(errCode); 
                    if (errCode == 0) { 
                        reply.writeRemoteObject(returnValue); 
                    } 
                }); 
                return true; 
            } 
            default: { 
                console.log("invalid request code" + code); 
                break; 
            } 
        } 
        return false; 
    } 
  
    queryBinder(binderCode: number, callback: queryBinderCallback): void{} 
  
    static readonly COMMAND_QUERY_BINDER = 1; 
}

接着,为binder链接池创建Service并实现IRemotePool,下面就是queryBinder的具体实现,可以看到请求转发方法实现,当Binder链接池链接上远程无法时,会根据不同模块的标识即binderCode返回不同Binder对象,通过这个Binder对象所执行的操作全部发生在远程服务端。

class IRemotePool extends RemotePoolStub  { 
  private BINDER_NONE:number = -1; 
  private BINDER_HUKS:number = 0; 
  private BINDER_COMPUTE:number = 1; 
  
  queryBinder(binderCode: number, callback: queryBinderCallback): void{ 
    let remoteObj:rpc.RemoteObject = null; 
    switch (binderCode) { 
      case this.BINDER_NONE: 
           break; 
      case this.BINDER_BOOK: 
           remoteObj = new HuksManager('huks') 
           break; 
      case this.BINDER_TEST: 
           remoteObj = new ComputeImp('compute'); 
           break 
    } 
    callback(0,remoteObj) 
  } 
} 
export default class ServiceExtAbility extends ServiceExtension { 
  
  onStart() { 
    console.info('ServiceAbility onStart'); 
  } 
  
  onStop() { 
    console.info('ServiceAbility onStop'); 
  } 
  
  onCommand(want: Want, startId: number) { 
    console.info('ServiceAbility onCommand'); 
  } 
  
  onConnect(want: Want) { 
    console.info('ServiceAbility onConnect'); 
    try { 
      console.log('ServiceAbility want:' + typeof(want)); 
      console.log('ServiceAbility want:' + JSON.stringify(want)); 
      console.log('ServiceAbility want name:' + want.bundleName) 
    } catch(err) { 
      console.log('ServiceAbility error:' + err) 
    } 
    console.info('ServiceAbility onConnect end'); 
    return new IRemotePool('IRemotePool') as rpc.RemoteObject; 
  } 
  
  onDisconnect(want: Want) { 
    console.info('ServiceAbility onDisconnect'); 
    console.info('ServiceAbility want:' + JSON.stringify(want)); 
  } 
}

下面还剩下Binder链接池的具体实现,在它的内部首先它要去绑定远程服务,绑定成功后,客户端就可以通过它的queryBinder方法获取各自对应的Binder,拿到所需Binder以后,不同的业务模块可以各自操作。

export default class BinderPoolManager { 
  
  private BINDER_NONE:number = -1; 
  private BINDER_BOOK:number = 0; 
  private BINDER_TEST:number = 1; 
  
  connection = -1 // 初始值 
  remoteCallback = null 
  context = null 
  options = null 
  binderPoolProxy = null 
  
  constructor(context) { 
    this.context = context 
    this.options = { 
      onConnect: this.onConnectBack.bind(this), 
      onDisconnect: this.onDisconnectBack.bind(this), 
      onFailed: this.onFailedBack.bind(this) 
    } 
  } 
  
  onDisconnectBack() { 
    console.info(` onDisconnect`) 
  } 
  
  onFailedBack() { 
    console.info(`onFailed`) 
  } 
  
 async queryBinderManger(binderCode) { 
    let remoteProxy = await this.queryBinder(binderCode); 
    console.info('onConnect remoteProxy ==' + remoteProxy) 
    return remoteProxy; 
 } 
  
  queryBinder(binderCode):Promise<rpc.RemoteObject>{ 
    return new Promise((resolve,reject) => { 
      this.binderPoolProxy.queryBinder(binderCode,(code,proxy) => { 
         code === 0 ? resolve(proxy): reject(code) 
      }) 
    }); 
  } 
  
  
  onConnectBack(elementName, proxy) { 
    console.info(`onConnect success`) 
    // 接收来自服务返回的实例 
    let deathRecipient = new MyDeathRecipient(); 
    proxy.addDeathRecipient(deathRecipient, 0); 
    this.binderPoolProxy = new RemotePoolProxy(proxy); 
    this.remoteCallback(SUCCESS_CODE) 
  } 
  
  connectServiceExtAbility(callback) { 
    this.remoteCallback = callback 
    let want = { 
      bundleName: BUNDLE_NAME, 
      abilityName: SERVICE_EXTENSION_ABILITY_NAME 
    } 
    this.connection = this.context.connectAbility(want, this.options) 
    console.info(`connectServiceExtAbility result:${this.connection}`) 
  } 
  
  disconnectServiceExtAbility(callback) { 
    console.info(`disconnectServiceExtAbility`) 
    this.context.disconnectAbility(this.connection).then((data) => { 
      console.info(`disconnectAbility success:${JSON.stringify(data)}`) 
      callback(SUCCESS_CODE) 
    }).catch((error) => { 
      console.error(`disconnectAbility failed:${JSON.stringify(error)}`) 
      callback(ERROR_CODE) 
    }) 
  } 
}

binder链接池具体实现已完成,它的好处是显然易见的,针对上面例子,我们只需要创建一个Service就完成多个IDL接口工作。下面就是测试界面了。

const SUCCESS_CODE = 0 // 成功 
  
@Entry 
@Component 
struct BinderPoolPage { 
  @State message: string = 'Hello World' 
  private binderPoolManager = new BinderPoolManager(getContext(this)) 
  private bookManager:BookFeatureManager = null; 
  private testManager:TestFeatureManager = null; 
  
  connectServiceExtAbility() { 
    this.binderPoolManager.connectServiceExtAbility((code,data)=> { 
      if(code == SUCCESS_CODE) { 
        this.message = '绑定binder链接池成功' 
        console.info("connectServiceExtAbility return success") 
      } else { 
        this.message = '绑定binder链接池失败' 
        console.info("connectServiceExtAbility return fail") 
      } 
    }) 
  } 
  
  aboutToAppear() { 
    let innerEvent: emitter.InnerEvent = { 
      eventId: 1 
    }; 
    emitter.on(innerEvent, (data) => { 
      console.info('"emitter callback success'+JSON.stringify(data.data)); 
      this.message = JSON.stringify(data.data) 
    }); 
  } 
  
  build() { 
    Column() { 
      Button('绑定binder链接池', { type: ButtonType.Normal, stateEffect: true }) 
        .borderRadius(8) 
        .backgroundColor(0x317aff) 
        .width(180) 
        .height(60) 
        .onClick(() => { 
          this.connectServiceExtAbility(); 
        }) 
  
      Button('进行Book通信', { type: ButtonType.Normal, stateEffect: true }) 
        .borderRadius(8) 
        .backgroundColor(0x317aff) 
        .width(180) 
        .height(60) 
        .margin({top:10}) 
        .onClick(async () => { 
           let proxy = await this.binderPoolManager.queryBinderManger(0); 
           this.bookManager = new BookFeatureManager(new BookManagerProxy(proxy)); 
           this.message = '进行book通信 loading......' 
        }) 
  
      Row() { 
        Button('添加书籍', { type: ButtonType.Normal, stateEffect: true }) 
          .borderRadius(8) 
          .backgroundColor(0x317aff) 
          .height(60) 
          .margin({right:4}) 
          .layoutWeight(1) 
          .onClick(async () => { 
            let isSave = this.bookManager.addBook(); 
            this.message = isSave ? '添加书籍成功' : '添加书籍失败'; 
          }) 
  
        Button('获取书籍', { type: ButtonType.Normal, stateEffect: true }) 
          .borderRadius(8) 
          .backgroundColor(0x317aff) 
          .layoutWeight(1) 
          .margin({left:4,right:4}) 
          .height(60) 
          .onClick(async () => { 
            let lists = await this.bookManager.getBookList(); 
            this.message = JSON.stringify(lists); 
          }) 
  
        Button('订阅书籍', { type: ButtonType.Normal, stateEffect: true }) 
          .borderRadius(8) 
          .backgroundColor(0x317aff) 
          .layoutWeight(1) 
          .margin({left:4,right:4}) 
          .height(60) 
          .onClick(async () => { 
            let register = await this.bookManager.addBookListener(); 
            this.message = register ? '订阅书籍成功,1秒后自动接收服务推送书籍' : '订阅失败,请重新订阅'; 
          }) 
  
        Button('取消订阅', { type: ButtonType.Normal, stateEffect: true }) 
          .borderRadius(8) 
          .backgroundColor(0x317aff) 
          .layoutWeight(1) 
          .margin({left:4}) 
          .height(60) 
          .onClick(() => { 
            let unRegister = this.bookManager.unRegisterListener(); 
            this.message = unRegister ? '取消订阅成功' : '取消订阅失败'; 
          }) 
      } 
      .width('90%') 
      .height(80) 
      .alignItems(VerticalAlign.Center) 
  
      Button('进行测试用例通信', { type: ButtonType.Normal, stateEffect: true }) 
        .borderRadius(8) 
        .backgroundColor(0x317aff) 
        .width(180) 
        .height(60) 
        .onClick(async () => { 
          //点击 
          let proxy = await this.binderPoolManager.queryBinderManger(1); 
          this.testManager = new TestFeatureManager(new IdlTestServiceProxy(proxy)); 
          this.message = '进行测试基础类型通信 loading......' 
        }) 
  
      Row() { 
        Button('测试int', { type: ButtonType.Normal, stateEffect: true }) 
          .borderRadius(8) 
          .backgroundColor(0x317aff) 
          .height(60) 
          .margin({right:4}) 
          .layoutWeight(1) 
          .onClick(async () => { 
            let result =  await this.testManager.testIntTransaction(1024); 
            this.message = "测试结果成功,测试返回结果:"+ result; 
          }) 
  
        Button('测试string', { type: ButtonType.Normal, stateEffect: true }) 
          .borderRadius(8) 
          .backgroundColor(0x317aff) 
          .layoutWeight(1) 
          .margin({left:4,right:4}) 
          .height(60) 
          .onClick(async () => { 
             let result  = await this.testManager.testStringTransaction('hello'); 
             let msg = result ? "成功" : '失败' 
             this.message = "测试string结果"+ msg +',无返回结果'; 
          }) 
  
        Button('测试map', { type: ButtonType.Normal, stateEffect: true }) 
          .borderRadius(8) 
          .backgroundColor(0x317aff) 
          .layoutWeight(1) 
          .margin({left:4,right:4}) 
          .height(60) 
          .onClick(async () => { 
            let result = await this.testManager.testMapTransaction(); 
            let msg = result ? "成功" : '失败' 
            this.message = "测试map结果"+ msg +',无返回结果'; 
          }) 
  
        Button('测试数组', { type: ButtonType.Normal, stateEffect: true }) 
          .borderRadius(8) 
          .backgroundColor(0x317aff) 
          .layoutWeight(1) 
          .margin({left:4}) 
          .height(60) 
          .onClick(async () => { 
            let result =  await this.testManager.testArrayTransaction(); 
            let msg = result ? "成功" : '失败' 
            this.message = "测试数组结果"+ msg +',无返回结果'; 
          }) 
      } 
      .width('90%') 
      .height(80) 
      .alignItems(VerticalAlign.Center) 
  
      Text(this.message) 
        .width('90%') 
        .height('40%') 
        .margin({ top: 20 }) 
        .padding({left:10}) 
        .fontSize(20) 
        .textAlign(TextAlign.Center) 
        .fontWeight(FontWeight.Bold) 
        .border({ width: 2, radius: 10, color: Color.Black }) 
    } 
    .margin({top:10}) 
    .justifyContent(FlexAlign.Start) 
    .width('100%') 
    .height('100%') 
  } 
}

实现效果

适配的版本信息

IDE:DevEco    Studio 4.0.1.501

SDK:HarmoneyOS    4.0.0.8

分享
微博
QQ
微信
回复
2024-05-27 11:50:01
相关问题
PopWindow的效果实现有哪些
643浏览 • 1回复 待解决
Scroller的fling实现有什么好的方案
762浏览 • 1回复 待解决
HarmonyOS线程使用崩溃
427浏览 • 1回复 待解决
HarmonyOS Factory还有哪些方法
102浏览 • 0回复 待解决
可以调用Android现有的库吗
7326浏览 • 1回复 待解决
request下载文件不能覆盖现有文件
1822浏览 • 1回复 待解决
语音识别的方法哪些
482浏览 • 1回复 待解决
Flutter 热重载方法哪些
330浏览 • 1回复 待解决
HarmonyOS @ohos.taskpool(启动任务
178浏览 • 1回复 待解决
文本转语音的方法哪些?
336浏览 • 1回复 待解决
Greenplum有哪些通用的处理方法
3539浏览 • 1回复 待解决
HarmonyOS线程周期执行任务
1038浏览 • 1回复 待解决
线程与线程的个数限制
1675浏览 • 1回复 待解决
OB的资源是多租户共享吗?
4933浏览 • 1回复 待解决