Android中的JNI

打印 上一主题 下一主题

主题 799|帖子 799|积分 2397

目录

一、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
  1. public class JNIDemo {
  2.     static {         /* 1. load */
  3.         System.loadLibrary("native"); /* libnative.so */
  4.      }
  5.     public native int[] hello(int[] a);
  6.     public static void main (String args[]) {
  7.         JNIDemo d = new JNIDemo();   
  8.         int [] a = {1, 2, 3};
  9.         int [] b = null;
  10.         int i;
  11.         /* 2. map java hello <-->c c_hello */
  12.         /* 3. call */
  13.         b = d.hello(a);
  14.         for (i = 0; i < b.length; i++)        
  15.             System.out.println(b[i]);
  16.     }
  17. }
复制代码
native.c
  1. #include <jni.h>  /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #if 0
  5. typedef struct {
  6.     char *name;          /* Java里调用的函数名 */
  7.     char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
  8.     void *fnPtr;          /* C语言实现的本地函数 */
  9. } JNINativeMethod;
  10. #endif
  11. jintArray c_hello(JNIEnv *env, jobject cls, jintArray arr)
  12. {
  13.     jint *carr;
  14.     jint *oarr;
  15.     jintArray rarr;
  16.    
  17.     jint i, n = 0;
  18.     carr = (*env)->GetIntArrayElements(env, arr, NULL);
  19.         if (carr == NULL) {
  20.         return 0; /* exception occurred */
  21.         }
  22.     n = (*env)->GetArrayLength(env, arr);
  23.     oarr = malloc(sizeof(jint) * n);
  24.         if (oarr == NULL)
  25.         {
  26.         (*env)->ReleaseIntArrayElements(env, arr, carr, 0);
  27.         return 0;
  28.         }
  29.         for (i = 0; i < n; i++)
  30.         {
  31.         oarr[i] = carr[n-1-i];
  32.         }
  33.    
  34.         (*env)->ReleaseIntArrayElements(env, arr, carr, 0);
  35.         /* create jintArray */
  36.     rarr = (*env)->NewIntArray(env, n);
  37.         if (rarr == NULL)
  38.         {
  39.         return 0;
  40.         }
  41.         (*env)->SetIntArrayRegion(env, rarr, 0, n, oarr);
  42.         free(oarr);
  43.    
  44.         return rarr;
  45. }
  46. static const JNINativeMethod methods[] = {
  47.     {"hello", "([I)[I", (void *)c_hello},
  48. };
  49. /* System.loadLibrary */
  50. JNIEXPORT jint JNICALL
  51. JNI_OnLoad(JavaVM *jvm, void *reserved)
  52. {
  53.     JNIEnv *env;
  54.     jclass cls;
  55.     if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
  56.         return JNI_ERR; /* JNI version not supported */
  57.     }
  58.     cls = (*env)->FindClass(env, "JNIDemo");
  59.     if (cls == NULL) {
  60.         return JNI_ERR;
  61.     }
  62.     /* 2. map java hello <-->c c_hello */
  63.     if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)
  64.         return JNI_ERR;
  65.     return JNI_VERSION_1_4;
  66. }
复制代码
二、Android中的JNI利用

1.system sever.java文件文件中利用 System.loadLibrary 函数加载C库。
文件路径:frameworks/base/services/java/com/android/server/SystemServer.java
  1. System.loadLibrary("android_servers");
复制代码
2.onload.cpp中利用JNI_OnLoad函数对所有硬件进行本地注册(JNI文件)。
  1. extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
  2. {
  3.     JNIEnv* env = NULL;
  4.     jint result = -1;
  5.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
  6.         ALOGE("GetEnv failed!");
  7.         return result;
  8.     }
  9.     ALOG_ASSERT(env, "Could not retrieve the env!");
  10. ......
  11.     register_android_server_VibratorService(env);    //示例如下查找对应的类注册本地方法。
  12.    
  13. ......
  14. }
复制代码
3.每个硬件都注册本都方法,在对应的hal文件中对本地方法进行详细的操作(open,close),为什么不在JNI文件中直接进行详细的操作,缘故起因如下:
1.方便编译,修改hal层之后直接编译hal层(硬件抽象层)
2.为了保密利用用,可直接向上层提供库文件即可。
文件名称:com_android_server_VibratorService.cpp
  1. int register_android_server_VibratorService(JNIEnv *env) {
  2.     sMethodIdOnComplete = GetMethodIDOrDie(env,
  3.             FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
  4.             "onComplete", "()V");
  5.     jclass primitiveClass = FindClassOrDie(env,
  6.             "android/os/VibrationEffect$Composition$PrimitiveEffect");        //查找对应的类
  7.     gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
  8.     gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
  9.     gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
  10.     return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
  11.             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界说了各种服务。
  1.     private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
  2.         t.traceBegin("startOtherServices");
  3.         final Context context = mSystemContext;
  4.         VibratorService vibrator = null;
  5.         ........
  6.          vibrator = new VibratorService(context);
  7.          ServiceManager.addService("vibrator", vibrator);
  8.      }
复制代码
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
  1. package android.os;
  2. import android.os.VibrationEffect;
  3. import android.os.VibrationAttributes;
  4. import android.os.IVibratorStateListener;
  5. /** {@hide} */
  6. interface IVibratorService
  7. {
  8.     boolean hasVibrator();
  9.     boolean isVibrating();
  10.     boolean registerVibratorStateListener(in IVibratorStateListener listener);
  11.     boolean unregisterVibratorStateListener(in IVibratorStateListener listener);
  12.     boolean hasAmplitudeControl();
  13.     int[] areEffectsSupported(in int[] effectIds);
  14.     boolean[] arePrimitivesSupported(in int[] primitiveIds);
  15.     boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect,
  16.             in VibrationAttributes attributes);
  17.     void vibrate(int uid, String opPkg, in VibrationEffect effect,
  18.             in VibrationAttributes attributes, String reason, IBinder token);
  19.     void cancelVibrate(IBinder token);
  20. }
  21. /*********************************/
  22. //eg:
  23. 文件名称:ILedService.aidl
  24. package android.os;
  25. interface ILedService
  26. {   
  27.     int LedCtrl(int which, int status);
  28. }
复制代码
放置雷同路径之后,必要修改对应的编译规则,查找Android.mk文件中关键字IVibratorService模仿加入,led相干。编译在frameworks/base目录下实行mmm下令,或者在bsp目录下实行mmm下令加文件路径。如 mmm frameworks/base,修改的Android.mk在base下。
编译出的java文件是不能修改的,接口利用方法:
  1.    private final IVibratorService mService
  2.     mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
  3.    
  4.    
  5.     .......
  6.     mService.hasAmplitudeControl();        //可以直接使用接口文件中提供的函数
  7.     .......
复制代码
二、编写service

service文件的作用是操作本地方法来操作硬件,在此文件中必要实现aidl文件中界说的方法。参考文件frameworks/base/services/core/java/com/android/server/VibratorService.java编写。
  1. package com.android.server;
  2. import android.os.ILedService;
  3. public class LedService extends ILedService.Stub {
  4.     private static final String TAG = "LedService";
  5.     /* call native c function to access hardware */
  6.     public int ledCtrl(int which, int status) throws android.os.RemoteException
  7.     {
  8.         return native_ledCtrl(which, status);
  9.     }
  10.     public LedService() {
  11.         native_ledOpen();        //构造方法中打开设备。
  12.     }
  13.     public static native int native_ledOpen();
  14.     public static native void native_ledClose();
  15.     public static native int native_ledCtrl(int which, int status);
  16. }
复制代码
三、systemserver

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

一、JNI包含硬件操作

编写com_android_server_****Service.cpp文件,实现service文件调用的方法,必要修改编译规则,open, close临时在JNI文件中调用,未编写hal层C文件。
文件存放路径:frameworks/base/services/core/jni/
  1. #define LOG_TAG "LedService"
  2. #include "jni.h"
  3. #include "JNIHelp.h"
  4. #include "android_runtime/AndroidRuntime.h"
  5. #include <utils/misc.h>
  6. #include <utils/Log.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <sys/types.h>
  10. #include <sys/stat.h>
  11. #include <fcntl.h>
  12. #include <sys/ioctl.h>
  13. #include <hardware/led_hal.h>
  14. jint ledOpen(JNIEnv *env, jobject cls)
  15. {
  16.     return 0;   
  17. }
  18. void ledClose(JNIEnv *env, jobject cls)
  19. {
  20.     //ALOGI("native ledClose ...");
  21.     //close(fd);
  22. }
  23. jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
  24. {
  25.     ALOGI("native ledCtrl %d, %d", which, status);
  26.     return 0
  27. }
  28. static const JNINativeMethod methods[] = {
  29.     {"native_ledOpen", "()I", (void *)ledOpen},
  30.     {"native_ledClose", "()V", (void *)ledClose},
  31.     {"native_ledCtrl", "(II)I", (void *)ledCtrl},
  32. };
  33.    
  34. int register_android_server_LedService(JNIEnv *env)
  35. {
  36.     return jniRegisterNativeMethods(env, "com/android/server/LedService",
  37.             methods, NELEM(methods));
  38. }
  39. }
复制代码
二、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来确认。
  1. #define LOG_TAG "LedService"
  2. #include "jni.h"
  3. #include "JNIHelp.h"
  4. #include "android_runtime/AndroidRuntime.h"
  5. #include <utils/misc.h>
  6. #include <utils/Log.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <sys/types.h>
  10. #include <sys/stat.h>
  11. #include <fcntl.h>
  12. #include <sys/ioctl.h>
  13. #include <hardware/led_hal.h>
  14. namespace android
  15. {
  16. static led_device_t* led_device;
  17. jint ledOpen(JNIEnv *env, jobject cls)
  18. {
  19.     jint err;
  20.     hw_module_t* module;
  21.     hw_device_t* device;
  22.     ALOGI("native ledOpen ...");
  23.     /* 1. hw_get_module */
  24.     err = hw_get_module("led", (hw_module_t const**)&module);
  25.     if (err == 0) {
  26.         /* 2. get device : module->methods->open */
  27.         err = module->methods->open(module, NULL, &device);
  28.         if (err == 0) {
  29.             /* 3. call led_open */
  30.             led_device = (led_device_t *)device;
  31.             return led_device->led_open(led_device);
  32.         } else {
  33.             return -1;
  34.         }
  35.     }
  36.    
  37.     return -1;   
  38. }
  39. void ledClose(JNIEnv *env, jobject cls)
  40. {
  41.     //ALOGI("native ledClose ...");
  42.     //close(fd);
  43. }
  44. jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
  45. {
  46.     ALOGI("native ledCtrl %d, %d", which, status);
  47.     return led_device->led_ctrl(led_device, which, status);
  48. }
  49. static const JNINativeMethod methods[] = {
  50.     {"native_ledOpen", "()I", (void *)ledOpen},
  51.     {"native_ledClose", "()V", (void *)ledClose},
  52.     {"native_ledCtrl", "(II)I", (void *)ledCtrl},
  53. };
  54.    
  55. int register_android_server_LedService(JNIEnv *env)
  56. {
  57.     return jniRegisterNativeMethods(env, "com/android/server/LedService",
  58.             methods, NELEM(methods));
  59. }
  60. }
复制代码
二、HAL


  • hal文件中必要构建hw_module_t布局体。
  • JNI中会调用open函数,这里必要构建open函数。他的第一个成员为hw_module_t,第二个参数确认返回布局体类型,第三个参数HW_device_t布局体。
编写的hal层文件放置:hardware/libhardware/include/hardware(头文件),hardware/libhardware//modules(文件)必要编写编译规则
  1. #define LOG_TAG "LedHal"
  2. /* 1. 实现一个名为HMI的hw_module_t结构体 */
  3. /* 2. 实现一个open函数, 第三个参数返回led_device_t结构体 */
  4. /* 3. 实现led_device_t结构体 */
  5. /* 参考 hardware\libhardware\modules\vibrator\vibrator.c
  6. */
  7. #include <hardware/vibrator.h>
  8. #include <hardware/hardware.h>
  9. #include <cutils/log.h>
  10. #include <stdio.h>
  11. #include <unistd.h>
  12. #include <fcntl.h>
  13. #include <errno.h>
  14. #include <hardware/led_hal.h>
  15. #include <stdlib.h>
  16. #include <sys/types.h>
  17. #include <sys/stat.h>
  18. #include <fcntl.h>
  19. #include <sys/ioctl.h>
  20. #include <utils/Log.h>
  21. static int fd;
  22. /** Close this device */
  23. static int led_close(struct hw_device_t* device)
  24. {
  25.     close(fd);
  26.     return 0;
  27. }
  28. static int led_open(struct led_device_t* dev)
  29. {
  30.     fd = open("/dev/leds", O_RDWR);
  31.     ALOGI("led_open : %d", fd);
  32.     if (fd >= 0)
  33.         return 0;
  34.     else
  35.         return -1;
  36. }
  37. static int led_ctrl(struct led_device_t* dev, int which, int status)
  38. {
  39.     int ret = ioctl(fd, status, which);
  40.     ALOGI("led_ctrl : %d, %d, %d", which, status, ret);
  41.     return ret;
  42. }
  43. static struct led_device_t led_dev = {
  44.     .common = {
  45.         .tag   = HARDWARE_DEVICE_TAG,
  46.         .close = led_close,
  47.     },
  48.     .led_open  = led_open,
  49.     .led_ctrl  = led_ctrl,
  50. };
  51. static int led_device_open(const struct hw_module_t* module, const char* id,
  52.         struct hw_device_t** device)
  53. {
  54.     *device = &led_dev;
  55.     return 0;
  56. }
  57. static struct hw_module_methods_t led_module_methods = {
  58.     .open = led_device_open,
  59. };
  60. struct hw_module_t HAL_MODULE_INFO_SYM = {
  61.     .tag = HARDWARE_MODULE_TAG,
  62.     .id = "led",
  63.     .methods = &led_module_methods,
  64. };
复制代码
五、noload

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


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

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

冬雨财经

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表