冬雨财经 发表于 2025-1-3 17:45:27

Android中的JNI

目录

一、JNI介绍
二、Android中的JNI利用
三、新增设备调用
一、接口文件编写
一、Android AIDL简介
二、Android AIDL编写
二、编写service
三、systemserver
四、JNI文件编写
一、JNI包含硬件操作
二、JNI不包含硬件操作
一、JNI
二、HAL
五、noload

一、JNI介绍

JNI(Java Native Interface)是Java语言中用于与本地C/C++代码交互的特性,答应Java代码调用本地代码。JNI全称Java Native Interface,是Java语言与本地C/C++代码交互的尺度方法。通过JNI,Java代码可以调用本地方法,这些方法以C或C++编写。这种机制使得Java步伐能够充分利用底层系统功能,如图形处理、音频处理等。JNI的重要优点包罗扩展Java步伐的功能、进步性能以及保护代码安全。JNI也是Android的重要构成部分。
利用方法如下:
JNIDemo.java
public class JNIDemo {
    static {         /* 1. load */
      System.loadLibrary("native"); /* libnative.so */
   }
    public native int[] hello(int[] a);
    public static void main (String args[]) {
      JNIDemo d = new JNIDemo();   
      int [] a = {1, 2, 3};
      int [] b = null;
      int i;

      /* 2. map java hello <-->c c_hello */

      /* 3. call */
      b = d.hello(a);

      for (i = 0; i < b.length; i++)      
            System.out.println(b);
    }
} native.c

#include <jni.h>/* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include <stdlib.h>

#if 0
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endif

jintArray c_hello(JNIEnv *env, jobject cls, jintArray arr)
{
    jint *carr;
    jint *oarr;
    jintArray rarr;
   
    jint i, n = 0;
    carr = (*env)->GetIntArrayElements(env, arr, NULL);
      if (carr == NULL) {
      return 0; /* exception occurred */
      }

    n = (*env)->GetArrayLength(env, arr);
    oarr = malloc(sizeof(jint) * n);
      if (oarr == NULL)
      {
      (*env)->ReleaseIntArrayElements(env, arr, carr, 0);
      return 0;
      }

      for (i = 0; i < n; i++)
      {
      oarr = carr;
      }
   
      (*env)->ReleaseIntArrayElements(env, arr, carr, 0);

      /* create jintArray */
    rarr = (*env)->NewIntArray(env, n);
      if (rarr == NULL)
      {
      return 0;
      }

      (*env)->SetIntArrayRegion(env, rarr, 0, n, oarr);
      free(oarr);
   
      return rarr;
}


static const JNINativeMethod methods[] = {
    {"hello", "([I)[I", (void *)c_hello},
};




/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
    JNIEnv *env;
    jclass cls;

    if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
      return JNI_ERR; /* JNI version not supported */
    }
    cls = (*env)->FindClass(env, "JNIDemo");
    if (cls == NULL) {
      return JNI_ERR;
    }

    /* 2. map java hello <-->c c_hello */
    if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)
      return JNI_ERR;

    return JNI_VERSION_1_4;
}

二、Android中的JNI利用

1.system sever.java文件文件中利用 System.loadLibrary 函数加载C库。
文件路径:frameworks/base/services/java/com/android/server/SystemServer.java
System.loadLibrary("android_servers"); 2.onload.cpp中利用JNI_OnLoad函数对所有硬件进行本地注册(JNI文件)。
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      ALOGE("GetEnv failed!");
      return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");

......
    register_android_server_VibratorService(env);    //示例如下查找对应的类注册本地方法。
   
......
} 3.每个硬件都注册本都方法,在对应的hal文件中对本地方法进行详细的操作(open,close),为什么不在JNI文件中直接进行详细的操作,缘故起因如下:
1.方便编译,修改hal层之后直接编译hal层(硬件抽象层)
2.为了保密利用用,可直接向上层提供库文件即可。
文件名称:com_android_server_VibratorService.cpp
int register_android_server_VibratorService(JNIEnv *env) {
    sMethodIdOnComplete = GetMethodIDOrDie(env,
            FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
            "onComplete", "()V");
    jclass primitiveClass = FindClassOrDie(env,
            "android/os/VibrationEffect$Composition$PrimitiveEffect");      //查找对应的类
    gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
    gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
    gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
    return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
            method_table, NELEM(method_table));    //注册本地方法,映射java与C库函数 4.hal层文件来调用open,close来详细的操作硬件。
5.每个硬件都有一个service.java,然后add service。他来利用第三步中注册的本地方法。比方VibratorService服务中他会实例化一个VibratorService对象。之后通过addService把这个服务告诉系统。这个对象中会调用映射的本地方法。
system sever是一个进程。告诉什么什么系统呢?(servivce_manger.c),servivce_manger他管理者系统的所有服务
在SystemServer.java文件中startOtherServices界说了各种服务。
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
      t.traceBegin("startOtherServices");

      final Context context = mSystemContext;
      VibratorService vibrator = null;
      ........
         vibrator = new VibratorService(context);
         ServiceManager.addService("vibrator", vibrator);
   }


6.应用层app可以从servivce_manger查询获取各种servivce。
7.app必要对底层操作时对每一个硬件的system sever提供的接口发送指令。
总结:整个操作过程涉及三个进程,第一个为system sever,第二个为servivce_manger,第三个为app进程。进程间的通讯是通过binder driver驱动。
1.(JNI文件)写一个CPP文件注册本地方法(映射函数),他会加载hal层的C文件。
2.写一个hal层的C文件,他会对应CPP文件中的C库函数,C文件中会调用open,close来操作硬件。
3.修改noload.cpp中调用第一步中注册的本地方法的函数。
4.修改systemserver.java。new一个实例化对象用于操作JNI文件中映射的JAVA方法。并将他添加值service_manger中。
5.第四步必要对象类的实现,写一个类(***service.Java)。他来实现JNI中对应的方法。
6.写一个 Iservice.Java接口文件,给APP利用。
三、新增设备调用

一、接口文件编写

一、Android AIDL简介

在Android开发中,AIDL(Android interace Defnition Language)是一种用于界说客户端与服务端之间通讯接口的语言。在利用AIDL时,我们必要先编写AIDL文件,然后通过工具生成对应的Java文件,用于实现客户端和服务端之间的通讯。
二、Android AIDL编写

AIDL文件他会直接生成Java的设备接口文件。这个文件中好比实现接口(实现哪一盏灯,什么状态),写好一个aidl文件后放置雷同目录,参考如下文件:frameworks/base/core/java/android/os/IVibratorService.aidl
package android.os;

import android.os.VibrationEffect;
import android.os.VibrationAttributes;
import android.os.IVibratorStateListener;

/** {@hide} */
interface IVibratorService
{
    boolean hasVibrator();
    boolean isVibrating();
    boolean registerVibratorStateListener(in IVibratorStateListener listener);
    boolean unregisterVibratorStateListener(in IVibratorStateListener listener);
    boolean hasAmplitudeControl();
    int[] areEffectsSupported(in int[] effectIds);
    boolean[] arePrimitivesSupported(in int[] primitiveIds);
    boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect,
            in VibrationAttributes attributes);
    void vibrate(int uid, String opPkg, in VibrationEffect effect,
            in VibrationAttributes attributes, String reason, IBinder token);
    void cancelVibrate(IBinder token);
}


/*********************************/
//eg:
文件名称:ILedService.aidl
package android.os;

interface ILedService
{   
    int LedCtrl(int which, int status);
} 放置雷同路径之后,必要修改对应的编译规则,查找Android.mk文件中关键字IVibratorService模仿加入,led相干。编译在frameworks/base目录下实行mmm下令,或者在bsp目录下实行mmm下令加文件路径。如 mmm frameworks/base,修改的Android.mk在base下。
编译出的java文件是不能修改的,接口利用方法:
   private final IVibratorService mService
    mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
   
   
    .......
    mService.hasAmplitudeControl();      //可以直接使用接口文件中提供的函数
    ....... 二、编写service

service文件的作用是操作本地方法来操作硬件,在此文件中必要实现aidl文件中界说的方法。参考文件frameworks/base/services/core/java/com/android/server/VibratorService.java编写。
package com.android.server;
import android.os.ILedService;

public class LedService extends ILedService.Stub {
    private static final String TAG = "LedService";

    /* call native c function to access hardware */
    public int ledCtrl(int which, int status) throws android.os.RemoteException
    {
      return native_ledCtrl(which, status);
    }

    public LedService() {
      native_ledOpen();      //构造方法中打开设备。
    }

    public static native int native_ledOpen();
    public static native void native_ledClose();
    public static native int native_ledCtrl(int which, int status);
}
三、systemserver

文件路径:frameworks/base/services/java/com/android/server/SystemServer.java
          //模仿添加服务
             t.traceBegin("StartLedService")//方便调调试的打印
;
            ServiceManager.addService("vibrator", new LedService(context));    //创建类对象并通知service_manger
            t.traceEnd(); 四、JNI文件编写

一、JNI包含硬件操作

编写com_android_server_****Service.cpp文件,实现service文件调用的方法,必要修改编译规则,open, close临时在JNI文件中调用,未编写hal层C文件。
文件存放路径:frameworks/base/services/core/jni/
#define LOG_TAG "LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <hardware/led_hal.h>


jint ledOpen(JNIEnv *env, jobject cls)
{

    return 0;   
}

void ledClose(JNIEnv *env, jobject cls)
{
    //ALOGI("native ledClose ...");
    //close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
    ALOGI("native ledCtrl %d, %d", which, status);
    return 0
}


static const JNINativeMethod methods[] = {
    {"native_ledOpen", "()I", (void *)ledOpen},
    {"native_ledClose", "()V", (void *)ledClose},
    {"native_ledCtrl", "(II)I", (void *)ledCtrl},
};
   

int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

}

二、JNI不包含硬件操作

当JNI不包含硬件操作时,JNI不仅必要向上提供本地接口还必要加载hal文件并调用hal文件中提供的函数用于硬件操作。JNI文件加载hal文件的实质就是怎么利用dlopen加载动态库。Android中对dlopen函数进行了封装利用hw_get_module函数。
一、JNI


[*]JNI的步伐中会利用hw_get_module函数获取一个hw_module_t布局体(详细介绍见:hw_get_module详解-CSDN博客)。
[*]根据得到hw_module_t布局体调用module->methods->open(module,device_name,&device),他会获取一个HW_device_t布局体(open函数的第三个参数),并将这个布局体转换为设备自界说的布局体,返回布局体的类型就根据传入的device_name来确认。

#define LOG_TAG "LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <hardware/led_hal.h>

namespace android
{

static led_device_t* led_device;
jint ledOpen(JNIEnv *env, jobject cls)
{
    jint err;
    hw_module_t* module;
    hw_device_t* device;

    ALOGI("native ledOpen ...");

    /* 1. hw_get_module */
    err = hw_get_module("led", (hw_module_t const**)&module);
    if (err == 0) {
      /* 2. get device : module->methods->open */
      err = module->methods->open(module, NULL, &device);
      if (err == 0) {
            /* 3. call led_open */
            led_device = (led_device_t *)device;
            return led_device->led_open(led_device);
      } else {
            return -1;
      }
    }
   
    return -1;   
}

void ledClose(JNIEnv *env, jobject cls)
{
    //ALOGI("native ledClose ...");
    //close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
    ALOGI("native ledCtrl %d, %d", which, status);
    return led_device->led_ctrl(led_device, which, status);
}


static const JNINativeMethod methods[] = {
    {"native_ledOpen", "()I", (void *)ledOpen},
    {"native_ledClose", "()V", (void *)ledClose},
    {"native_ledCtrl", "(II)I", (void *)ledCtrl},
};
   

int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

}

二、HAL


[*]hal文件中必要构建hw_module_t布局体。
[*]JNI中会调用open函数,这里必要构建open函数。他的第一个成员为hw_module_t,第二个参数确认返回布局体类型,第三个参数HW_device_t布局体。
编写的hal层文件放置:hardware/libhardware/include/hardware(头文件),hardware/libhardware//modules(文件)必要编写编译规则

#define LOG_TAG "LedHal"
/* 1. 实现一个名为HMI的hw_module_t结构体 */
/* 2. 实现一个open函数, 第三个参数返回led_device_t结构体 */
/* 3. 实现led_device_t结构体 */
/* 参考 hardware\libhardware\modules\vibrator\vibrator.c
*/
#include <hardware/vibrator.h>
#include <hardware/hardware.h>
#include <cutils/log.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <hardware/led_hal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>

static int fd;

/** Close this device */
static int led_close(struct hw_device_t* device)
{
    close(fd);
    return 0;
}

static int led_open(struct led_device_t* dev)
{
    fd = open("/dev/leds", O_RDWR);
    ALOGI("led_open : %d", fd);
    if (fd >= 0)
      return 0;
    else
      return -1;
}

static int led_ctrl(struct led_device_t* dev, int which, int status)
{
    int ret = ioctl(fd, status, which);
    ALOGI("led_ctrl : %d, %d, %d", which, status, ret);
    return ret;
}

static struct led_device_t led_dev = {
    .common = {
      .tag   = HARDWARE_DEVICE_TAG,
      .close = led_close,
    },
    .led_open= led_open,
    .led_ctrl= led_ctrl,
};

static int led_device_open(const struct hw_module_t* module, const char* id,
      struct hw_device_t** device)
{
    *device = &led_dev;
    return 0;
}

static struct hw_module_methods_t led_module_methods = {
    .open = led_device_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .id = "led",
    .methods = &led_module_methods,
};


五、noload

noload.cpp文件修改,申明调用注册本地方法。
register_android_server_LedService(env);

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Android中的JNI