Android NDK 开发跟其它开发一样,首先需要配置好开发环境,本文以 Ubuntu系统为例介绍如何进行 Android NDK 开发环境的配置

1. 简介

什么是 Android NDK 呢? NDK(Native Development Kit) 是一个允许开发者用一些本地语言(C/C++)编写 Android App 的部分功能的工具集。对于一些特定的 App,NDK 非常有利于我们直接使用现成的用 C/C++ 编写的代码库(但对于大多数 App 来说,NDK 是没有必要的)。使用 NDK 进行 C/C++ Android 开发的基本结构和流程如下图(来自shihongzhi博客):

跟我学Android NDK开发(一)

2.开发环境配置

NDK 开发过程中涉及到将 C/C++ 程序编译为动态库(.so文件),由于Ubuntu系统中已经安装有 C/C++ 的编译工具 gcc/g++,以及 make 工具,所以在此不作介绍。另外还需要安装 JDK,并配置 Java 的环境变量,这个不是本文重点,在此也不作介绍了。

下面重点讲讲 Android 相关 SDK 的安装和配置,主要涉及到 Android SDK,ADT,NDK等。要进行 Android 开发,首先需要安装 Android SDK,要在 Eclipse 中进行开发的话,还需在 Eclipse 中安装 ADT(Android Develop Tools),在 Android 官网上提供了 SDK 和 包含 ADT 的 Eclipse 的集成开发包,可以一起下载:adt-bundle-linux-x86-20140321.zip。另外,还需要安装 NDK,下载地址:android-ndk-r9d-linux-x86.tar.bz2。下载完这两个压缩包后解压并移动到 /usr/local 目录下:

(1)先下载安装NDK。
下载链接:http://developer.android.com/tools/sdk/ndk/index.html,下载后解压缩就可以用了。

(2)打开Eclipse,点Window->Preferences->Android->NDK,设置NDK路径(如果没有NDK选项,可按照http://jingyan.baidu.com/article/4d58d5413000a09dd4e9c0fe.html进行设置),如下图所示:

跟我学Android NDK开发(一)
(3)新建一个Android工程(这里为AntiVirus),在工程上右键点击Android Tools->Add Native Support...,然后给我们的.so文件取个名字,例如本例中的:HelloJni。这时候工程就会多一个jni的文件夹,jni下有Android.mk和HelloJni.cpp文件。Android.mk是NDK工程的Makefile,HelloJni.cpp就是NDK的源文件。

(4)新建并配置一个Builder:
(a)Project->Properties->Builders->New,新建一个Builder。
(b)在弹出的【Choose configuration type】对话框,选择【Program】,点击【OK】:
(c)在弹出的【Edit Configuration】对话框中,配置选项卡【Main】。
在“Name“中输入新builders的名称(这里为Ndk_Builder)。
在“Location”中输入nkd-build(windows为nkd-build.cmd)的路径。
(我的是/usr/local/android-ndk-r10/ndk-build,根据各自的ndk路径设置,也可以点击“Browser File System…”来选取这个路径)。
在“Working Diretcoty”中输入${workspace_loc:/AntiVirus}(也可以点击“Browse Workspace”来选取AntiVirus目录)。
跟我学Android NDK开发(一)
(d)【Edit Configuration】对话框中,配置选项卡【Refresh】。
勾选“Refresh resources upon completion”,
勾选“The entire workspace”,
勾选“Recuresively include sub-folders”。
跟我学Android NDK开发(一)
(e)【Edit Configuration】对话框中,配置选项卡【Build options】。
勾选“After a “Clean””,
勾选“During manual builds”,
勾选“During auto builds”,
勾选“Specify working set of relevant resources”。
跟我学Android NDK开发(一)
点击“Specify Resources…”,勾选AntiVirus工程的“jni“目录,点击”finish“。点击“OK“,完成配置。到这里Eclipse就能够自动调用NDK编译jni目录下的C/C++代码了。
(5)在AntiVirus工程中新建一个TestJni.java类(为了调用C/C++代码),其内容如下:

 1 package com.wat.antivirus; 2   3 public class TestJni { 4     static {  5         System.loadLibrary("HelloJni"); // //加载相应的动态库,相对路径,src的根目录,库名必须是libxxx.so  6     }  7     8     public native String helloSay(); // 返回字符串,使用JNI的关键字native 9     public native int helloAdd(int a,int b); // 两个整数相加 10     public native int helloSub(int a,int b); // 两个整数相减 11     public native int helloMul(int a,int b); // 两个整数相乘 12     public native int helloDiv(int a,int b); // 两个整数相除 13 }

(6)在 Eclipse 中 build 一下生成对应 .class 文件,然后使用 javah 工具根据该 class 文件自动生成 jni API 的头文件。在 AntiVirus工程根目录下执行如下命令:

javah -classpath ./bin/classes -d jni com.wat.antivirus.TestJni

其中-classpath ./bin/classes表示类的路径,-d jni表示生成的头文件存放的目录,com.wat.antivirus.TestJni则是完整类名。

注意生成的头文件命名采用:包名+类名

JNI头文件一般由三部分组成:

  • 头文件
  • 本地方法的注释
  • 本地方法在头文件中的表示

执行命令后,在~/workspace/AntiVirus/jni目录下生成了C++头文件com_wat_antivirus_TestJni.h文件内容如下:

 1 /* DO NOT EDIT THIS FILE - it is machine generated */ 2 #include <jni.h> 3 /* Header for class com_wat_antivirus_TestJni */ 4  5 #ifndef _Included_com_wat_antivirus_TestJni 6 #define _Included_com_wat_antivirus_TestJni 7 #ifdef __cplusplus 8 extern "C" { 9 #endif10 /*11  * Class:     com_wat_antivirus_TestJni12  * Method:    helloSay13  * Signature: ()Ljava/lang/String;14  */15 JNIEXPORT jstring JNICALL Java_com_wat_antivirus_TestJni_helloSay16   (JNIEnv *, jobject);17 18 /*19  * Class:     com_wat_antivirus_TestJni20  * Method:    helloAdd21  * Signature: (II)I22  */23 JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloAdd24   (JNIEnv *, jobject, jint, jint);25 26 /*27  * Class:     com_wat_antivirus_TestJni28  * Method:    helloSub29  * Signature: (II)I30  */31 JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloSub32   (JNIEnv *, jobject, jint, jint);33 34 /*35  * Class:     com_wat_antivirus_TestJni36  * Method:    helloMul37  * Signature: (II)I38  */39 JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloMul40   (JNIEnv *, jobject, jint, jint);41 42 /*43  * Class:     com_wat_antivirus_TestJni44  * Method:    helloDiv45  * Signature: (II)I46  */47 JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloDiv48   (JNIEnv *, jobject, jint, jint);49 50 #ifdef __cplusplus51 }52 #endif53 #endif

我们拿其中一个方法作具体说明:

JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloAdd(JNIEnv *, jobject, jint, jint);

1、JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。

2、jint本地方法返回的数据类型,表示的是java下面的int类型

3、Java_com_wat_antivirus_TestJni_helloAdd 这个就是被JNICALL调用的部分,也就是Java中的native 方法名,这里起名字的方式比较特别,是:包名+类名+方法名。(Java_为JNI中标识此方法来源于java的标识头)

注意:类名千万别加下划线,Test_Jni

4、JNIEnv * 它是一个接口指针,用于定位函数表中的函数,jni.h头文件中存在着大量被封装好的函数,这些函数也是JNI编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象

5、jobject 两种情况

类 对象 实例 TestJnitj = new TestJni(); tj.helloAdd(10,20);

  • 上述中helloAdd方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看做Java类的一个实例化对象,也就是在本地方法实现上 jobject thiz 这个thiz参数就是指的tj
  • 如果helloAdd是一个静态方法,那么在Java中,它不是属于一个对象的,而是属于一个类的,Java中用TestJni.helloAdd()这样的方式来调用,这个时候jobject就可以看做是java类的本身,也就是说thiz就是TestJni.class

(7)编辑Android.mk文件,其内容如下:

 1 #返回当前mk的路径,宏函数’my-dir’, 由编译系统提供 2 LOCAL_PATH := $(call my-dir)  3 #CLEAR_VARS由编译系统提供,让GNU MAKEFILE为你清除LOCAL_XXX变量 4 include $(CLEAR_VARS)  5 #生成动态库名 6 LOCAL_MODULE := HelloJni  7 #添加源文件、头文件 8 LOCAL_SRC_FILES := HelloJni.c  9 #生成动态库10 include $(BUILD_SHARED_LIBRARY)

补充说明:

 1 #增加所需的库        2 LOCAL_LDLIBS := -llog   3 链接的库不产生依赖关系,一般用于不需要重新编译的库,如库不存在,则会报错找不到。默认的路径/android-ndk-r6b/platforms/android-3/arch-arm/usr/lib 链接的都是系统的库 4 LOCAL_SHARED_LIBRARIES := libexpat libcurl  5 会生成依赖关系,当库不存在时会去编译这个库。一般用于编译第三方提供的库 6 #生成动态库 7 include $(BUILD_SHARED_LIBRARY) 8 #生成静态库 9 #include $(BUILD_STATIC_LIBRARY) 10 #生成应用程序11 #include $(BUILD_EXECUTABLE)

(8)将com_wat_antivirus_TestJni.h拷贝到AntiVirus工程的jni目录下, 然后在HelloJni.c文件中完成头文件中函数的实现,其内容如下:

 1 #include <string.h> 2 #include <jni.h> 3  4 jstring 5 Java_com_wat_antivirus_TestJni_helloSay( JNIEnv* env, 6                                                   jobject thiz ) 7 { 8 #if defined(__arm__) 9   #if defined(__ARM_ARCH_7A__)10     #if defined(__ARM_NEON__)11       #if defined(__ARM_PCS_VFP)12         #define ABI "armeabi-v7a/NEON (hard-float)"13       #else14         #define ABI "armeabi-v7a/NEON"15       #endif16     #else17       #if defined(__ARM_PCS_VFP)18         #define ABI "armeabi-v7a (hard-float)"19       #else20         #define ABI "armeabi-v7a"21       #endif22     #endif23   #else24    #define ABI "armeabi"25   #endif26 #elif defined(__i386__)27    #define ABI "x86"28 #elif defined(__x86_64__)29    #define ABI "x86_64"30 #elif defined(__mips64)  /* mips64el-* toolchain defines __mips__ too */31    #define ABI "mips64"32 #elif defined(__mips__)33    #define ABI "mips"34 #elif defined(__aarch64__)35    #define ABI "arm64-v8a"36 #else37    #define ABI "unknown"38 #endif39 40     return (*env)->NewStringUTF(env, "Hello from JNI !  Compiled with ABI " ABI ".");41 }

编辑HelloJni.c并保存后,可以看到AntiVirus工程下的obj/local/armeabi目录下将自动生成libAntiVirus.so库。

(9)在AntiVirus.java中完成对TestJni.java中函数的调用:

 1 package com.wat.antivirus; 2  3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.view.View; 6 import android.widget.Button; 7 import android.widget.TextView; 8  9 10 public class MainActivity extends Activity {11 12     @Override13     protected void onCreate(Bundle savedInstanceState) {14         super.onCreate(savedInstanceState);15         setContentView(R.layout.activity_main);16         17         Button btn = (Button)findViewById(R.id.btn_cal);18         btn.setOnClickListener(new Button.OnClickListener() {19             @Override20             public void onClick(View arg0) {21                 // TODO Auto-generated method stub22                 TextView tv1 = (TextView) findViewById(R.id.textView1);23                 TextView tv2 = (TextView) findViewById(R.id.textView2);24                 TextView tv3 = (TextView) findViewById(R.id.textView3);25     26                 int a = Integer.parseInt(tv1.getText().toString());27                 int b = Integer.parseInt(tv2.getText().toString());28                 TestJni tj = new TestJni();29                 int c = tj.helloAdd(a,b);30                 String str = tj.helloSay();  31                 tv3.setText(str + Integer.toString(c));32             }33         });34     }35 }

(10)运行AntiVirus工程,在模拟器中可以看到界面输出来自HelloJni.c 文件中的“HelloWorld from JNI ! “。

OK,NDK实例到此完成。后续就可以深入的学习NDK/JNI了,比如C/C++与Java的数据类型转换,Android.mk文件的编写格式等。

更多相关文章

  1. C语言函数以及函数的使用
  2. 【转】Android xml资源文件中@、@android:type、@*、?、@+含义和
  3. 传智播客—Android(二)数据存储和访问 之文件
  4. Android夸进程通信机制五:使用文件共享进行进程间通信
  5. Android 存储路径浅析
  6. 初涉Android之文件保存
  7. Android Studio Gradle 添加.so 支持文件
  8. 在Android设备与Mac电脑之间传输文件

随机推荐

  1. Android中一个Activity调用另一个Activit
  2. Android Studio下ActionBar示例
  3. Android属性动画--基础使用
  4. Android之会员页面及进度条
  5. AIDL跨进程通信和Service调用
  6. Linux上添加udev规则使adb可以操作Androi
  7. 关于android avd目录的设置
  8. Android中的持续集成CI系统、代码review
  9. Android Studio使用小指南
  10. android studio第一个项目hello world之A