IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [译]Learning Android 第十五章 NDK 翻译二

    lincyang发表于 2011-07-20 17:58:59
    love 0
    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
    




沪ICP备19023445号-2号
友情链接