Learning Android 第十五章 NDK 翻译二

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 __cplusplus
extern "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;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Find UI views
editInput = (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 - Recursive
start = System.currentTimeMillis(); //注释二
result = FibLib.fibJ(input); //注释三
stop = System.currentTimeMillis(); //注释四
out += String.format("Dalvik recur sive: %d (%d msec)", result,
stop - start);
// Dalvik – Iterative
start = System.currentTimeMillis();
result = FibLib.fibJI(input); //注释五
stop = System.currentTimeMillis();
out += String.format("\nDalvik iterative: %d (%d msec)", result,
stop - start);
// Native - Recursive
start = System.currentTimeMillis();
result = FibLib.fibN(input); //注释六
stop = System.currentTimeMillis();
out += String.format("\nNative recursive: %d (%d msec)", result,
stop - start);
// Native - Iterative
start = 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-r5b

export NDK

PATH=$NDK:$PATH

export PATH


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值