Android中JNI开发的入门知识:一

介绍一下Android中JNI开发的入门以及在Eclipse中使用相关工具进行开发:


一、基本概念

1、什么是JNI:java native interface( java与本地语言的接口规范 )
2、应用JNI的场景:调用C代码去操作硬件、对计算效率要求非常高的场合、对安全性要求比较高的场合
3、交叉编译:在一个平台(ios,window,linux / intel,arm,mips)上,编译出另一个平台可以运行的应用。
4、NDK: ( native develop kits ) 本地语言开发工具集,是谷歌公司给我们提供 
5、CDT:eclipse中运行C语言的插件

二、学习JNI

1、学习JNI的要求:

* 熟悉java语言    * 熟悉 C语言 * 掌握JNI调用的规范

2、c语言和java语言有关数据类型的比较:

##JAVA中的基本数据类型
int      占用 4 个字节 
byte     占用 1 个字节
long     占用 8 个字节
float    占用 4 个字节
doublt   占用 8 个字节
boolean  占用 1 个字节
char     占用 2 个字节
short    占用 2 个字节
## C语言中的数据类型
int    类型占用字节数:4
float  类型占用字节数:4
double 类型占用字节数:8
short  类型占用字节数:2
char   类型占用字节数:1
long   类型占用字节数:4


比较结果:
* 在java语言和C语言中 int ,short,float,double 这些数据类型是一样的。
* java语言中的byte 和boolean 可以用C语言中的char 代替
* java语言中的char 类型要用C语言中的short 代替
* java语言中long 要用C语言中long long 代替
* java语言中的boolean 用C语言中的char 代替,如果为1 表示真,0表示假
 
 

三:NDK的目录结构

-- docs : ndk 自带的帮助文档
-- build : 编译时所需要的工具
* .sh 文件是linux 系统是的可运行脚本,类似于windows中bat  批处理命令
-- platforms : 不同的android版本与同的CPU之间,编译时,所需要的文件
* jni.h   位置在:android-ndk-r9b\platforms\android-xx\arch-arm\usr\include
-- prebuilt : 对不同的CPU进行预编译,时使用
-- samples : 提供的一些小例子
-- sources : NDK的源码
-- tests: ndk 自带的一些测试用例
-- toolchains: 不同平台编译所需要的工具链
--ndk-build.cmd :在ndk目录结构的根目录下边。在widnows中可以执行的命令, 对本地代码进行编译(编译生成.so文件)。
在命令行中使用ndk-build命令需要做一下必要的配置:需要将ndk的目录路径配置在电脑的环境变量中去,这样可以在任意路径下使用该命令。
ndk-build命令会将项目中写好的c文件编译成.so文件,注意需要进入.c文件的目录路径使用该命令。

四、JNI的使用步骤

1、在java类中声明native 方法 

/**
* 通过关键字 native 声明一个本地方法
* @return
*/
private native String helloJni();

2、在C代码中实现native方法 

1)在当前项目中创建文件夹名称固定为jni ,并在该文件夹中新建一个.c的文件。

注:如果在eclipse中可以通过以下的方法来快捷生成对应的文件
a、先关联NDK:

b、利用eclipse的工具来替我们生成对应的文件:

具体的过程如下:(注意这样必须是设置NDK的路径后才可以使用)
右键项目->Android Tools -> Add Native Support -> 写入名称(我这里写的是helloJNI)->Finish
Eclipse会替我们生成jni文件夹其中包括两个文件:Android.mk和helloJNI.cpp(这里生成的c++文件,这里将后缀名改为.c,同时需要将
Android.mk中的helloJNI.cpp也改为helloJNI.c)

2)声明与native方法对应的C方法

/**
* 1、 查询jni.h 头文件,找到与java中String 类型匹配的c语言的类型  jstring ,就是函数返回值的类型
* 2、声明函数名:
* 		Java_包名_类名_方法名(注意包名中的点都要换成下划线)
* 3、添加二个固定的参数:
* 	JNIEnv* env -- jni开发环境的引用
*	jobject -- 是调用该函数的java对象的引用
*/
jstring Java_zz_itcast_jnihello1_MainActivity_helloJni(JNIEnv* env,jobject obj){}
	注:这里的方法名称非常容易写错,因此建议使用命令javah来自动生成具体的方法为:
		如果是java1.6 javah命令操作的是class 文件,应在\bin\classes\ 目录执行
		如果是java1.7 javah命令操作的是java 文件, 应在 \src\ 目录中执行
		步骤:
			根据自己的java版本进入项目中对应的目录,打开命令行,使用命令javah+空格+类的全名
			这里我使用的命令为:javah com.example.myjnitest.MainActivity
			命令完成后会生成一个com_example_myjnitest_MainActivity.h的文件,将其打开内容如下:
#include <jni.h>
/* Header for class com_example_myjnitest_MainActivity */
#ifndef _Included_com_example_myjnitest_MainActivity
#define _Included_com_example_myjnitest_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myjnitest_MainActivity
 * Method:    helloJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MainActivity_helloJNI
  (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
这里的方法名就自动生成了,我们将JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MainActivity_helloJNI(JNIEnv * env, jobject  obj)
拷贝出来放到我们的.c文件中就是对应的方法名称,这里要注意加上方法签名中形参的名称	

 3)实现该C方法,并返回对应的数据类型

char * msg = "hello i am from c"; // 声明一个要返回的字符串
// 查询 jni.h中 JNINativeInterface 结构体:
// jstring     (*NewStringUTF)(JNIEnv*, const char*); NewStringUTF是一个指向返回jstring类型的函数的指针
return (*env)->NewStringUTF(env,msg);//这里查看jni.h可以知道*env表示JNINativeInterface 这个结构体,调用它的方法
									//NewStringUTF,也可以写成(*(*env)).NewStringUTF(env,msg);

 4)注意:在C文件中 添加  #include <jni.h>

3、将C代码编成可执行的二进制文件

1、在jni目录中添加 Android.mk 的配置文件,( 该配置文件会告诉ndk,我们要编译哪个模块,以及哪些源文件。如果在Eclipse中生成.c文件的话就会同时生   成这个文件,就不需要再次重写了),内容参照docs文档(NDK中)
	    LOCAL_PATH := $(call my-dir)
	    include $(CLEAR_VARS)
	
	    LOCAL_MODULE    := helloJNI  // 我们定义的模块名 最终编译的 so 文件,以此命名
	    LOCAL_SRC_FILES := helloJNI.c // 该模块包含的c文件 如果有多个文件,以空格间隔
	    include $(BUILD_SHARED_LIBRARY)
2、在.c文件和Android.mk同级目录中,执行ndk-build 命令(命令为:ndk-build)
		*注意:ndk-build.cmd文件是ndk 安装目录根目录中的文件,应将该文件的目录,添加至系统环境变量PATH ,中以方便在其他目录中调用 
      3、注意,这里如果想要编译成支持所有系统的so文件需要在项目jni目录下新建Application.mk文件
		其中的内容为:APP_ABI := all
	 这里如果在Eclipse中使用了NDK集成开发环境就不要我们手动的执行第2步中的ndk-build命令了,因为运行时Eclipse会帮我们进行编译的

4、在JAVA类中,加载编译生成的so文件

static{ // 加载so 文件  System.loadLibrary("helloJNI ");

完成以上所有步骤后,就可以在java类方法中调用native方法了。

下边是我的文件具体代码:
MainActivity.java文件的内容:
package com.example.myjnitest;

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

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	public void btnClick(View v){
		Toast.makeText(this, helloJNI(), 1).show();//调用native方法
	}
	
	private native String helloJNI();
	
	static{
		//静态代码库,加载我们的so文件
		System.loadLibrary("helloJNI");
	}
}

activity_main.xml文件的内容:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:orientation="vertical" >

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="btnClick"
        android:text="点击调用C代码" />

</LinearLayout>


helloJNI.c的内容:
注意:如果如下代码在项目中报错是因为没有关联源码(我们需要关联一下代码)
	关联的代码的步骤:
	选中项目:快捷键Alt+Enter -> C/C++ General -> Paths and Symbols -> 在Includes选项卡中选中add(将对于ndk路径中的代码关联) ->ok
	我的关联路径为:D:\Programandroid-ndk-r9b\platforms\android-19\arch-arm\usr\include其中platforms以后的路径可以根据需要选不同的版本及不同的	系统的代码。如果还是报错,在确定代码无误的情况下,将Problems下的错误delete掉后直接运行,这个错误是由于Eclipse自身的问题导致的,我们不必考虑
#include <jni.h>
#include<stdlib.h>
#include<jni.h>

//  实现 java中声明的native 方法
// 	private native String helloJni();

/**
 * 1\ 查询jni.h 头文件,找到与java中String 类型匹配的c语言的类型  jstring ,就是函数返回值的类型
 * 2\声明函数名:
 * 		Java_包名_类名_方法名
 * 3\添加二个固定的参数:
 * 		JNIEnv* env -- jni开发环境的引用
 * 		jobject -- 是调用该函数的java对象的引用
 */
JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MainActivity_helloJNI(JNIEnv* env,jobject obj){

	char * msg = "hello i am from c";

	// 查询 JNINativeInterface 结构体中内容:
	// jstring     (*NewStringUTF)(JNIEnv*, const char*);

	//return (*(*env)).NewStringUTF(env,msg);

	return (*env)->NewStringUTF(env,msg);
};


Android.mk的内容:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := helloJNI
LOCAL_SRC_FILES := helloJNI.c

include $(BUILD_SHARED_LIBRARY)


Application.mk 的内容:
APP_ABI := all

下面附上我的例子地址:有需要的可以点击免费下载:
点击打开链接
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值