android ndk开发


理论部分:

android ndk就是Java里面jni技术,当然会略有点区别,但总体一致。android 项目要使用ndk开发主要有两大原因:

1,能够使用已有的类库(很多开源库c++/c为基础)在基础上进行开发,有些功能java无法实现(解码库)或者效率问题(这个比较少)

2,考虑源代码保护,c++/c编译成类库比Java更难破解


学习ndk开发应用程序首先掌握jni语法,可以参考jni手册或者oracle网站进行了解

首先看一下数据类型:

Java类型    本地类型         描述

boolean    jboolean       C/C++8位整型

byte       jbyte          C/C++带符号的8位整型

char       jchar          C/C++无符号的16位整型

short      jshort         C/C++带符号的16位整型

int        jint           C/C++带符号的32位整型

long       jlong          C/C++带符号的64位整型e

float      jfloat         C/C++32位浮点型

double     jdouble        C/C++64位浮点型

Object     jobject        任何Java对象,或者没有对应java类型的对象

Class      jclass         Class对象

String     jstring        字符串对象

Object[]   jobjectArray   任何对象的数组

boolean[]  jbooleanArray  布尔型数组

byte[]     jbyteArray     比特型数组

char[]     jcharArray     字符型数组

short[]    jshortArray    短整型数组

int[]      jintArray      整型数组

long[]     jlongArray     长整型数组

float[]    jfloatArray    浮点型数组

double[]   jdoubleArray   双浮点型数组

和Java类型一一对应,了解更多详细内容,自行查询jni手册

jni函数:看一个简单的函数

// 返回字符串"hello load jni"
JNIEXPORT jstring JNICALL native_hello(JNIEnv* env,jobject thiz, jclass clazz)
{
    return (*env)->NewStringUTF(env, "hello load jni.");
}
说一下参数含义:

其中 JENEnv* 代表的是java环境 通过这个环境可以调用java的方法,jobject 表示哪个对象调用了 这个c语言的方法, thiz就表示的是当前的对象

JNI方法注册到jvm有两种:

1,静态注册(方法名称为Java_packegeName_包含native方法声明class_native method name)

2,动态注册(通过JNI_OnLoad()实现,jvm启动时调用该方法,在该方法内注册方法签名即可)后面介绍

两种方法的区别如下:

静态:先由Java得到本地方法的声明,然后再通过JNI实现该声明方法。

动态:先通过JNI重载JNI_OnLoad()实现本地方法,然后直接在Java中调用本地方法。


ndk编译:

1,需要下载ndk开发包,解压配置环境变量,和sdk配置差不多

下载地址:http://developer.android.com/tools/sdk/ndk/index.html

2,下载cygwin软件在windows下模拟Linux环境编译器

下载地址:http://www.cygwin.com/

相关文章:http://blog.csdn.net/buildon/article/details/711536

http://www.cnblogs.com/playing/archive/2011/07/14/2106727.html

编译.so类库

相关文章:http://blog.csdn.net/android_panda/article/details/7607946

http://blog.csdn.net/wangkuifeng0118/article/details/7311724




@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	//	setContentView(R.layout.activity_main);
		 TextView  tv = new TextView(this);
	        tv.setText( stringFromJNI() );
	        setContentView(tv);
	}

	 /* A native method that is implemented by the
     * 'hello-jni' native library, which is packaged
     * with this application.
     */
    public native String  stringFromJNI();

    
    static {	//导入的lib名去掉前面的lib
        System.loadLibrary("hello-jni");
    }

2.build下项目,打开cmd,cd到该工程bin目录下,
D:\>cd D:\android\rayn\TestJni\bin

D:\android\rayn\TestJni\bin>javah -jni com.stay.jni.CallNative

然后在bin目录下会出现一个.h文件这是声明的头文件

3.我们要的是里面声明的方法,把我们要写的c文件改成相同的方法名就可以了。

4.在工程里建个jni目录,将改好的c文件和mk文件(随便copy一个,改下路径和lib名就好啦)copy进去


常见错误:

错误1:当输入cd &NDK时出现no such file or Director时

原因1:在配置目录下cygwin\home\Andy\下的.bash_profile时配置的ndk所在路径有错误(

正确格式NDK=/cygdrive/d/adt-bundle-windows-x86/android-ndk-r8e

export NDK)

原因2:没有在系统环境变量中配置ndk(与sdk路径配置一样)


错误2:native method not found

原因:.c或.cpp内的函数名与Javah 编译生成包名不一致所导致


关于ndk详细介绍,可参考这篇文章

http://blog.chinaunix.net/uid-10008293-id-2972294.html

http://blog.csdn.net/zhangjie201412/article/category/1086150


第一步:打开命令行切换目录到xx.java目录下 javac命令编译:


第二部编译头文件:当执行javah -jni xxx文件是会出现找不到文件情况

解决办法:将目录切换到项目中src下(例如项目目录E:\workspace4.2\HelloJavaJni\src\com\sample\jni)


即可在src目录下生成xx.h头文件

问题原因:环境变量的设置不正确,正确设置如下:
path为"%JAVA_HOME%\bin"
ClassPath设置为".;%JAVA_HOME%\lib"
JAVA_HOME为"E:\Java\jdk1.6.0_13" (JDK的安装路径)
另外:需要先编译,后生成头文件:


下面介绍一下JNA技术:

JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。使用JNI调用共享类库(.dll/.so文件)是非常麻烦的事情,既需要编写java代码,又要编写C语言的代理方法,这其中需要很多数据类型的转换,是让人非常头痛。JNA框架就是为了解决这些问题和繁琐的事情而开发的,
它提供一组Java工具类用于在运行期动态访问系统本地共享类库而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,
JNA将自动实现Java接口到native function的映射,大大降低了Java调用本体共享库的开发难度。JNA与.NET平台上的P/Invoke机制一样简单和方便。

使用JNA的强大功能方便地调用动态链接库中的C函数,需要下载一个jar包,https://github.com/twall/jna
假设有一个动态链接库: Jna.dll。里面有这样一个函数

void sayHello(char * name){
    printf("C Code Start...\n");
    printf("Hello! Mr %s.\n",name);
    printf("C Code End.\n");
}
为了调用这个函数,使用JNA,我们需要编写下面的JAVA代码:
public interface ICnblogsJna extends Library {
    // 接口实例
    ICnblogsJna INSTANCE = (ICnblogsJna) Native.loadLibrary("CnblogsJna",ICnblogsJna.class);

    // 与C代码映射的函数
    public void sayHello(String name);
}

接口内部需要一个公共静态常量INSTANCE, 通过这个常量,就可以获得这个接口的实例,从而使用接口的方法。也就是调用动态链接库CnblogsJna.dll中的sayHello函数了。
 如果使用JNI,你需要使用System.loadLibrary方法,来加载我们专为JNI编写的动态链接库,这个动态链接库实际上是我们真正需要的动态链接库的代理。使用JNA是,
需要用JNA类库的Native类的loadLibrary函数,是直接把我们需要的动态链接库载入进来。使用JNA,我们不需要编写作为代理的动态链接库,不需要编写一行原生代码。
Native类的loadLibrary方法有两个参数:第一个参数是.dll或者.so文件的名字,但不带后缀名。这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。
第二个参数是本接口的Class类型,JNA通过这个Class类型,根据指定的dll/.so文件,动态创建接口的实例
            
 // 调用动态链库中的sayHello函数
  ICnblogsJna.INSTANCE.sayHello("jna sayhello test!");
 由于跨平台和跨语言尤其自身无法克服的确定,所以尽量少跨平台、跨语言传递数据。如果必须这样做,也尽量使用简单的数据类型。如果有复杂的数据类型需要在Java和原生函数中传递,
那么我们就必须在Java中模拟这种复杂的原生类型。这将大大增加实现的难度,甚至无法实现。如果在Java和原生函数间存在大量的数据传递,一方面,会损失程序的性能

;另一方面会造成内存碎片,Java调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这些Java数据。这些数据,JVM的GC不能管理,就会造成内存碎片。




posted @ 2013-04-23 16:18  HappyCode002  阅读(148)  评论(0编辑  收藏  举报