jni与ndk之简单使用

 先介绍环境:

window:win7 32位 jdk1.7

ubuntu 11.04 64位 jdk1.6 ndk-r8b

  

JNI(Java Native Interface)----java本地接口,它的好处是:允许java代码在java虚拟机里面相互操作使用其他语言(例如C、C++、汇编等等)编写的类库或者应用程序.

   什么时候用:当你的应用程序用java编写的时候没有办法完成所有的功能的时候,就要用到JNI了.(比如你需要在应用层驱动底层的硬件工作)

在此先介绍使用javah工具、arm-linux-gcc等工具生成.so的共享库文件,提供给上层的android应用.(需要window和ubuntu环境)

     1.先在eclipse中新建一个NdkC的android工程,修改MainActivity.java如下

package com.undergrowth.ndkc;

import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;

public class MainActivity extends Activity {

	private TextView textView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		textView=(TextView) findViewById(R.id.text);
		ArithOpera add=new ArithOpera();
		int result=add.add(10, 20); //调用本地方法
		textView.append("
 10+20="+result);
	}
}


提供本地算术运算的ArithOpera.java如下

  

package com.undergrowth.ndkc;
/*
 * 提供一个本地方法 计算两数之和
 */
public class ArithOpera {
	//本地加法方法
	public native int add(int a,int b);
	
	//加载共享库名为libarith.so  
	//遵循Unix的习惯  前缀lib 和后缀.so都不加 系统会自动加上
	static{
		System.loadLibrary("arith");
	}

}


  2.使用javah工具生成含有本地方法的头文件

     在cmd下进入到上面NdkC的目录下,如我的

       

   输入javah -classpath ./bin/classes/ com.undergrowth.ndkc.ArithOpera

就会在NdkC的目录下生成含有本地方法的头文件com_undergrowth_ndkc_ArithOpera.h

   内容许下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_undergrowth_ndkc_ArithOpera */

#ifndef _Included_com_undergrowth_ndkc_ArithOpera
#define _Included_com_undergrowth_ndkc_ArithOpera
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_undergrowth_ndkc_ArithOpera
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_undergrowth_ndkc_ArithOpera_add
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif


 

对上面的文件简单解释一下:

#include <jni.h>   包含jni.h头文件 jni.h头文件声明/定义了在c语言中使用的数据类型 接口函数 宏之类的 位于 Javajdk1.7.0_01include目录下

#ifndef _Included_com_undergrowth_ndkc_ArithOpera

#define _Included_com_undergrowth_ndkc_ArithOpera 

#endif

这两句还有最后面的一个#endif,用于保证头文件在预编译阶段只被包含一次

#ifdef __cplusplus

extern "C" {

#endif

.....

#ifdef __cplusplus

}

#endif

这几句代码是说如果是c++源文件,则使用c语言的方式进行编译和链接

中间的

/*
 * Class:     com_undergrowth_ndkc_ArithOpera
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_undergrowth_ndkc_ArithOpera_add(JNIEnv *, jobject, jint, jint);

是利用jni本地方法的命名规则生成的一个方法声明,规则如下

3.上面的操作都是在window环境下,接着在ubuntu11.04环境下使用交叉编译器生成.so文件

    将com_undergrowth_ndkc_ArithOpera.h拷贝到ubuntu环境中的 在com_undergrowth_ndkc_ArithOpera.h的同一目录中新建add.c文件 内容如下:

#include <jni.h>
#include "com_undergrowth_ndkc_ArithOpera.h"

JNIEXPORT jint JNICALL Java_com_undergrowth_ndkc_ArithOpera_add(JNIEnv *env,jobject obj,jint a,jint b)
{
	return (a+b);
}


   使用arm-linux-gcc进行编译 arm-linux-gcc  -I ~/java/jdk1.6.0_30/include/ -I ~/java/jdk1.6.0_30/include/linux/ -fPIC -c add.c -o add.o

      上面命令生成add.o的目标文件

 -I 用于指点add.c中用到的jni.h头文件的位置 第二个-I是因为jni.h中包含jni_md.h 而jni_md.h则在第二个位置

-fPIC(位置独立代码)用于指定生成与位置无关的代码,即在内存的任意地方都可以运行

上面生成add.o的文件后,在使用arm-linux-ld进行链接,生成.so的文件  arm-linux-ld -share add.o -o libarith.so

即生成了libarith.so的共享库 将之拷贝到NdkC的libs/armeabi/目录下  没有该目录则创建即可 该目录名不可为其他的

4.运行 效果如下

现在介绍使用ndk的方式达到上面的一样效果.下面的操作都是在ubuntu 11.04的环境中

先对ndk做个简单的介绍:ndk相当于是jni的超集,

主要做这么两件事: a.利用Android.mk生成一个jni兼容的共享库

                                 b.将生成的共享库复制到你项目的libs/armeabi/目录下

1.在eclipse中新建一个ndk1的android工程,将Ndk1Activity.java修改如下

package com.undergrowth.ndk;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class Ndk1Activity extends Activity {
    /** Called when the activity is first created. */
    private TextView textView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        textView=(TextView) findViewById(R.id.text);
        textView.append("
 10+20="+add(10,20));
    }
    
    public  native int add(int a,int b);
    static{
        System.loadLibrary("arith");
    }
} 

2.然后再ndk1的项目文件夹中新建jni目录(与src目录在同一级别),在jni中新建add.c源文件 内容如下:

#include <jni.h>

jint Java_com_undergrowth_ndk_Ndk1Activity_add(JNIEnv *env,jobject thiz,jint a,jint b)
{
    return (a+b);
}


同样使用上面提到的jni本地方法的命名规则 以Java_ 开头 接着是类的全名 接着是方法名 都用_(下划线)分隔

同时在jni的目录中新建Android.mk文件 用于告诉ndk的编译系统编译什么 如何编译   内容如下:

LOCAL_PATH    :=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    :=arith
LOCAL_SRC_FILES    :=add.c
include $(BUILD_SHARED_LIBRARY)


上面的Android.mk文件主要涉及到NDK的三个概念:

a.模块变量-----用于向编译系统描述你的模块

如LOCAL_PATH-----用于定位源代码在开发者目录中的位置

LOCAL_MODULE----用于定义你要生成的模块
LOCAL_SRC_FILES-----用于告诉编译系统你要编译的源文件

b.宏函数---必须要使用$(call <function name>)的方式使用,并且返回文本信息

   如$(call my-dir)-----my-dir----用于返回当前文件的目录

c.ndk提供的变量-----具有特定的功能

include $(CLEAR_VARS)-----CLEAR_VARS---指向一个gnu的特殊脚本文件,用于清除(LOACL_XXX)变量,LOCAL_PATH除外,因为所有的编译控制文件都会被解析成一个全局文件,而全局文件里面的所有变量均为全局的

include $(BUILD_SHARED_LIBRARY)------编译成共享库,即.so的后缀

3.进行编译,进入到ndk1的目录中,使用ndk-build(前提是你以前配置好了ndk的环境),会有如下信息,并会在ndk1的目录中的libs/armeabi/下找到libarith.so文件

 

u1@u1:~/java/workspace/ndk1$ ndk-build
Compile thumb  : arith <= add.c
SharedLibrary  : libarith.so
Install        : libarith.so => libs/armeabi/libarith.so


4.运行

 

      上面即是jni与ndk操作的简单实现.其实还可以使用JNI_OnLoad的方式进行编写,在之前的使用中,我倒是两种方式都用,用JNI_OnLoad主要是我觉得可以自己在C组件中定义函数名,只需要通过RegisterNatives进行注册即可,即把C组件中的函数与上层应用的函数进行一一的映射.

     JNI_OnLoad的具体使用,参见: http://blog.csdn.net/undergrowth/article/details/9163745

原文地址:https://www.cnblogs.com/liangxinzhi/p/4275608.html