Learning Android 第十五章 NDK 翻译二

来源:互联网 发布:mac git图形化工具 编辑:程序博客网 时间:2024/05/25 05:37

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/


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

[cpp] view plaincopy
  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_marakana_FibLib */  
  4. #ifndef _Included_com_marakana_FibLib  
  5. #define _Included_com_marakana_FibLib  
  6. #ifdef __cplusplus  
  7. extern "C" {  
  8. #endif  
  9. /* 
  10. * Class: com_marakana_FibLib 
  11. * Method: fibN 
  12. * Signature: (I)J 
  13. */  
  14. JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN  
  15. (JNIEnv *, jclass, jint);  
  16. /* 
  17. * Class: com_marakana_FibLib 
  18. * Method: fibNI 
  19. * Signature: (I)J 
  20. */  
  21. JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI  
  22. (JNIEnv *, jclass, jint);  
  23. #ifdef __cplusplus  
  24. }  
  25. #endif  
  26. #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

[cpp] view plaincopy
  1. #include "com_marakana_FibLib.h" /*(注释一)*/  
  2. /* 递归斐波那契算法(注释二)*/  
  3. long fibN(long n) {  
  4. if(n<=0) return 0;  
  5. if(n==1) return 1;  
  6. return fibN(n-1) + fibN(n-2);  
  7. }  
  8. /*迭代斐波那契算法(注释三)*/  
  9. long fibNI(long n) {  
  10. long previous = -1;  
  11. long result = 1;  
  12. long i=0;  
  13. int sum=0;  
  14. for (i = 0; i <= n; i++) {  
  15. sum = result + previous;  
  16. previous = result;  
  17. result = sum;  
  18. }  
  19. return result;  
  20. }  
  21. /*头文件中生产的JNI方法的签名(注释四)*/  
  22. JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN  
  23. (JNIEnv *env, jclass obj, jlong n) {  
  24. return fibN(n);  
  25. }  
  26. /*头文件中生产的JNI方法的签名(注释五) */  
  27. JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI  
  28. (JNIEnv *env, jclass obj, jlong n) {  
  29. return fibNI(n);  
  30. }  

注释一:我们引入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

[java] view plaincopy
  1. package com.marakana;  
  2. import android.app.Activity;  
  3. import android.os.Bundle;  
  4. import android.view.View;  
  5. import android.view.View.OnClickListener;  
  6. import android.widget.Button;  
  7. import android.widget.EditText;  
  8. import android.widget.TextView;  
  9. public class Fibonacci extends Activity implements OnClickListener {  
  10. TextView textResult;  
  11. Button buttonGo;  
  12. EditText editInput;  
  13. @Override  
  14. public void onCreate(Bundle savedInstanceState) {  
  15. super.onCreate(savedInstanceState);  
  16. setContentView(R.layout.main);  
  17. // Find UI views  
  18. editInput = (EditText) findViewById(R.id.editInput);  
  19. textResult = (TextView) findViewById(R.id.textResult);  
  20. buttonGo = (Button) findViewById(R.id.buttonGo);  
  21. buttonGo.setOnClickListener(this);  
  22. }  
  23. public void onClick(View view) {  
  24. int input = Integer.parseInt(editInput.getText().toString()); //注释一  
  25. long start, stop;  
  26. long result;  
  27. String out = "";  
  28. // Dalvik - Recursive  
  29. start = System.currentTimeMillis(); //注释二  
  30. result = FibLib.fibJ(input); //注释三  
  31. stop = System.currentTimeMillis(); //注释四  
  32. out += String.format("Dalvik recur sive: %d (%d msec)", result,  
  33. stop - start);  
  34. // Dalvik – Iterative  
  35. start = System.currentTimeMillis();  
  36. result = FibLib.fibJI(input); //注释五  
  37. stop = System.currentTimeMillis();  
  38. out += String.format("\nDalvik iterative: %d (%d msec)", result,  
  39. stop - start);  
  40. // Native - Recursive  
  41. start = System.currentTimeMillis();  
  42. result = FibLib.fibN(input); //注释六  
  43. stop = System.currentTimeMillis();  
  44. out += String.format("\nNative recursive: %d (%d msec)", result,  
  45. stop - start);  
  46. // Native - Iterative  
  47. start = System.currentTimeMillis();  
  48. result = FibLib.fibNI(input); //注释七  
  49. stop = System.currentTimeMillis();  
  50. out += String.format("\nNative iterative: %d (%d msec)", result,  
  51. stop - start);  
  52. textResult.setText(out); //注释八  
  53. }  
  54. }  

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

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


linc注:

把ndk-build写到环境变量:

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

[java] view plaincopy
  1. NDK=/home/linc/android/android-ndk-r5b  
  2.   
  3. export NDK  
  4.   
  5. PATH=$NDK:$PATH  
  6.   
  7. export PATH  

原创粉丝点击