关于JNI

JNI

  • NDK开发环境的搭建

    • 将NDK的路径拷贝到环境变量path中 cmd中运行ndk-build可验证是否添加成功
  • JNI_HelloWorld
  • 步骤
    • 1.创建Android工程
    • 2.java代码中声明native方法
    • 3.在工程根目录下创建jni文件夹,编写c代码,名字要对应
    • 4.编写Android.mk文件
    • 5.NDK编译生成动态链接库
    • 6.java代码load动态库,调用native代码
  • 实现
    • 通过一个点击事件来调用C代码
    • 在Activity中添加方法public native String hello();
    • 在该项目的根目录下添加文件夹 名为jni
    • 在jni文件夹下添加文件 hello.c(命名与Activity中的native方法名一样)
    • hello.c文件中的代码:

    • 在项目的根目录下启动cmd ndk-build,此时会出现错误Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk。缺少Android.mk文件
    • 在jni目录下创建Android.mk文件并添加

    LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)

    LOCAL_MODULE:= hello-jni//要生成的动态库

    LOCAL_SRC_FILES := hello-jni.c//源文件

    include $(BUILD_SHARED_LIBRARY)

    修改LOCAL_MODULE和LOCAL_SRC_FILES然后在 ndk-build 编译

    [armeabi] Compile thumb : helloJNI <= hello.c

    [armeabi] SharedLibrary : libhelloJNI.so

    [armeabi] Install: libhelloJNI.so => libs/armeabi/libhelloJNI.so

    生成动态库

    • java代码点击事件中 添加 System.loadLibrary(“helloJNI”);

      • 库的名称是 libhelloJNI.so 将lib和.so去掉 就可以

Javah生成头文件

  • 如果将native方法名改为public native String hello_from_jni();那么在c代码中的方法名是这样的:
  • jstring Java_com_example_jnihello_MainActivity_hello_from_jni(JNIEnv* env,jobject jobj)
  • ndk-build编译也不会报错,但是运行的时候会崩溃
  • No implementation found for native Lcom/example/jnihello/MainActivity;.hello_from_jni:()Ljava/lang/String;

  • 在c代码中将Java的全类名中的.变为_ ,原来有下划线的地方需要在后面加个1,改成这样的:
  • jstring Java_com_example_jnihello_MainActivity_hello_1from_1jni(JNIEnv* env,jobject jobj)
  • 但是如果native方法中有很多下划线和数字的话,这样修改起来就会显得很麻烦,需要一个便捷的方法来实现–javah
    • JDK1.6在工程的bin/classes目录下使用
    • JDK1.7在工程的src目录下使用
  • 使用方法:javah+空格+java全类名
    • 例如:native方法名为public native String hello_1_1_23_from_jni(); 在src目录下启动cmd,输入Javah com.example.jnihello.MainActivity就行了。将生成的.h文件移动到jni目录下。
    • 在c代码中原来的#include<jni.h>修改为#include "com_example_jnihello_MainActivity.h"并把c代码中的方法名修改为com_example_jnihello_MainActivity.h中的方法名

常见错误

  • 缺少Android.mk文件报错
  • c文件没有include导入jni.h的头文件
  • 方法的形参没有指定名称
  • 没有动态加载.so文件
  • 加载.so时名字写错
  • 不同cpu需要编译不用的.so文件
    • 针对不同cup编译需要Application.mk文件(jni目录下)

For example, to support hardware FPU instructions on ARMv7 based devices, use:

      APP_ABI := armeabi-v7a

Or to support ARMv8 AArch64 instruction set, use:

      APP_ABI := arm64-v8a

Or to support the IA-32 instruction set, use:

      APP_ABI := x86

Or to support the Intel64 instruction set (r1), use:

      APP_ABI := x86_64

Or to support the MIPS32 instruction set, use:

      APP_ABI := mips

Or to support the MIPS64 instruction set (r6), use:

      APP_ABI := mips64

Or to support all at the same time, use:

      APP_ABI := armeabi armeabi-v7a x86 mips arm64-v8a x86_64 mips64

Or even better, since NDK r7, you can also use the special value ‘all’ which means “all ABIs supported by this NDK release”:

      APP_ABI := all

Android.mk文件

#$:调用系统的工具链函数,当前的作用:调用当前目录
LOCAL_PATH := $(call my-dir)
#清除LOCAL环境变量并且初始化工具链工具,但是不会清除LOCAL_PATH
include $(CLEAR_VARS)
#要生成的.so文件名称,前面省略lib可以不写,但是后缀不需要写
LOCAL_MODULE    := hello-jni
#指向源文件,多个源文件用空格连接
LOCAL_SRC_FILES := hello-jni.c
#编译成动态链接库BUILD_SHARED_LIBRARY,后缀.so ,文件比较小
#BUILD_STATIC_LIBRARY编译成静态链接库,后缀.a,文件比较大
include $(BUILD_SHARED_LIBRARY)

NDK简便开发流程

  • Windows ->Preferences->NDK->添加NDK路径

  • 一开始我的Eclipse没有NDK这个选项,百度之后解决。
  • 新建Android Progect
  • 右键Android Tools ->Add Native Support 填上库的名字,这时候会生成jni文件夹,其中有Android.mk文件和一个cpp文件,关联源码
    • 右键->Properties->C/C++ General->Paths and Sysbols->includes->add->File system->NDK安装路径->platforms->选择版本->arm->include ->确定
  • 在项目工程的src文件夹下 javah 生成头文件,移动到jni目录下
  • 在c文件中导入这个头文件并实现native方法

Java与C之间的数据传递

  • 在C代码中打印LOG信息,需要在C代码中添加头文件#include<android/log.h>并使用宏定义#define LOGD(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

    还需要在Android.mk文件中增加LOCAL_LDLIBS += -llog

  • 在C代码中直接使用定义好的LOGD,并将要打印的信息传入即可。
  • 将Java中的String类型转换成C中的char*类型
    char* jstringTostring(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes",
            "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char*) malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
    
  • JNI中对数组的操作
    • 获取数组的长度

      • int size = (*env)->GetArrayLength(env, jarray);
    • 获取数组中每个元素

    jint* arrayElement = (*env)->GetIntArrayElements(env, jarray, JNI_FALSE);

    for (i = 0; i < size;i++){

    jint s = *(arrayElement + i);

    }

C调用Java

反射(Reflection)

  • 动态获取类的信息以及动态调用对象的方法
  • Java反射机制主要提供的功能:
    • 1.在运行时判断任意一个对象所属的类
    • 2.在运行时构造任意一个类的对象
    • 3.在运行是判断任意一个类所具有的成员变量和方法
    • 4.在运行是调用任意一个对象的方法
  • Java Reflection API简介
    • Class类:代表一个类,位于java.lang包下。
    • Field类:代表类的成员变量(成员变量也称为类的属性)。
    • Method类:代表类的方法。
    • Constructor类:代表类的构造方法。
    • Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
  • 反射步骤:
    • 1.得到字节码文件
    • 2.得到字节码对应的方法
    • 3.得到这个类的实例
    • 4.执行方法
  • MainActivity.java
package com.example.ccalljavademo;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    private JNI jni;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        jni = new JNI();
    }

    public void helloFromJava(View v) {
        jni.cMethods_call_helloFromJava();
    }

    public void add(View v) {
        jni.cMethods_call_add();
    }

    public void printString(View v) {
        jni.cMethods_call_printString("sad");
    }

    public void callStatic(View cv) {
        jni.cMethods_call_sayHello();
    }
}
  • JNI.java
package com.example.ccalljavademo;

public class JNI {

    {
        System.loadLibrary("CCallJavaDemo");
    }
    /**
     * C调用Java空方法
     */
    public void helloFromJJava() {
        System.out.println("Hello Form Java");
    }

    /**
     * C调用Java两个int参数方法
     * @param x
     * @param y
     * @return
     */
    public int add(int x, int y) {
        int result = x + y;
        System.out.println("被c调用" + result);
        return result;

    }

    /**
     * C调用Java参数为String方法
     * @param s
     */
    public void printString(String s) {
        System.out.println(s);
    }

    /**
     * 静态方法
     */
    public static void sayHello (){
        System.out.println("Hello");
    }

    /**
     * 让C语言调用JNI.java中的 helloFromJava
     */
    public native void cMethods_call_helloFromJava();

    /**
     * 调用JNI.java中的add方法
     */
    public native void cMethods_call_add();

    /**
     * 调用JNI.java中的printString方法
     * @param s
     */
    public native void cMethods_call_printString(String s);
    /**
     * 调用静态方法
     */
    public native void cMethods_call_sayHello();
}
  • CCallJavaDemo.c
#include "com_example_ccalljavademo_JNI.h"
#include <stdio.h>
#include<Stdlib.h>

/**
 * 让c语言调用Java中的HelloFromJava
 */

JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1helloFromJava(
        JNIEnv *env, jobject jobj) {
//  1.得到字节码文件
//     jclass      (*FindClass)(JNIEnv*, const char*);
//  第二个参数 是全类名 com.example.ccalljavademo.JNI 将.换成/

    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");

//  2.得到字节码对应的方法
//      jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
//  第三个参数:方法名
//  第四个参数:方法签名
    jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "helloFromJJava",
            "()V");
//  3.得到类的实例
//      jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject obj = (*env)->AllocObject(env, jclazz);
//  4.执行方法
//      void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env, obj, jmethodid); //成功调用JNI.java中的helloFromJava方法

}

/***
 *C调用Java中带有两个int参数的方法
 *
 */JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1add(
        JNIEnv *env, jobject jobj) {
    //  1.得到字节码文件
    //     jclass      (*FindClass)(JNIEnv*, const char*);
    //  第二个参数 是全类名 com.example.ccalljavademo.JNI 将.换成/

    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");

    //  2.得到字节码对应的方法
    //      jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //  第三个参数:方法名
    //  第四个参数:方法签名
    jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "add", "(II)I");
    //  3.得到类的实例
    //      jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject obj = (*env)->AllocObject(env, jclazz);
    //  4.执行方法
    //          jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallIntMethod(env, obj, jmethodid, 99, 12); //成功调用JNI.java中的add方法
}

JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1printString(
        JNIEnv *env, jobject jobj, jstring jstr) {
    //  1.得到字节码文件
    //     jclass      (*FindClass)(JNIEnv*, const char*);
    //  第二个参数 是全类名 com.example.ccalljavademo.JNI 将.换成/

    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");

    //  2.得到字节码对应的方法
    //      jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //  第三个参数:方法名
    //  第四个参数:方法签名
    jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "printString",
            "(Ljava/lang/String;)V");
    //  3.得到类的实例
    //      jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject obj = (*env)->AllocObject(env, jclazz);
    //  4.执行方法
    //    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env, obj, jmethodid, jstr); //成功调用JNI.java中的add方法
}

JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1sayHello(
        JNIEnv *env, jobject jobj) {
    //  1.得到字节码文件
    //     jclass      (*FindClass)(JNIEnv*, const char*);
    //  第二个参数 是全类名 com.example.ccalljavademo.JNI 将.换成/

    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");

    //  2.得到字节码对应的方法
    //      jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //  第三个参数:方法名
    //  第四个参数:方法签名
    jmethodID jmethodid = (*env)->GetStaticMethodID(env, jclazz, "sayHello",
            "()V");
    //  3.得到类的实例
    //      jobject     (*AllocObject)(JNIEnv*, jclass);
//          jobject obj = (*env)->AllocObject(env, jclazz);
    //  4.执行方法
//              void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    (*env)->CallStaticVoidMethod(env, jclazz, jmethodid);
}
时间: 2024-10-06 03:56:41

关于JNI的相关文章

Delphi使用android的NDK是通过JNI接口,封装好了,不用自己写本地代码,直接调用

一.Android平台编程方式:      1.基于Android SDK进行开发的第三方应用都必须使用Java语言(Android的SDK基于Java实现)      2.自从ndk r5发布以后,已经允许完全用C/C++ 来开发应用或者游戏,而不再需要编写任何Java 的代码   Android程序运行在Dalvik虚拟机中,NDK允许用户使用类似C / C++之类的原生代码语言执行部分程序. 二.跨平台移动开发   Delphi使用android的NDK是通过JNI接口,封装好了,不用自己

[转]Ubuntu下使用Jni开发例子

http://www.cnblogs.com/zxglive2006/archive/2012/01/15/2323110.html 先用eclipse 创建 Java Project; 然后直接在项目中添加Prompt.java文件,放在default package下(最好不要添加包,否则容易出错). 1. 编写Java文件,在其中声明native方法, 并通过static 语句块加载动态链接库,示例Prompt.java代码如下: class Prompt { private native

Android中JNI调用时出现accessed stale local reference的问题

之前在做一个native的模块时遇到这样一个问题: 代码运行在android2.3上没有任何问题,可是在4.2上运行时报出了:JNI ERROR (app bug): accessed stale local reference 的错误. 后来在StackOverflow上找到了问题的答案.简单来说就是  4.0以上的android系统GC在垃圾回收时为了减少内存碎片,会对内存进行整理,整理时必然会移动对象的内存地址,这时C代码的指针还指向原来对象的地址,这时该对象已经被移动到了其他位置,因此会

JTI + JNI,为Java程序提供获取JVM内部信息的通道

首先,JTI是啥? HotSpot JVM是使用C++写的,在操作系统层面来看,java.exe进程与其他进程并无特别之处.任何一个进程都可以加载第三方的DLL,JTI就是java.exe开放出来的向Java.exe进程注入dll的接口.也就是说,开发者根据JTI定义好的规范,用C++写一个dll,这个dll就可以被java.exe进程加载了[启动jvm的时候要加上-agentlib参数]. JTI的详细资料参见以下网址:http://docs.oracle.com/javase/7/docs/

JNI基础学习

1.JNI(Java Native Interface): 它允许Java代码和其他语言写的代码进行交互,JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了.下图是java与c的交互表现: 2.JNI的编写与编译流程: 1)编写java文件,声名Native方法 2)javac编译java文件 3)javah -jni 编译生成Native方法的头文件 4)用c/c++实现Native方法 5)编译Native方法 6)在JVM运

JNI/NDK开发指南(三)——JNI数据类型及与Java数据类型的映射关系

转载请注明出处:http://blog.csdn.net/xyang81/article/details/42047899 当我们在调用一个Java native方法的时候,方法中的参数是如何传递给C/C++本地函数中的呢?Java方法中的参数与C/C++函数中的参数,它们之间是怎么转换的呢?我猜你应该也有相关的疑虑吧,咱们先来看一个例子,还是以HelloWorld为例: HelloWorld.java: package com.study.jnilearn; class MyClass {}

android使用c通过jni回调java

很多场合都有这样的需求,由于以前都是java调用c的接口,没有做过回调,今天花了大半天时间把这个流程跑通了,记录一下,以备后用.这里发句牢骚,那些网上分享出来的代码,请问你们确实是能正常工作吗?还有查来查去都是那几份,大家转载精神可嘉啊 jni相关头文件代码 /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_example_ndktest_CallbackTest */ #

jni ndk 入门

1. Linux环境模拟,下载sygwin 安装,选择devl 和shell -> install sygwin 中的配置ndk环境,进入安装目录c:/cygwin64 etc/profile文件配置ndk的环境 //37行 PATH="/usr/local/bin:/usr/bin:/cygdrive/d/android-ndk-r9d-windows-x86_64/android-ndk-r9d${PATH:+:${PATH}}" 2. 下载ndk 3. 开发,参考于ndk/

在 Linux 平台下使用 JNI

引言 Java 的出现给大家开发带来的极大的方便.但是,如果我们有大量原有的经过广泛测试的非 Java 代码,将它们全部用 Java 来重写,恐怕会带来巨大的工作量和长期的测试:如果我们的应用中需要访问到特定的设备,甚至是仅符合公司内部信息交互规范的设备,或某个特定的操作系统才有的特性,Java 就显得有些力不从心了.面对这些问题,Sun 公司在 JDK1.0 中就定义了 JNI 规范,它规定了 Java 应用程序对本地方法的调用规则. 实现步骤及相关函数使用 本文将一步步说明在 Linux 平

Java中JNI的使用详解第一篇:HelloWorld

今天开始研究JNI技术,首先还是老套路,输出一个HelloWorld:具体流程如下:在Java中定义一个方法,在C++中实现这个方法,在方法内部输出"Hello World",然后再回到Java中进行调用.分为以下步骤: 第一步:在Eclipse中建立一个类:JNIDemo 1 package com.jni.demo; 2 public class JNIDemo { 3 //定义一个本地方法 4 public native void sayHello(); 5 public sta