Binder通信,Binder是实现进程间通信(IPC)的重要机制,它是基础框架的一个核心组件。

​Binder通信


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

Binder是一个比较复杂话题,它的底部实现比较复杂,本文我们只简单对binder通信进行讲解。Binder是实现进程间通信(IPC)的重要机制,它是基础框架的一个核心组件。这里我们选择用IDL来分析Binder的工作机制。为了分析IDL工作机制,我们需要新建IDL文件,SDK会自动为生产IDL所对应的Binder类,然后我们可以分析下Binder工作过程。

使用的核心API

RPC通信

核心代码解释

首先创建.idl文件,例如此处构建一个名为IIdlTestService.idl的文件。

interface OHOS.IIdlTestService { 
    int TestIntTransaction([in] int data); 
    void TestStringTransaction([in] String data); 
    void TestMapTransaction([in] Map<int, int> data); 
    int TestArrayTransaction([in] String[] data); 
}

在idl的可执行文件所在文件夹下执行命令:”idl -gen-ts -d dir -c dir/IIdlTestService.idl”。

执行命令后,在指定目录文件中可以看到根据 IIdlTestService.idl系统为我们生成IIdlTestService、IdlTestServiceProxy与IdlTestServiceStub三个类。IIdlTestService这个类是个接口类,它内部结构比较简单,其中声明四个方法,这个四个方法明显是我们idl文件中定义。接着我们看IdlTestServiceStub与IdlTestServiceProxy类,这个stub明显就是binder类,proxy是Stub的内部代理类。

export default interface IIdlTestService { 
    testIntTransaction(data: number, callback: testIntTransactionCallback): void; 
    testStringTransaction(data: string, callback: testStringTransactionCallback): void; 
    testMapTransaction(data: Map<number, number>, callback: testMapTransactionCallback): void; 
    testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void; 
} 
export type testIntTransactionCallback = (errCode: number, returnValue: number) => void; 
export type testStringTransactionCallback = (errCode: number) => void; 
export type testMapTransactionCallback = (errCode: number) => void; 
export type testArrayTransactionCallback = (errCode: number, returnValue: number) => void; 
import {testIntTransactionCallback} from "./i_idl_test_service"; 
import {testStringTransactionCallback} from "./i_idl_test_service"; 
import {testMapTransactionCallback} from "./i_idl_test_service"; 
import {testArrayTransactionCallback} from "./i_idl_test_service"; 
import IIdlTestService from "./i_idl_test_service"; 
import rpc from "@ohos.rpc"; 
  
export default class IdlTestServiceProxy implements IIdlTestService { 
    constructor(proxy) { 
        this.proxy = proxy; 
    } 
  
    testIntTransaction(data: number, callback: testIntTransactionCallback): void 
    { 
        let _option = new rpc.MessageOption(); 
        let _data = new rpc.MessageParcel(); 
        let _reply = new rpc.MessageParcel(); 
        _data.writeInt(data); 
        this.proxy.sendMessageRequest(IdlTestServiceProxy.COMMAND_TEST_INT_TRANSACTION, _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.readInt(); 
                callback(_errCode, _returnValue); 
            } else { 
                console.log("sendMessageRequest failed, errCode: " + result.errCode); 
            } 
        }) 
    } 
  
    testStringTransaction(data: string, callback: testStringTransactionCallback): void 
    { 
        let _option = new rpc.MessageOption(); 
        let _data = new rpc.MessageParcel(); 
        let _reply = new rpc.MessageParcel(); 
        _data.writeString(data); 
        this.proxy.sendMessageRequest(IdlTestServiceProxy.COMMAND_TEST_STRING_TRANSACTION, _data, _reply, _option).then(function(result) { 
            if (result.errCode === 0) { 
                let _errCode = result.reply.readInt(); 
                callback(_errCode); 
            } else { 
                console.log("sendMessageRequest failed, errCode: " + result.errCode); 
            } 
        }) 
    } 
  
    testMapTransaction(data: Map<number, number>, callback: testMapTransactionCallback): void 
    { 
        let _option = new rpc.MessageOption(); 
        let _data = new rpc.MessageParcel(); 
        let _reply = new rpc.MessageParcel(); 
        _data.writeInt(data.size); 
        for (let [key, value] of data) { 
            _data.writeInt(key); 
            _data.writeInt(value); 
        } 
        this.proxy.sendMessageRequest(IdlTestServiceProxy.COMMAND_TEST_MAP_TRANSACTION, _data, _reply, _option).then(function(result) { 
            if (result.errCode === 0) { 
                let _errCode = result.reply.readInt(); 
                callback(_errCode); 
            } else { 
                console.log("sendMessageRequest failed, errCode: " + result.errCode); 
            } 
        }) 
    } 
  
    testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void 
    { 
        let _option = new rpc.MessageOption(); 
        let _data = new rpc.MessageParcel(); 
        let _reply = new rpc.MessageParcel(); 
        _data.writeStringArray(data); 
        this.proxy.sendMessageRequest(IdlTestServiceProxy.COMMAND_TEST_ARRAY_TRANSACTION, _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.readInt(); 
                callback(_errCode, _returnValue); 
            } else { 
                console.log("sendMessageRequest failed, errCode: " + result.errCode); 
            } 
        }) 
    } 
  
    static readonly COMMAND_TEST_INT_TRANSACTION = 1; 
    static readonly COMMAND_TEST_STRING_TRANSACTION = 2; 
    static readonly COMMAND_TEST_MAP_TRANSACTION = 3; 
    static readonly COMMAND_TEST_ARRAY_TRANSACTION = 4; 
    private proxy 
} 
import {testIntTransactionCallback} from "./i_idl_test_service"; 
import {testStringTransactionCallback} from "./i_idl_test_service"; 
import {testMapTransactionCallback} from "./i_idl_test_service"; 
import {testArrayTransactionCallback} from "./i_idl_test_service"; 
import IIdlTestService from "./i_idl_test_service"; 
import rpc from "@ohos.rpc"; 
  
export default class IdlTestServiceStub extends rpc.RemoteObject implements IIdlTestService { 
    constructor(des: string) { 
        super(des); 
    } 
  
    modifyLocalInterface() { 
  
    } 
  
    async onRemoteMessageRequest(code: number, data, reply, option): Promise<boolean> { 
        console.log("onRemoteMessageRequest called, code = " + code); 
        switch(code) { 
            case IdlTestServiceStub.COMMAND_TEST_INT_TRANSACTION: { 
                let _data = data.readInt(); 
                this.testIntTransaction(_data, (errCode, returnValue) => { 
                    reply.writeInt(errCode); 
                    if (errCode == 0) { 
                        reply.writeInt(returnValue); 
                    } 
                }); 
                return true; 
            } 
            case IdlTestServiceStub.COMMAND_TEST_STRING_TRANSACTION: { 
                let _data = data.readString(); 
                this.testStringTransaction(_data, (errCode) => { 
                    reply.writeInt(errCode); 
                }); 
                return true; 
            } 
            case IdlTestServiceStub.COMMAND_TEST_MAP_TRANSACTION: { 
                let _data = new Map(); 
                let _dataSize = data.readInt(); 
                for (let i = 0; i < _dataSize; ++i) { 
                    let key = data.readInt(); 
                    let value = data.readInt(); 
                    _data.set(key, value); 
                } 
                this.testMapTransaction(_data, (errCode) => { 
                    reply.writeInt(errCode); 
                }); 
                return true; 
            } 
            case IdlTestServiceStub.COMMAND_TEST_ARRAY_TRANSACTION: { 
                let _data = data.readStringArray(); 
                this.testArrayTransaction(_data, (errCode, returnValue) => { 
                    reply.writeInt(errCode); 
                    if (errCode == 0) { 
                        reply.writeInt(returnValue); 
                    } 
                }); 
                return true; 
            } 
            default: { 
                console.log("invalid request code" + code); 
                break; 
            } 
        } 
        return false; 
    } 
  
    asObject() { 
        return this; 
    } 
  
    testIntTransaction(data: number, callback: testIntTransactionCallback): void{} 
    testStringTransaction(data: string, callback: testStringTransactionCallback): void{} 
    testMapTransaction(data: Map<number, number>, callback: testMapTransactionCallback): void{} 
    testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void{} 
  
    static readonly COMMAND_TEST_INT_TRANSACTION = 1; 
    static readonly COMMAND_TEST_STRING_TRANSACTION = 2; 
    static readonly COMMAND_TEST_MAP_TRANSACTION = 3; 
    static readonly COMMAND_TEST_ARRAY_TRANSACTION = 4; 
}

下面详细介绍针对Stub类和Stub的内部代理类Proxy的方法的含义。

IdlTestServiceStub继承了RemoteObject接口,该接口提供许多方法:

onRemoteMessageRequest

这个方法运行在服务端的binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统层封装后交给此方法处理。服务端是通过code可以确定客户端所请求的目标方法是什么,接着从data取出目标方法所需的参数,然后执行目标方法。当目标方法执行完毕后,就行replay写入返回值, onRemoteMessageRequest执行方法就是这样,需要注意是,如果此方法返回为false,那么客户端的请求会失败,因此我们可以利用这个特性做权限验证。

asObject:此方法用户返回当前Remote对象

modifLocalInterface:此方法用于把接口描述符和IRemoteBroker对象进行绑定

getLocalInterface:此方法用于获取与接口描述绑定的IRemoteBroker。注意需要先绑定对象描述符,才能获取绑定的IRemoteBroker对象。

IdlTestServiceProxy实现IIdlTestService接口,我们只讲解其中一个接口。

Proxy#testIntTransaction这个方法运行在客户端,当客户端调用此方法时,它的内部实现是这样的,首先创建该方法所需要的输入型messageSequence对象_data,输出型messageSequence对象_replay。然后把该方法参数写入_data对象中,接着会调用sendMessageRequest方法发起RPC请求,然后服务端的onRemoteMessageRequest方法就会被调用,直到RPC过程返回后,当前程序继续执行,并从_replay中取出RPC过程的返回结果。

通过上面分析,可以简单了解binder工作机制,下面我们给出一个binder工作机制图。

讲解完binder工作机制,接下来创建Service并实现IdlTestServiceStub接口类,具体代码实现如下。

export default class IdlTestImp extends IdlTestServiceStub { 
  
  testIntTransaction(data: number, callback: testIntTransactionCallback): void 
  { 
    callback(0, data + 1); 
  } 
  testStringTransaction(data: string, callback: testStringTransactionCallback): void 
  { 
    callback(0); 
  } 
  testMapTransaction(data: Map<number, number>, callback: testMapTransactionCallback): void 
  { 
    callback(0); 
  } 
  testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void 
  { 
    callback(0, 1); 
  } 
}

在服务实现接口后,需要向客户端公开该接口,以便客户端进程绑定。如果开发者的服务要公开该接口,请扩展Ability并实现onConnect()从而返回IRemoteObject,以便客户端能与服务进程交互。服务端向客户端公开IRemoteAbility接口的代码示例如下:

import Want from '@ohos.app.ability.Want'; 
import rpc from "@ohos.rpc"; 
  
class ServiceAbility { 
  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 IdlTestImp('connect') as rpc.RemoteObject; 
  } 
  onDisconnect(want: Want) { 
    console.info('ServiceAbility onDisconnect'); 
    console.info('ServiceAbility want:' + JSON.stringify(want)); 
  } 
} 
export default new ServiceAbility()

接下来就是客户端调用IPC接口,客户端通过调用connectServiceExtensionAbility()以连接服务时,客户端的onAbilityConnectDone中的onConnect回调会接收服务的onConnect()方法返回的IRemoteObject实例。由于客户端和服务在不同应用内,所以客户端应用的目录内必须包含.idl文件(SDK工具会自动生成Proxy代理类)的副本。

import common from '@ohos.app.ability.common'; 
import Want from '@ohos.app.ability.Want'; 
import IdlTestServiceProxy from './idl_test_service_proxy' 
  
function callbackTestIntTransaction(result: number, ret: number): void { 
  if (result == 0 && ret == 124) { 
    console.log('case 1 success'); 
  } 
} 
  
function callbackTestStringTransaction(result: number): void { 
  if (result == 0) { 
    console.log('case 2 success'); 
  } 
} 
  
function callbackTestMapTransaction(result: number): void { 
  if (result == 0) { 
    console.log('case 3 success'); 
  } 
} 
  
function callbackTestArrayTransaction(result: number, ret: number): void { 
  if (result == 0 && ret == 124) { 
    console.log('case 4 success'); 
  } 
} 
  
let onAbilityConnectDone: common.ConnectOptions = { 
  onConnect: (elementName, proxy) => { 
    let testProxy: IdlTestServiceProxy = new IdlTestServiceProxy(proxy); 
    let testMap: Map<number, number> = new Map(); 
    testMap.set(1, 1); 
    testMap.set(1, 2); 
    testProxy.testIntTransaction(123, callbackTestIntTransaction); 
    testProxy.testStringTransaction('hello', callbackTestStringTransaction); 
    testProxy.testMapTransaction(testMap, callbackTestMapTransaction); 
    testProxy.testArrayTransaction(['1','2'], callbackTestMapTransaction); 
  }, 
  onDisconnect: (elementName) => { 
    console.log('onDisconnectService onDisconnect'); 
  }, 
  onFailed: (code) => { 
    console.log('onDisconnectService onFailed'); 
  } 
}; 
  
let context: common.UIAbilityContext = this.context; 
  
function connectAbility(): void { 
    let want: Want = { 
        bundleName: 'com.example.myapplicationidl', 
        abilityName: 'com.example.myapplicationidl.ServiceAbility' 
    }; 
    let connectionId = -1; 
    connectionId = context.connectServiceExtensionAbility(want, onAbilityConnectDone); 
}

适配的版本信息

IDE:DevEco    Studio 4.0.1.501

SDK:HarmoneyOS    4.0.0.8

分享
微博
QQ
微信
回复
2024-05-27 11:46:38
相关问题
Binder链接池实现有哪些方法?
392浏览 • 1回复 待解决
js Fa如何实现线程通信
4219浏览 • 1回复 待解决
公共事件实现进程通信
518浏览 • 1回复 待解决
OpenHarmony L1 Ipc samgr_lite通信报错异常
6553浏览 • 2回复 待解决
JS UI框架中FA和PApage之间如何通信
1725浏览 • 1回复 待解决
arkts父子组件组件怎么通信传值啊?
3463浏览 • 1回复 待解决
liteos-m怎么实现进程互斥?
4721浏览 • 1回复 已解决
如何实现一个折叠组件
403浏览 • 1回复 待解决
如何实现一个组件不停地旋转
903浏览 • 1回复 待解决
fegin 和 docker 通信问题
1678浏览 • 1回复 待解决
鸿蒙是否进行异步通信
2691浏览 • 1回复 待解决