Android JNI HIDL 简单实例学习

    突然看到一篇文章Android HIDL学习,跟这位大神是有一面之缘的,正好也想学习Android是怎么从 APP → JNI Client → HIDL

代码结构

代码我已经上传到了CSND的资源,首先我们看一下结构图和代码目录吧
在这里插入图片描述


1. Android 11/12中编译的源码 naruto目录
hareware/interface/naruto/
└── 1.0
    ├── Android.bp
    ├── default
    │   ├── Android.bp
    │   ├── android.hardware.naruto@1.0-service.rc
    │   ├── client.cpp
    │   ├── jni
    │   │   ├── Android.mk
    │   │   ├── CMakeLists.txt
    │   │   └── native-lib.cpp
    │   ├── Naruto.cpp
    │   ├── Naruto.h
    │   └── service.cpp
    └── INaruto.hal
 
2. Android 11/12源码编译的产物 out目录
system/lib64/android.hardware.naruto@1.0.so
system/lib64/libnative-lib.so
system/app/app-debug.apk

vendor/lib64/hw/android.hardware.naruto@1.0-impl.so
vendor/bin/hw/android.hardware.naruto@1.0-service
vendor/bin/hw/naruto_client
vendor/etc/init/android.hardware.naruto@1.0-service.rc


3. Android Studio中编译的 MyApplication目录
这个就不赘述了,常规代码,逻辑也不多,但有naruto和sasiki的个人壁纸,还挺帅的

HIDL接口

按着开头提到的博客,常规的创建hidl文件的方法,这里也可以不用make hidl-gen,使用out目录中已经生成的一样可以用

PACKAGE=android.hardware.naruto@1.0
LOC=hardware/interfaces/naruto/1.0/default/
make hidl-gen -j64
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

./hardware/interfaces/update-makefiles.sh

这里要注意大神博客上提到INaruto.hal的位置可以随便放,但实际上是不可以的,必须按结构目录来放置,不然无法使用hidl-gen生成c++ impl的文件,还有我使用hidl-gen生成的文件域不对,对着用了好几遍都有问题,这里我手动改的,不然编译的时候找不到INaruto这个类

- namespace android::hardware::naruto::implementation
+ namespace android::hardware::naruto::V1_0::implementation

添加完hidl后别忘了在manifest.xml去添加实现的接口。编译完成后,将我们生成的so和bin都adb push到对应的目录,手动运行service,再运行natruo_client我们写的getservice的测试代码,输出结果如下,那hidl这一块我们就算实现了,再次感谢这个大神

manifest.xml
<hal format="hidl">
    <name>android.hardware.naruto</name>
    <transport>hwbinder</transport>
    <version>1.0</version>
    <interface>
        <name>INaruto</name>
        <instance>default</instance>
    </interface>
</hal>

run result
Hello World, JayZhang

APP 应用

app的话,我之前也没接触过,Android Studio的安装教程网上搜搜一大堆,写APP我推荐看这个博主手把手教你用Android Studio写一个APP,对我来说真的很有帮助,能在短时间内速成一个APP,当然写APP的过程中也让我了解了一些App的知识,比如

(1)起始函数不是main而是onCreate
(2)activity_main.xml是用来对当前页面布局的
(3)MainActivity.java是用来实现activity_main.xml这个页面上的控制
(4)页面按钮点击触发事件常用onClick

我们这个APP主要有两个页面,实现的功能为鸣人和佐助的爱恨情仇(当佐助喊鸣人的名字后,鸣人也能回应佐助名字)
(1)主页面MainActivity上有一个按钮为Naruto,按下后触发调转页面,
(2)调转页面FunctionActivity上有一个text用来显示从JNI获取的string字符串Sasiki

JNI Native

首先我现在AndroidStudio中实现JNI来测试好功能,AndroidStduio网上说是可以使用NDK进行编译,但感觉没有CMake方便,我这边就是通过CMakeList.txt来进行编译libnative-lib.so,实现的很简单就是返回一个string,这个按固定格式去添加即可,前面就是java\com\example\myapplication + FunctionActivety.java调用jni的文件 + 调用的函数接口

native-lib.cpp  测试naruto_client
#include <string>
#include <jni.h>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_FunctionActivity_getStringFromJni(
    JNIEnv* env,
    jobject /*this */){
    std::string hello = "SASIKI";
    return env->NewStringUTF(hello.c_str());
}


app调转页面中的实现
public class FunctionActivity extends AppCompatActivity {

    private TextView myTextView;

    static {
        System.loadLibrary("native-lib");
    }

    public native String getStringFromJni();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_function);

        myTextView = findViewById(R.id.function);
        myTextView.setText(getStringFromJni());
    }

}
后续我们有在这native-lib.cpp中添加了getservice的操作
#include <string>
#include <jni.h>
#include <android/hardware/naruto/1.0/INaruto.h>
#include <hidl/Status.h>
#include <hidl/LegacySupport.h>
#include <utils/misc.h>
#include <hidl/HidlSupport.h>
#include <stdio.h>

using android::hardware::naruto::V1_0::INaruto;
using android::sp;
using android::hardware::hidl_string;

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_FunctionActivity_getStringFromJni(
    JNIEnv* env,
    jobject /*this */){
    std::string hello = "SASIKI";
    std::string hello2 = "Orochimaru";

        android::sp<INaruto> service = INaruto::getService();
    if(service == nullptr) {
        printf("Failed to get service\n");
        return env->NewStringUTF(hello2.c_str());
    }

    service->helloWorld("JayZhang", [&](hidl_string result) {
                printf("%s\n", result.c_str());
    });

    return env->NewStringUTF(hello.c_str());

}

运行结果

关闭sepolicy,运行的时候,APP报错的carsh如下:

dlopen failed: library "/system/lib64/libnative-lib.so" needed or dlopened by "/apex/com.android.art/lib64/libnativeloader.so"
 is not accessible for the namespace "classloader-namespace"

    网上搜了一大堆,都是一样的回复,让你去修改/system/etc/public.libraries.txt,将需要的libnative-lib.so去添加到这个文件中,实际操作中发现修改后会导致开不了机,报错就是因为添加的so的原因;后来还尝试过将所有的依赖库都防止到data/app中lib目录中,最终也能运行起来,但在data目录中是无法实现从vendor获取service的功能
    然后看了这个博客才恍然大悟Android系统编译apk dlopen so失败,public.libraries.txt这个方法在Android 9.0的时候其实就失效了,然后我意识到我从Android Stduio去lanuch的这个测试app,其安装是在data/app目录下,这其实上就是一个三方app,而不是system app,按Android现在的策略三方APP就是无法访问Systemlib的,其实我们只要将我们的apk push到system/lib下即可,当时花了我好久的时间才认识到这个问题,最终apk在手机上运行的结果就是如下画面
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值