Java 调用 Native 服务

本文介绍一下怎么通过 Java 代码使用 Native Binder Service。

接下来我们演示使用 Java 代码访问 Binder 程序示例之 aidl-cpp 篇 编写的 Native Binder Service。

device/jelly/rice14 目录下创建如下的文件与文件夹:

bash 复制代码
JavaCallNativeService/
├── Android.bp
└── com
    └── yuandaima
        └── JavaClient.java

其中 JavaClient.java 内容如下:

java 复制代码
package com.yuandaima;

import android.util.Log;
import android.os.ServiceManager;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;

public class JavaClient {
    public static void main(String[] args) {
        //获取Native Binder Service的代理
        IBinder binder = ServiceManager.getService("IHello");
        String DESCRIPTOR = "com.yuandaima.IHello";
        
        try {
            if (binder != null) {
                Parcel data = Parcel.obtain();
                Parcel replay = Parcel.obtain();
                try {
                    data.writeInterfaceToken(DESCRIPTOR);
                    //通过 BinderProxy 进行数据交互
                    binder.transact(IBinder.FIRST_CALL_TRANSACTION + 0, data, replay, 0);
                    int result = replay.readInt();
                    Log.e("Client","get result :" + result);
                } finally {
                    replay.recycle();
                    data.recycle();
                }
            }
 
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    
    }
}

主要分为 4 步执行:

  • 获得 Hello 服务,与 C++ 程序区别是这里不做转换了,直接使用 IBinder(实际类型是 BinderProxy)
  • 构建好两个 Parcel 结构,data reply
  • 通过 BinderProxy 的 transact 方法发起远程调用
  • 处理返回的 reply

接着我们在系统源码下执行:

bash 复制代码
source build/envsetup.sh
lunch 
make installclean

然后终端进入到 device/jelly/rice14/JavaCallNativeService,编译我们的源码:

bash 复制代码
mm

接着我们进入 device/jelly/rice14/AIDLCppDemo 目录下,编译 C++ 服务端:

bash 复制代码
mm

接着回到系统源码目录下,push 源码产物到 Android 模拟器上:

bash 复制代码
adb push out/target/product/rice14/system/bin/IHelloServer /data/local/tmp
adb push out/target/product/rice14/system/framework/JavaClient.jar /data/local/tmp

接着进入 Andorid shell,执行程序:

bash 复制代码
adb shell
cd /data/local/tmp
# 执行服务端
./IHelloServer & 
# 执行客户端
export CLASSPATH=/data/local/tmp/JavaClient.jar
app_process /data/local/tmp com.yuandaima.JavaClient

最后查看log:

bash 复制代码
logcat | grep JavaClient

从 log 中看出 Client 收到了回复的数据 0,证明我们的调用成功了。

以上介绍的方法算是一种简易的零时解决方案,更为完整的做法是利用 aidl 生成的文件来"衔接" Java 层和 C++ 层:

我们把 device/jelly/rice14/AIDLCppDemo/com/yuandaima/IHello.aidl 文件拷贝到 device/jelly/rice14/JavaCallNativeService/com/yuandaima,接着我们在终端中进入 device/jelly/rice14/JavaCallNativeService/com/yuandaima 目录,接着使用 aidl 生成代码:

bash 复制代码
aidl IHello.aidl

然后修改源码:

java 复制代码
package com.yuandaima;

import android.util.Log;
import android.os.ServiceManager;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;

public class JavaClient {

    public static final String TAG = "JavaClient";

    public static void main(String[] args) {
        //获取Native Binder Service的代理
        IBinder binder = ServiceManager.getService("IHello");
        String DESCRIPTOR = "com.yuandaima.IHello";

        IHello svr = IHello.Stub.asInterface(binder);

        try {
	        svr.hello();
	        Log.i(TAG, "call hello");
        } catch (Exception e) {

        }
           
        try {
	        int cnt = svr.sum(3, 4);
	        Log.i(TAG, "call sum(3, 4)");
        } catch (Exception e) {
    
        }
    
    }
}

然后修改编译文件 Android.bp:

json 复制代码
java_library {
    name: "JavaClient",
    installable: true,
    srcs: [ "com/yuandaima/JavaClient.java",
            "com/yuandaima/IHello.java", ],
}

接着在 device/jelly/rice14/JavaCallNativeService 目录下单编模块:

bash 复制代码
mm

最后我们来测试一下:

bash 复制代码
adb push out/target/product/rice14/system/framework/JavaClient.jar /data/local/tmp
# 进入 Android shell 环境
adb shell
cd /data/local/tmp
./IHelloServer &
export CLASSPATH=/data/local/tmp/JavaClient.jar
app_process /data/local/tmp com.yuandaima.JavaClient

接着查看 log:

less 复制代码
logcat | grep aidl_cpp
08-08 11:15:42.935 29755 29756 I aidl_cpp: hello
08-08 11:15:42.936 29755 29756 I aidl_cpp: server: sum: 3 + 4

从 Log 可以看出我们的 Java 客户端已经成功访问到 Native Binder 服务了。

参考资料

关于

我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,主要研究方向是 Android Framework 与 Linux Kernel。

如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。

相关推荐
debug_cat26 分钟前
AndroidStudio Ladybug中编译完成apk之后定制名字kts复制到指定目录
android·android studio
编程洪同学5 小时前
Spring Boot 中实现自定义注解记录接口日志功能
android·java·spring boot·后端
氤氲息7 小时前
Android 底部tab,使用recycleview实现
android
Clockwiseee7 小时前
PHP之伪协议
android·开发语言·php
小林爱7 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
小何开发8 小时前
Android Studio 安装教程
android·ide·android studio
开发者阿伟9 小时前
Android Jetpack LiveData源码解析
android·android jetpack
weixin_438150999 小时前
广州大彩串口屏安卓/linux触摸屏四路CVBS输入实现同时显示!
android·单片机
CheungChunChiu10 小时前
Android10 rk3399 以太网接入流程分析
android·framework·以太网·eth·net·netd
木头没有瓜10 小时前
ruoyi 请求参数类型不匹配,参数[giftId]要求类型为:‘java.lang.Long‘,但输入值为:‘orderGiftUnionList
android·java·okhttp