初识Android NDK

本文介绍Windows环境下搭建Android NDK开发环境,并创建一个简单的使用Native代码的Android Application。

一、环境搭建

二、JNI函数绑定

三、例子



一、环境搭建

1. 操作系统:Windows7 64位

2. 安装Java,最新的JDK8貌似还不支持,敢于折腾的同学可以试试,下载JDK7安装即可,别忘了添加JDK的bin目录到PATH环境变量。http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html。"jdk-7u71-windows-x64.exe"

3. 下载Android ADT Bundle,https://developer.android.com/sdk/index.html。"adt-bundle-windows-x86_64-20140702.zip"

4. [可选]使用SDK Manager更新到最新,本文编辑之时API Level已经更新到21了。

5. 下载NDK开发包,解压即可,路径要固定不要轻易改动,因为Eclipse的NDK插件会配置这个路径。https://developer.android.com/tools/sdk/ndk/index.html。"android-ndk-r10b"

6. 安装NDK插件,在Eclipse中,打开菜单Help>Install New Software,Work with里用下拉菜单选择Android Developer Tools Update Site,下面就会出现Developer Tools的分组,展开选择Android Native Development Tool,点击Finish安装。不用选择Developer Tools里面其他的插件,它们已经安装好了。

从NDK r7开始就不需要使用cygwin了,因此以上就是所有的环境搭建步骤。

二、JNI函数绑定

先在Eclipse中创建一个Android Project。然后添加Native支持,在Project Explorer中右键选择新建的工程,在菜单中选择Android Tools>Add Native Support,会自动创建好JNI目录和Android.mk,以及配置好C\C++ Include路径。

JNI中Java方法调用Native方法的绑定有两种方法:一种是自动绑定,只需要C函数名和Java方法名称按照一定的规则能匹配上就能自动绑定。可以通过javah工具将绑定的规则都处理好,生成头文件方便使用,也可以根据规则自己定义函数,不使用生成头文件的方法。具体的规则是:

1. C函数要加上前缀"Java_"。

2. Java中package路径中的圆点要转换成下划线,类名和方法名都保持原样,用下划线隔开。

3. C方法中比Java方法多了两个参数放在最前面,JNIEnv*和jobject。前者是起到C代码和Java代码交互作用的一个结构体指针,后者是调用这个函数的Java对象在C代码中的代表。

4. Java调用Native方法还涉及到参数和返回值的传递,Java中的类型会转换为C中的对应类型,可以精确对应的类型有基本数据类型(如jint, jdouble)、数组(如jintArray)、String(jstring),其他类型一律当做Object(jobject)。

例如Java中定义的一个Activity的类:

package com.example.myhellojni;
public class MyHelloJniActivity extends Activity {
    private native String getNativeString();
}

在C文件中的对应函数:

jstring Java_com_example_myhellojni_MyHelloJniActivity_getNativeString(JNIEnv* env, jobject thiz) {
    return ...;
}

还有一种动态注册的方法,使得C代码定义的函数不必遵守命名规则也可以与Java中的方法绑定起来。这种绑定是在C代码中实现的,需要在加载这个动态链接库的时候的回调函数JNI_OnLoad中注册。注册方法见代码:

#include <jni.h>
#include <stdlib.h>
// 要绑定到Java的native方法
jstring ajey_getNativeString(JNIEnv* env, jobject thiz) {
    return (*env)->NewStringUTF(env, "what? this is a native string!!!!");
}

// 计算数组大小的小技巧,只能用于数组,不能用于指针(因为指针的sizeof得不到指针指向内存的大小)
#define SIZE_OF_ARRAY(array)  (sizeof(array)/sizeof(array[0]))

static JNINativeMethod gMethods[] = { // 这个结构体数组是传给register函数的参数,定义了java方法和C函数的对应关系    // 成员依次是java方法名称、参数和返回值签名字符串、对应的C函数名
    { "getNativeString", "()Ljava/lang/String;", (void*)ajey_getNativeString },
};

static int registerNatives(JNIEnv* env) {    // 先找到java的class。注意gMethods中并没有定义是哪个类的方法,需要在注册的时候指明。
    jclass clazz = (*env)->FindClass(env, "com/ajeyone/myhellojni/MyHelloJniActivity");
    if (clazz == NULL) {
        return JNI_FALSE;
    }    // 这一步是真正注册的地方
    if ((*env)->RegisterNatives(env, clazz, gMethods, SIZE_OF_ARRAY(gMethods)) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;    // 第一步获取JNIEnv
    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    // 第二步注册
    if (!registerNatives(env)) {
        return -1;
    }
    // 成功返回表示JNI version的常量,失败返回-1
    return JNI_VERSION_1_4;
}

以上代码有一个奇怪的地方:env指针(vm指针也是这样)使用这种方式来调用函数:(*env)->function(env, ...),原因是JNIEnv实际上是typedef定义的指针类型,那么JNIEnv* env就是二级指针,因此需要(*env)->的方式来使用;而且有点像C++中对象成员函数的调用,但这些代码不是用C++编译的而是用C编译的,实际上是结构体中定义了函数指针,所以需要将"this"指针显式地传递过去,也就是env本身。jni.h中也为C++定义了基于对象的访问JNIEnv的方式,实际上是对C函数的包装,使其在C++中更容易使用,如果你的代码是写在cpp文件中的,那么使用的就是C++对象的方式,这时JNIEnv不再是一个typedef的指针,而是一个类类型,直接使用env->function(...)就行了,而且也不需要将env当做第一个参数传递给function。

三、例子

使用Android Tools>Add Native Development Support的时候,将Library名称定义成MyHelloJni。需要注意的是不要忘记写加载动态库的代码,否则肯定是找不到Native方法的。在Eclipse中C\C++代码不会在保存时自动编译,需要自己在菜单中选择Build或者在运行App时触发编译。

前面Java代码中Activity的全部内容:

package com.ajeyone.myhellojni;

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

public class MyHelloJniActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		TextView text = new TextView(this);		text.setText(getNativeString());
		setContentView(text); // 保持简单,未使用layout文件
	}

	private native String getNativeString();

	static { // 放在这里是一般的做法,如果有需求可以在其他地方加载
		System.loadLibrary("MyHelloJni"); // 千万不要忘了加载动态库
	}
}
时间: 2024-08-25 13:02:54

初识Android NDK的相关文章

Android NDK开发初识

神秘的Android NDK开发往往众多程序员感到兴奋,但又不知它为何物,由于近期开发应用时,为了是开发的.apk文件不被他人解读(反编译),查阅了很多资料,其中有提到使用NDK开发,怀着好奇的心理,通过在线视频教育网站,我初步了解了NDK的神秘面纱,好东西自然要分享,接下来我们就一起来认识一下Android NDK开发. 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三

哪位大兄弟有用 cMake 开发Android ndk的

一直用 Android studio 开发ndk,但是gradle支持的不是很好,只有experimental 版本支持 配置各种蛋疼.主要每次新建一个module都要修改配置半天.之前也看到过google 开发文档有提到 cmake 但是一直没用.哪位大兄弟用过,说下经验 哪位大兄弟有用 cMake 开发Android ndk的 >> android 这个答案描述的挺清楚的:http://www.goodpm.net/postreply/android/1010000007205830/哪位

Android NDK开发的一些技巧

Android NDK(Native Development Kit)是基于Java JNI的使用C/C++和Java来混合开发应用的一种方式,甚至在Android 2.3(API 9)以后可以用Native(Native这个字在不同的语境中意义是不一样的,这里指的是JNI中的C/C++的代码:如果放在Hybrid中Native就是指平台原生的语言和API)代码来创建标准界面组件Activity,换句话说就可以用纯C/C++来写一个Android应用,但是貌似还没有人这么干过,这只是一种理论上的

Android NDK 编译加入so文件

在cocos2d-x中集成百度语音识别的时候,运行build_native.py会把libs/armeabi目录清空. 以下是解决办法,把so文件放在jni/prebuilt里面. 修改Android.mk文件,主要有两行include $(CLEAR_VARS) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) #百度语音识别so LOCAL_MODULE := BDVoiceRecognitionClient_V1 LOCAL_SRC_FI

基于 Android NDK 的学习之旅----- C调用Java

http://www.cnblogs.com/luxiaofeng54/archive/2011/08/17/2142000.html 基于 Android NDK 的学习之旅----- C调用Java许多成熟的C引擎要移植到Android 平台上使用 , 一般都会 提供 一些接口, 让Android sdk 和 jdk 实现. 下文将会介绍 C 如何 通过 JNI 层调用 Java 的静态和非静态方法. 1.主要流程 1.  新建一个测试类TestProvider.java a)       

Android NDK学习笔记(一) 为什么要用NDK?

NDK是什么 NDK是Native Development Kit的简称,即本地开发工具包.通过NDK,Android允许开发人员使用本地代码语言(例如C/C++)来完成应用的部分(甚至全部)功能.注意:由于翻译原因,有些地方也把Native翻译为"原生". NDK是SDK的一个补充,可以帮助你做这些事情: 生成可以在ARM CPU,Android 1.5(及以上)平台运行的JNI兼容的共享库. 将生成的共享库放置在应用程序项目路径的合适位置,使其能自动地添加进你最终的(和经过签名的)

Android NDK:Aborting..Stop的处理方法

在eclipse中配置cocos2d-x的android环境时,遇到这样的错误提示 网上搜索了一下,说是在NDK_MODULE_PATH环境变量下未找到所需要的Android.mk文件,后来仔细研究了一下项目属性,找到环境设置这一项 添加完NDK_MODULE_PATH路径之后,会在项目的.settings目录下生成一个名为org.eclipse.cdt.core.prefs的文件 之后将project clean && build就好了     注意事项: 添加NDK_MODULE_PA

【转】基于 Android NDK 的学习之旅-----数据传输(引用数据类型)

原文网址:http://www.cnblogs.com/luxiaofeng54/archive/2011/08/20/2147086.html 基于 Android NDK 的学习之旅-----数据传输二(引用数据类型)(附源码) 基于 Android NDK 的学习之旅-----数据传输(引用数据类型) 接着上篇文章继续讲.主要关于引用类型的数据传输,本文将介绍字符串传输和自定义对象的传输. 1.主要流程 1.  String 字符串传输 a)         上层定义一个native的方法

深入理解Android NDK日志符号化

为了进行代码及产品保护,几乎所有的非开源App都会进行代码混淆,这样当收集到崩溃信息后,就需 要进行符号化来还原代码信息,以便开发者可以定位Bug.基于使用SDK和NDK的不同,Android的崩溃分为两类:Java崩溃和C/C++崩溃.Java崩溃通过mapping.txt文件进行符号化,比较简单直观,而C/C++崩溃的符号化则需要使用Google自带的一些NDK工具,比如ndk-stack.addr2line.objdump等.本文不去讨论如何使用这些工具,有兴趣的朋友可以参考同事写的另一篇