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

相关推荐
传奇开心果编程43 分钟前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程2 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程2 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程3 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓3 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程3 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer
supabc1233 小时前
Celium:连接 Windows、Mac、Linux 与 Android,让远程访问和设备管理更简单
android·linux·windows·macos·远程访问·网络管理·celium
Dovis(誓平步青云)4 小时前
导览音频切换太快,旧讲解不能覆盖新展品
android·前端·javascript·ecmascript·音视频·宠物
五彩小白20 小时前
自回归和上下文
android
事圆则缓1 天前
Android 多渠道打包实战:Product Flavors、签名、AAB 与发包校验
android