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]