Android JNI 学习笔记

JNI  是 Java Native Interface(Java 本地接口)。JNI不是Android 专有的东西,他是从Java继承来的。但是 对于Android来说JNI至关重要,Android 作为一种嵌入式操作系统,有大量和驱动、硬件相关的功能都是用C/C++来实现的。可以说在Android中不管应用级还是系统级的开发都离不开JNI。

Java语言的执行,离不开JVM,因此当需要在Java层中调用C/C++层时,需要先告诉JVM那个方法代表本地函数,伊基在哪里能找到这个函数,反之也一样。但是从Java层调用C/C++需要建立函数间的关联;而C/c++到Java。必须先得到Java对象的引用,才能调用该对象的方法(Java纯面向对象)。

需要注意的是,不论是从C/C++到Java 还是从Java到C/C++都是在一个线程中运行的。

从JAVA到C/C++

装载JNI动态库

为了能够使用JNI,在调用本地方法前必须把C/C++代码所在的动态库装载到进程的内存空间中。一般之间使用System的LoadLibrary() 方法

public  static void loadLibrary(String libName);

值得一提的是,libName是我们生成的动态库名称的一部分(注意:这里是一部分),在Android 中JNI动态库的名称必须为"lib"开始,那么一般我们的libName就会去掉前缀"lib"和后缀".so"或者“.dll”。加入我们本地的动态库文件为"libjni.so",那么我们的调用就是

System.loadLibrary("jni");

注:动态库的后缀在Linux中是".so",在Window中是“.dll”。

在这里我们并不需要指定我们的动态链接库的具体路劲。Android系统会自动在相关的系统路径里面查找。

一般情况下LoadLibrary()会在类的static代码块中执行(为了保证native方法在调用之间就已经加载完成,static代码块会首先执行);

public class Sample{

     static {

          System.loadLibrary("jni");

     }

}

定义native方法

Java层申明native方法很简单:

private  static native  final void native_init();

在native方法中,可以使用任何类型作为参数,包括基本对象类型、数组、复杂对象。Java中调用native方法和普通方法无异。

JNI动态链接库

JNI动态链接库与非动态库的区别就在于:JNI动态库中定义了"JNI_OnLoader"函数,这个函数在System.LoadLibrary();后被系统调用,用于完成JNI函数的注册。

jint JNI_OnLoader(JavaVm* vm, void* reserved)

在"JNI_OnLoader"中,最重要的一件事就是通过registerNativeMethods()函数完成JNI函数的注册(完成Java层中申明的native方法和C/C++中C函数关联);这样一来,我们的JVM在运行native方法时就能够找到对应的C函数。

jnitest.cpp

#define LOG_TAG "hello-JNI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
static jint com_jeason_jnitest_nadd(JNIEnv *env, jobject obj, jint a, jint b){
return (a * b);
}
static JNINativeMethod gMethods[] = {
{"nadd", "(II)I", (void *)com_jeason_jnitest_nadd},
};
static int register_android_test_add(JNIEnv *env){
return android::AndroidRuntime::registerNativeMethods(env, "com/jeason/jnitest/LoadLibraryJavaClass", gMethods, NELEM(gMethods));
}
jint JNI_OnLoad(JavaVM *vm, void *reserved){
JNIEnv *env = NULL;
if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
printf("Error GetEnv\n");
return -1;
}
assert(env != NULL);
if (register_android_test_add(env) < 0) {
printf("register_android_test_add error.\n");
return -1;
}
return JNI_VERSION_1_4;
}

registerNativeMethods()函数的原型:

int registerNativeMethods(JNIEnv* env,const char* classname,const JNINativeMethos* gMethods,int numberMethods);

第二个参数className 指的是我们的Java类(调用SYstem.loadLibrary())的路径全名,但是在名称中‘.’需要用路径符‘/‘.

第三个参数gMethods 是JNINativeMethods类型数组

typedef  struct {

    const char* name;
    const char* signature;
    void*   fnPtr;
} JNINativeMethods;

其中name 是指Java中申明native方法的名称;signature是指native方法参数签名(后面给出说明);

fnPtr是指native方法对应的本地函数指针,一般都为void* (无法直接指定,一般只能用void*,调用时在强制类型转换)

通常函数原型一般为

JNIEXPORT jint JNICALL com_jeason_jnitest_jnitext_nadd(JNIEnv *env, jobject obj ,jint a,jint b);

JNIEXPORT 和 JNICALL 可以省略

LoadLibraryJavaClass.java

public class LoadLibraryJavaClass {

static {

System.loadLibrary("jni");

}

public native int nadd(int a, int b);

}

参数签名

native方法中的参数签名使用了一些缩写符号来代表参数类型,这些符号都是Java语言规定的。签名由参数和返回值组成,参数必须用小括号括起来,没有的时候也必须要使用一对空括号。例如"(I)V"表示方法有一个整型参数,无返回值。"([IZ)I"表示有2个参数,第一个是整形数组第二个是布尔型,返回值为整形。

具体的对应关系见下面两张图:

数组则以”["开始,用两个字符表示

以上都是基本数据类型,前面我们解决了JNI函数的注册问题,下面我们来考虑这样一个问题。在java中调用native函数传递的参数是java数据类型,那么这些参数类型到了JNI会变成什么呢? 下面我们引出了一个新的话题——数据类型转换。

数据类型转换:

在java层调用native函数传递到JNI层的参数,JNI层会做一些特殊处理,我们知道java数据类型分为基本数据类型和引用数据类型两种,JNI层也是区别对待的。下表示出了java数据类型—>native类型的转换。

其中在java数据类型中,除了java中基本数据类型和数组,Class,String,Throwable,其余所有的java对象的数据类型在JNI中用jobject表示。

但是,是不是所有的都这么简单呢

{"myway",“(Ljava.lang.String;Ljava.lang.String;Landroid.bean.Bean;)V”,(void*)myway}

对于JNI的编译:

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES :=      jnitest.cpp
LOCAL_SHARED_LIBRARIES := libandroid_runtime
LOCAL_MODULE := libjni
include $(BUILD_SHARED_LIBRARY)

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-08 15:56:06

Android JNI 学习笔记的相关文章

Android JNI学习笔记(三)-编译文件Android.mk、Application.mk 与camke

1. 前言 在android2.2中,加入了cmake编译,而以前都是用Android.mk.Application.mk的,今天就来记录下,他们的配置选项. 2. Android.mk Android.mk在jni目录下,用于描述构建系统的源文件以及 shared libraries .文件格式如下: 以LOCAL_PATH变量开始 LOCAL_PATH := $(call my-dir) 紧接着是CLEAR_VARS变量 include $(CLEAR_VARS) 接下来LOCAL_MODU

Android JNI学习笔记(四)-数据类型映射以及native调用java

1. 前言 前几篇学习了jni开发的基本流程.动态注册native函数以及相关编译文件的编写,咱们也算是知道了jni开发,但是还不够,今天咱们来学习下,java和jni的数据类型映射(说白了就是对应关系),以及如何在jni层调用java层的一些东西.偷偷告诉你们,这些全在jni.h文件里. 2. 数据类型映射 首先是我们的基本数据类型,其关系如下表描述这样. 上面关系的相关代码在jni.h的44-51行,如下 typedef unsigned char jboolean; /* unsigned

Android NDK学习笔记(一) 为什么要用NDK?

NDK是什么 NDK是Native Development Kit的简称,即本地开发工具包.通过NDK,Android允许开发人员使用本地代码语言(例如C/C++)来完成应用的部分(甚至全部)功能.注意:由于翻译原因,有些地方也把Native翻译为"原生". NDK是SDK的一个补充,可以帮助你做这些事情: 生成可以在ARM CPU,Android 1.5(及以上)平台运行的JNI兼容的共享库. 将生成的共享库放置在应用程序项目路径的合适位置,使其能自动地添加进你最终的(和经过签名的)

java/android 设计模式学习笔记(14)---外观模式

这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式.通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节.当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块.ImageLoader 模块等.其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它. 转载请注明出处:http://bl

java/android 设计模式学习笔记(10)---建造者模式

这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离.Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程.一个复杂的对象有大量的组成部分,比如汽车它有车轮.方向盘.发动机.以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过

java/android 设计模式学习笔记(一)---单例模式

前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使用的时候也会有一些坑. PS:对技术感兴趣的同鞋加群544645972一起交流 设计模式总目录 java/android 设计模式学习笔记目录 特点 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的使用很广泛,比如:线程池(threadpool).缓存(cache).对

Android:日常学习笔记(8)———探究UI开发(5)

Android:日常学习笔记(8)---探究UI开发(5) ListView控件的使用 ListView的简单用法 public class MainActivity extends AppCompatActivity { private String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","

java/android 设计模式学习笔记(7)---装饰者模式

这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是继承关系的一种替代方案之一,但比继承更加灵活.在现实生活中也可以看到很多装饰者模式的例子,或者可以大胆的说装饰者模式无处不在,就拿一件东西来说,可以给它披上无数层不一样的外壳,但是这件东西还是这件东西,外壳不过是用来扩展这个东西的功能而已,这就是装饰者模式,装饰者的这个角色也许各不相同但是被装饰的对

疯狂Android讲义 - 学习笔记(四)

Android应用通常有多个Activity,多个Activity组成Activity栈,当前活动的Activity位于栈顶.Activity相当于Java Web开发的Servlet.当Activity处于Android应用运行时,同样受系统控制.有生命周期. 4.1 建立.配置和使用Activity 4.1.1 Activity 建立自己的Activity需要集成Activity基类,在不同的应用场景,有时也能集成Activity的子类. Activity间接或直接继承了Context.Co