Learning Android 第十五章 NDK 翻译二

来源:互联网 发布:本草纲目 辣妹子 知乎 编辑:程序博客网 时间:2024/05/19 02:04
JNI头文件
下一个步骤就是在Fiblib Java文件的基础上创建C的头文件。如何做到呢,我们用Java的标准javah工具。前提是你要安装上Java开发工具集(JDK),你会在JDK/bin目录下找到这个工具。现在让我们来创建C的头文件吧,去你的项目的bin目录执行:
[Fibonacci/bin]> javah -jni com.marakana.FibLib
javah-jni 把一个Java类作为参数。并不是所有的类都默认在类路径,所以很容易改变目录为你项目的bin目录。到这里我们假设当前工作目录为Java类路径并且javah -jni com.marakana.FibLib能够工作。
结果应该是生成一个名为com_marakana_FibLib.h的新文件。这就是我们需要实现下一步的C头文件。在实现我们的本地文件之前,让我们管理我们的项目一小下。虽然Eclipse帮我们做了很多事,如安装Android应用程序目录等,但它并没有提供很高级别和自动化的支持给NDK开发者。我们接下来将要手动做两个步骤。
第一步,在你的Eclipse中Fibonacci项目中创建一个名为jni的目录,这里就是用来存放所有你的本地代码和相关文件的地方。在Eclipse的Package Explorer中选择Fibonacci项目,鼠标右键选择New->Folder,这样就创建了目录了。
第二步,把新的头文件放进这个文件夹。命令为:
[Fibonacci/bin]> mv com_marakana_FibLib.h ../jni/


下面我们来看看这个文件:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_marakana_FibLib */#ifndef _Included_com_marakana_FibLib#define _Included_com_marakana_FibLib#ifdef __cplusplusextern "C" {#endif/** Class: com_marakana_FibLib* Method: fibN* Signature: (I)J*/JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN(JNIEnv *, jclass, jint);/** Class: com_marakana_FibLib* Method: fibNI* Signature: (I)J*/JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI(JNIEnv *, jclass, jint);#ifdef __cplusplus}#endif#endif

可以看到,这个文件时自动生成的,并不需要程序员直接修改。这里你还会观察到我们已经实现的两个本地函数的签名。
...
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN
(JNIEnv *, jclass, jlong);
...
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI
(JNIEnv *, jclass, jlong);
...


这些是标准的JNI签名。上述两个本地函数fibN和fibNI是按照命名约定(naming convention)并通过Java中的类com.marakana.FibLib包含的函数自动生成的。还可以看到这两个函数都返回jlong,一个JNI标准的整型。
它们的输入参数也很有意思:JNIEnv, jclass, 和 jlong。前两个参数通常是Java类的部分,为本地代码创建接口。JNIEnv是虚拟机环境的指针。第二个参数是指向函数所在的类或对象,jclass是类方法,jobject是实例方法。第三个参数jlong,是我们输入斐波那契算法的那个数,也就是我们的n。
我们已经有了这个头文件,是时候提供它的C语言实现了。




C语言实现
我们将要创建一个C文件用来实现我们的本地算法。为简单起见(for simplicity),我们把它起名为fib.c。像之前的头文件一样,它也放在jni的文件夹里。用鼠标右键点击jni文件夹,选择New->File来创建它,保存为fib.c。
注意:
当你打开C语言文件的时候可能会出现在Eclipse的外面用其他编辑器打开文件的情况。这是因为Java版的Eclipse默认是不支持C语言开发的。你可以用C语言开发工具扩展你的Eclipse,打开Eclipse,Help->Install New Software。或者,就直接用文件右键选择文本编辑器中选择标准的Eclipse文本编辑器(standard Eclipse text editor)打开。


接下来,我们提供C语言的斐波那契算法实现,请看Example15-2 。这个C语言版本几乎和Java版本是一样的(identical to)。
Example 15-2. jni/fib.c

#include "com_marakana_FibLib.h" /*(注释一)*//* 递归斐波那契算法(注释二)*/long fibN(long n) {if(n<=0) return 0;if(n==1) return 1;return fibN(n-1) + fibN(n-2);}/*迭代斐波那契算法(注释三)*/long fibNI(long n) {long previous = -1;long result = 1;long i=0;int sum=0;for (i = 0; i <= n; i++) {sum = result + previous;previous = result;result = sum;}return result;}/*头文件中生产的JNI方法的签名(注释四)*/JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN(JNIEnv *env, jclass obj, jlong n) {return fibN(n);}/*头文件中生产的JNI方法的签名(注释五) */JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI(JNIEnv *env, jclass obj, jlong n) {return fibNI(n);}

注释一:我们引入com_marakana_FibLib.h,这个头文件是我们调用javah -jni com.marakana.FibLib生成的。
注释二:实际的递归斐波那契算法。这个与Java版本非常相似。
注释三:迭代版的斐波那契,怎么样,又和Java版的很像吧。
注释四:这是JNI为我们提供的函数。从com_marakana_FibLib.h复制粘贴的原型,增加了变量名并调用相应的C语言函数来做成了这样子。
注释五:同样的,这是迭代方法的签名。
现在我们就把C语言版的斐波那契实现了,我们将要构建成共享库。那么,我们就需要一个合适的makefile。


Makefile
为了构建本地库,Android.mk makefile 必须要描述我们的文件。请看Example 15-3 。
Example 15-3. jni/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := fib
LOCAL_SRC_FILES := fib.c
include $(BUILD_SHARED_LIBRARY)


makefile是标准Android make系统的一部分。所有我们添加的文件,是指定的输入文件(fib.c)和指定的输出文件(fib模块)。我们指定的模块的名称很重要,它将会决定以操作系统约定为基础的库的名称。举个例子,在基于ARM的系统,输出是libfib.so文件。
一旦我们有了这个makefile,我们就做好了构建初始化的准备了。


构建共享库
假定你已经安装好了NDK,那你就可以运行ndk-build在你的项目路径来构建本地共享库了。这里的ndk-build是一个工具,在你的NDK安装目录就可以找到它。我们假定你已经把这个目录写进了环境变量路径。
这时候(at this point),你可能已经有了一个名为lib的子目录包含了你的共享库。当你在下一部分配置斐波那契应用的时候,这个库会被打包成APK的一部分。


注意:
这个共享库编译后默认运行在模拟器上,所以它是基于ARM架构的。


最后,我们需要一个应用程序来使用这个库。


斐波那契Activity
这个activity用来询问用户输入一个数字,然后就计算这个数的四种斐波那契值。而且在屏幕上打印出计算时间和结果。这个activity基本上使用FibLib类继而本地部分转向使用libfib.so。看Example 15-4的代码。


Example 15-4. FibActivity.java

package com.marakana;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class Fibonacci extends Activity implements OnClickListener {TextView textResult;Button buttonGo;EditText editInput;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// Find UI viewseditInput = (EditText) findViewById(R.id.editInput);textResult = (TextView) findViewById(R.id.textResult);buttonGo = (Button) findViewById(R.id.buttonGo);buttonGo.setOnClickListener(this);}public void onClick(View view) {int input = Integer.parseInt(editInput.getText().toString()); //注释一long start, stop;long result;String out = "";// Dalvik - Recursivestart = System.currentTimeMillis(); //注释二result = FibLib.fibJ(input); //注释三stop = System.currentTimeMillis(); //注释四out += String.format("Dalvik recur sive: %d (%d msec)", result,stop - start);// Dalvik – Iterativestart = System.currentTimeMillis();result = FibLib.fibJI(input); //注释五stop = System.currentTimeMillis();out += String.format("\nDalvik iterative: %d (%d msec)", result,stop - start);// Native - Recursivestart = System.currentTimeMillis();result = FibLib.fibN(input); //注释六stop = System.currentTimeMillis();out += String.format("\nNative recursive: %d (%d msec)", result,stop - start);// Native - Iterativestart = System.currentTimeMillis();result = FibLib.fibNI(input); //注释七stop = System.currentTimeMillis();out += String.format("\nNative iterative: %d (%d msec)", result,stop - start);textResult.setText(out); //注释八}}

注释一:将用户输入的字符串转换为整形。
注释二:在开始计算之前,获取当前的时间戳(timestamp)。
注释三:我们在FibLib中调用相关的静态函数来显示实际的斐波那契计算。这里是Java递归实现。
注释四:获取另一个时间戳并减(subtract)前一个。结果就是计算的时间,单位是毫秒(milliseconds)。
注释五:Java的迭代实现。
注释六:本地递归算法。
注释七:最后,调用本地迭代算法。

注释八:格式化输出并打印在屏幕上。


linc注:

把ndk-build写到环境变量:

用文本编辑器打开/etc/profile
在profile文件末尾加入:

NDK=/home/linc/android/android-ndk-r5bexport NDKPATH=$NDK:$PATHexport PATH


原创粉丝点击