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[i]);
    }
}

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[i] = carr[n-1-i];
        }
    
        (*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
  1. JNI的程序中会使用hw_get_module函数获取一个hw_module_t结构体(详细介绍见:hw_get_module详解-CSDN博客)。
  2. 根据获得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
  1. hal文件中需要构建hw_module_t结构体。
  2. 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); 

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值