安卓java端service如何在native进程进行访问-跨进程通讯高端知识

devtools/2025/1/20 1:37:56/

背景:

近来有学员朋友在马哥vip群里提出关于跨进程相关的问题,具体问题如下:
想要在纯native进程中获取当前android系统有多个display,然后获取每个display的Id。但是这个获取display的数目和id的接口其实是在systemserver进程的DisplayManagerService里面。
在这里插入图片描述
目前很多系统服务,比如wms,ams,display都是java形式作为服务端运行在systemserver,正常客户端访问肯定也是用java相关接口进行调用。但是今天需求就是需要使用native进程来跨进程访问到systemserver的DisplayManagerService。
在这里插入图片描述
简单总结需求:
需要在纯native只有c++代码的进程中跨进程访问systemserver的DisplayManagerService相关的接口getDisplayIds。
这里其实我们在跨进程通迅专题课程中有讲解过类似native和java端的相互调用情况,当时的案例是服务端是native的,客户端是java的情况
,相信大家只要实战过马哥跨进程通讯课程肯定就对这个题目有自己的思路和方案。

思路设计和参考

因为跨进程通讯本质上都是需要通过native端,虽然服务端或者客户端是java端,那都是先经过了native端再jni调用到java的,所以完全是可以实现客户端和服务端不要求两个都属于native或者java,完全可以native和java互通。
但是这个需求是要调用到DisplayManagerService的java接口,native因为不可以调用java,也就无法直接使用aidl生成的java接口。那么具体该怎么写这个native端调用服务端的接口呢?这里就需要我们拿aidl文件生成的java文件来参考,看看这个java文件是如何实现的跨进程通讯。

上一篇文章
aosp系统源码aidl文件如何查看对应生成的java文件-安卓系统开发实战小技巧分享
已经分享了如何查看系统中aidl文件对应的java文件,这里我们展示一下我们要访问的DisplayManagerService的getDisplayIds相关方法详情:

 private static class Proxy implements android.hardware.display.IDisplayManager{
//省略部分@Override public int[] getDisplayIds(boolean includeDisabled) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int[] _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeBoolean(includeDisabled);boolean _status = mRemote.transact(Stub.TRANSACTION_getDisplayIds, _data, _reply, 0);_reply.readException();_result = _reply.createIntArray();}finally {_reply.recycle();_data.recycle();}return _result;}

可以从上面这个java的Proxy端即客户端看出,调用一个跨进程接口getDisplayIds,本质上套路如下:
1、parcel对象会包装好相关的传递数据
2、需要知道跨进程调用的方法的code,这里的话是Stub.TRANSACTION_getDisplayIds,这里的

static final int TRANSACTION_getDisplayIds = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

其实就是一个整数,这里是一般根据aidl编写方法的上到下顺序生成

3、调用transact方法把上面准备的parcel,code,_reply带上,这样数据到了远端就可以根据code找到对应的实现方法,同时通过parcel获取相关参数,也可以写回相关数据到_reply

4、读取跨进程返回的_reply相关数据

分析到这里我们就有了一个很清晰思路,那就要针对native端使用c++代码写出和上面客户端跨进程访问服务端代码。

实战开发:

代码参考就是上面Proxy的的getDisplayIds方法,然后变成c++形式而已,整体的流程属性,和parcel包装都可以完全参考java的,这里其实核心代码就是以下几行:

_data.writeInterfaceToken(DESCRIPTOR);
_data.writeBoolean(includeDisabled);
boolean _status = mRemote.transact(Stub.TRANSACTION_getDisplayIds, _data, _reply, 0);
_reply.readException();
_result = _reply.createIntArray();

首先就是writeInterfaceToken方法:
frameworks/base/core/java/android/os/Parcel.java

public final void writeInterfaceToken(@NonNull String interfaceName) {nativeWriteInterfaceToken(mNativePtr, interfaceName);}

frameworks/base/core/jni/android_os_Parcel.cpp

static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jclass clazz, jlong nativePtr,jstring name)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != nullptr) {InterfaceDescriptorString descriptor(env, name);parcel->writeInterfaceToken(reinterpret_cast<const char16_t*>(descriptor.str()),descriptor.size());}
}

最后调用到Parcel.cpp
在这里插入图片描述
可以看出本质就是writeString16
writeBoolean
frameworks/base/core/java/android/os/Parcel.java

 public final void writeBoolean(boolean val) {writeInt(val ? 1 : 0);}

就是直接writeInt

readException

frameworks/base/core/java/android/os/Parcel.java

   public final void readException() {int code = readExceptionCode();if (code != 0) {String msg = readString();readException(code, msg);}}public final int readExceptionCode() {int code = readInt();//省略异常情况return code;}

也就是正常情况readException也就是readInt()获取一个返回int值而已

createIntArray

还有差异的就是java端的_reply.createIntArray()代码c++没有,这里就需要进去看看它的具体实现:
frameworks/base/core/java/android/os/Parcel.java

    @Nullablepublic final int[] createIntArray() {int N = readInt();//首先读取数组大小ensureWithinMemoryLimit(SIZE_INT, N);if (N >= 0 && N <= (dataAvail() >> 2)) {int[] val = new int[N];for (int i=0; i<N; i++) {val[i] = readInt();//然后读取int放入数组}return val;} else {return null;}}

有了上面的这些转化后,写出native的如下代码:

#define LOG_TAG "Main"#include <android-base/logging.h>
#include <android-base/properties.h> 
#include <binder/IBinder.h>
#include <binder/Parcel.h>
#include <binder/IServiceManager.h>#include <utils/Log.h>
#include <utils/threads.h>
#include "MyThread.h"using namespace android;
void testNativeCallJavaServer() {android::sp<android::IServiceManager> sm = android::defaultServiceManager();android::sp<android::IBinder> binder = sm->checkService(android::String16("display"));if (binder == nullptr) {ALOGV("Failed to get display service.");std::cerr << "Failed to get display service." << std::endl;return;}android::Parcel _aidl_data;_aidl_data.markForBinder(binder);_aidl_data.writeInterfaceToken(android::String16("android.hardware.display.IDisplayManager"));_aidl_data.writeInt32(1);android::Parcel _aidl_reply;int TRANSACTION_getDisplayIds = 2//代码1android::status_t _aidl_ret_status = binder->transact(TRANSACTION_getDisplayIds, _aidl_data, &_aidl_reply);if (_aidl_ret_status != android::NO_ERROR) {std::cerr << "Transact failed with status: " << _aidl_ret_status << std::endl;return;}int32_t resultCode;if (_aidl_reply.readInt32(&resultCode) != android::NO_ERROR || resultCode != 0) {std::cerr << "Error in response or non-zero resultCode." << std::endl;return;}size_t count = _aidl_reply.readInt32();//读取std::cerr << "Number of displays: " << count << std::endl;for (size_t i = 0; i < count; ++i) {int32_t displayId;if (_aidl_reply.readInt32(&displayId) == android::NO_ERROR) {std::cerr << "Display ID: " << displayId << std::endl;} else {std::cerr << "Failed to read displayId at index " << i << std::endl;}}
}

代码1处的int TRANSACTION_getDisplayIds = 2,注意这里的2就是从aidl对应的java文件中获得的,所有这个就是为啥要一定要找一个aidl文件生成的参考代码。

验证成果:

NX563J:/ # android_thread                                                                                                                                                                                         
Number of displays: 2
Display ID: 0
Display ID: 2

运行后输出当前display有2个,id分别0和2.

更多framework实战技术干货,请关注下面“千里马学框架”


http://www.ppmy.cn/devtools/151970.html

相关文章

MySQL程序之:使用类似URI的字符串或键值对连接到服务器

本节介绍使用类似URI的连接字符串或键值对来指定如何为MySQLShell等客户端建立到MySQL服务器的连接。 以下MySQL客户端支持使用类似URI的连接字符串或键值对连接到MySQL服务器&#xff1a; MySQL Shell实现X DevAPI的MySQL连接器 本节记录了所有有效的类似URI的字符串和键值…

matlab函数的主要目的是对包含在 Excel 电子表格中的实验数据进行模型拟合

function Latex_Fitting_Sample_Code% clear screen and all variablesclc; clear;% 包含恒定通量横流结垢实验数据的 Excel 电子表格filename = Experimental Data.xlsx;% 包含模型拟合数据的 Excel 电子表格filename2 = Model Fit.xlsx; % 下面的循环采用不规则滤饼模型拟合 …

Uniapp判断设备是安卓还是 iOS,并调用不同的方法

在 UniApp 中&#xff0c;可以通过 uni.getSystemInfoSync() 方法来获取设备信息&#xff0c;然后根据系统类型判断当前设备是安卓还是 iOS&#xff0c;并调用不同的方法。 示例代码 export default {onLoad() {this.checkPlatform();},methods: {checkPlatform() {// 获取系…

动手学大数据-1大数据体系介绍与 SQL 处理流程

前言&#xff1a; 突然想开一篇新专栏学大数据&#xff0c;感觉也不是那么难&#xff0c;起码比深度学习简单多了——我这样想到 为什么大数据平台会回归SQL 总结来说就是&#xff1a;因为还没找到更好的。 结构化数据计算仍是重中之重 大数据平台主要是为了应对海量数据存…

awk命令进阶

1.连接文件 awk NRFNR{a[$1]$0;next} NR!FNR{ if(($5) in a) print a[$1],$0 } file1 file2 命令详解&#xff1a; 这个命令的目的是将 file1 和 file2 基于某个共同字段进行连接&#xff08;类似于 SQL 中的 JOIN 操作&#xff09;。下面我们逐步解析它的工作原理。 1. NRF…

Linux-day06

第14章 进程管理&#xff08;重点&#xff09; 进程基本介绍 程序运行起来就是一个进程 1.程序和进程的关系 2.在Linux中有两种方式执行&#xff0c;一种叫前台&#xff0c;一种后台 ps指令详解 显示系统执行的进程 USER&#xff1a;进程执行用户 PID&#xff1a;进程号 …

Android BitmapShader实现狙击瞄具十字交叉线准星,Kotlin

Android BitmapShader实现狙击瞄具十字交叉线准星&#xff0c;Kotlin <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.…

2025.1.19机器学习笔记:PINN文献精读

第三十周周报 一、文献阅读题目信息摘要Abstract创新点物理背景网络框架实验实验一&#xff1a;直道稳定流条件实验二&#xff1a;环状网络中的非稳定流条件 结论缺点及展望 二、代码实践总结 一、文献阅读 题目信息 题目&#xff1a;《Enhanced physics-informed neural net…