android ndk

Android系统中的应用程序都是用Java开发的。Android NDK使我们能够在android上使用C/C++开发的原生代码。有两个理由使用NDK: 一是合理的重用现有的代码;二是在程序中某些关键的部分提高执行效率。
这里先讲几个符号的约定:
<project> - 你的Android应用程序工程的目录
<ndk> - 你的ndk安装的目录
捷径这里先扯一句题外话 -- 如果你不需要使用NDK开发,只是需要使用第三方用NDK开发的库,那么你只需要这样做:
把第三方提供的libxxx.so放到你的<project>/libs/armeabi/下, 然后在程序随便什么地方中加入
static {
    System.loadLibray("xxx");
}
就可以使用该库了。
安装NDKNDK的安装很简单:
1. 首先要将SDK升级至最新,然后下载ndk(可能要翻墙,恩恩)将它解压到某个目录<ndk>下。
2. 运行: <ndk>/build/host-setup.sh(注:r4版本或更高版本,无须执行这步,详情请看ndk/doc/install.html)

3. 如果成功的话就OK了,如果失败的话检查一下你是不是下载了正确的ndk版本(例如你的操作系统是linux而下载了windows版的ndk).


Java部分现在我们用一个很简单的例子来说明NDK的使用。我们在eclipse中新建一个android工程,其中:
Project Name:jnitest
Build Target: Android 2.2
Application Name: JNI Test
Package Name: org.eshock.jnitest
Create Activity: JNITest

JNITest.java:
package org.eshock.jnitest;

import android.app.Activity;
import android.os.Bundle;

public class JNITest extends Activity {
    public native int plus (int x, int y);
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        int x = plus(1, 2);
        android.util.Log.d("jni", String.valueOf(x));
    }
    
    static {
        System.loadLibrary("mylib");
    }
}


我们只是演示NDK,所以就不要界面了。在这个程序中,我们调用一个c语言编写的plus(int, int)函数来计算1+2的值,然后在log中打印它。这个库的名称叫做mylib。
要使用一个c语言的函数,需要在java中声明:
public native int plus(int x, int y);
这样java编译器就知道这个函数是外部库中实现的。

这里需要提到的一点就是在JNI里面,有两个很重要的函数

jint JNI_OnLoad(JavaVM* vm, void* reserved)
 {
 void *venv;
 LOGI("JNI_OnLoad!");
 
if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) {
 LOGE("ERROR: GetEnv failed");
 return -1;
 }
 
return JNI_VERSION_1_4;
 }
 
jint JNI_OnUnload(JavaVM* vm,void* reserved)
 {
 void *venv;
 LOGI("JNI_OnUnload!");
 if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) {
 LOGE("ERROR: GetEnv failed");
 return -1;
 }
 return JNI_VERSION_1_4;
 }

上面的函数是指如果我们需要在加载SO库或者释放SO库的时候,需要额外处理,可以加在这里。

/

如果用eclipse编译相应的project后,进入bin路径;

把ndk安装路径export 到你的PATH环境变量中。例如你的NDK安装路径:/home/android-ndk-sdk/android-ndk-r8b,下一步设置NDK=/home/android-ndk-sdk/android-ndk-r8b,export NDK; 然后export PATH=$NDK:$PATH

然后执行:javah org.eshock.jnitest.JNITest

或者在当前project路径输入下面命令:

1:mkdir jni

2:javah -classpath bin -d jni  org.eshock.jnitest.JNITest


-classpath bin:表示类的路劲

-d jni: 表示生成的头文件存放的目录

org.eshock.jnitest.JNITest 则是完整类名

这一步的成功要建立在已经在 bin/org/eshock/jnitest/  目录下生成了 HelloJni.class的基础之上。

如果有些eclipse在bin下面多了几层文件夹,则需要进行相应的org/eshock/jnitest/ 路径。例如如果用eclipse编译后,

bin下面的路径是classes/org/eshock/jnitest,则上面的(2)命令要修改为:javah -classpath bin/classes -d jni  org.eshock.jnitest.JNITest



在Android中,我们如果编译了一个APK,可以使用javah 自动生成相应的JNI的C实现头文件。但是,跟单独使用javac编译文件不同的时候,我们需要额外指定android.jar库的路径。否则会出现奇怪的错误。下面是两个典型的错误。

当我们编译完毕后,使用 javahorg.eshock.jnitest.JNITest,如果会出现如下错误:

Error: Could not find class file for ‘org.eshock.jnitest.JNITest’.
解决办法:

javah -classpath /root/workspace/HelloWorld/bin/classesorg.eshock.jnitest.JNITest
请注意,由于我们Android编译后,会将.class文件放到classes目录下面,如果我们在bin目录下,执行,还会报上面的错误的。这里非常关键。

当我们使用上面的方法编译后,出现如下错误:

Error: cannot access android.app.Activity
class file for android.app.Activity not found

解决方法,附加android.jar库的地址:
javah -classpath /opt/android-sdk-linux/platforms/android-8/android.jar:/root/workspace/HelloWorld/bin/classesorg.eshock.jnitest.JNITest

至此,我们就会生成文件org_eshock_jnitest_JNITest.h

或者如果当那个JAVA是通用版本,首先随便在某个目录下创建一个文件JNITest.java:
public class JNITest {
    public native int plus (int x, int y);
}

先用javac JNITest.java生产.class文件,然后用javah 生成.h文件
然后在命令行下执行:
javac JNITest.java && javah JNITest 
将生成一个文件JNITest.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNITest */

#ifndef _Included_JNITest
#define _Included_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNITest
 * Method:    plus
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_JNITest_plus
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif
这里自动生成了java中native函数对应的签名,你可以在c代码中使用它。(记得修改包名,把JNICALL Java_JNITest_plus改成Java_org_eshock_jnitest_JNITest_plus

//

接下来,我们就可以根据定义C部分。在project目录下建立一个jni目录,进入jni目录,建立下面的.c文件,同时把上面javah生成的.h文件拷贝

到jni路径
接下来我们使用C语言实现这个plus函数。
创建<project>/jni/mylib.c:
#include <string.h>
#include <jni.h>

JNIEXPORT jint JNICALL
Java_org_eshock_jnitest_JNITest_plus( JNIEnv* env,
                                                  jobject thiz,
                                                  jint x,
                                                  jint y )
{
    return x + y;
}

这里还有一个问题需要注意的就是:

我们使用NDK-Build生成的文件是libhelloworld.so,我们在使用System.loadLibrary的时候,参数只需要到helloworld,而不需要把lib和.so加上

编写Android.mk文件

//一般都是这行
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
//代码文件
LOCAL_SRC_FILES:=mylib.c
//包含的头文件
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
//生成的so库名称
LOCAL_MODULE := mylib
LOCAL_SHARED_LIBRARIES := libutils
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)

//

include $(BUILD_SHARED_LIBRARY)
其中LOCAL_PATH表示c源代码文件的位置;LOCAL_MODULE表示生成的共享库的名称;LOCAL_SRC_FILES代表c代码的文件。不需要把头文件列在里面;头文件的依赖关系是ndk自动计算的。

//

最后执行ndk-build。如果是虚拟机则要ndk-build APP_ABI=x86


这样就在project目录下面自动生成了libs和obj目录


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值