8步教你打开Android之门 NDK入门教程
这是一篇Android NDK开发的入门教程,在这一教程结束后,你将创建你自己的项目,从Java代码简单地调用原生C语言代码。
本文为一篇外文翻译,我们将介绍如何学习安装 Android NDK 并开始使用它。在这一教程结束后,你将创建你自己的项目,从 Java 代码简单地调用原生 C 语言代码。
教程细节
技术:Android SDK、NDK、C 语言
难度:进阶
预计完成时间:60-90 分钟
先决经验
在我们开始之前,我们需要先花点时间了解一下这一教程的难度。它的标记是“进阶”。之所以标为“进阶”是因为我们这些作者想要确保你符合以下要求:
你有Java和C语言经验。
你能适应命令行操作。
你知道如何了解你的 Cygwin、awk 和其他工具的版本。
你能适应 Android Development。
你有一个有效的 Android 开发环境(本文撰写时,笔者使用的是 Android 2.2)
你使用 Eclipse 或者可以将 Eclipse 的指导步骤轻松应用于你自己的 IDE 上。
就算你并不满足这些条件,我们当然也欢迎你阅读这一教程,不过你可能在某些步骤遇到困难,如果你满足了以上条件这些困难就会轻易解除。也就是说,即使你认为自己是个移动开发老手,使用 NDK 依然很容易碰到困难和麻烦。请注意你可能要自行排查故障才能让一切正常运转于你的开发系统中。
本教程提供完整的样例项目的开源代码下载。
何时使用 NDK 的说明
好,如果你正在阅读这篇教程,你也许已经在考虑在你的 Android 项目中使用 NDK 了。不过,我们想要花点时间讨论一下 NDK 为什么那么重要、何时该使用它,以及——同等重要的,何时不该使用它。
总的来说,只有当你的应用程序真的是个处理器杀手的时候你才需要使用 NDK。也就是说,你设计的算法要利用 DalvikVM 中所有的处理器资源,而且原生运行较为有利。还有,别忘了在 Android 2.2 中,JIT 编译器会提高类似代码的效率。
另一个使用 NDK 的原因是方便移植。如果你在现有的应用程序中有大量的 C 语言代码,那么使用 NDK 不仅可以加速你的项目的开发进程,也能在你的 Android 和非 Android 项目中保持修改的同步。这一点对于那些为其他平台而写的 OpenGL ES 应用程序来说尤为如此。
别以为只要用了原生代码就能提高你的应用程序的效率。Java 与原生 C 语言之间的转换会增加一些资源开销,因此只有你有一些集中消耗处理器资源的任务时才真正有必要这么做。
第 0 步:下载工具
好了,让我们开始吧。你需要下载 NDK。我们先开始下载,因为在下载的过程中你可以检查一下确保你所需要用到的其余工具的版本都正确。
从 Android 网站下载适合你的操作系统的 NDK。
现在,对照下列检查你的工具版本:
如果在 Windows 下,Cygwin 1.7 或更高版本
将 awk 升级到最新版本(我们使用的是 20070501)
GNU Make 3.81 或更高版本(我们使用的是 3.81)
如果其中任何一个的版本太旧,请在继续之前先升级。
第 1 步:安装 NDK
既然 NDK 已经下载完成(没错吧?),你就需要解压缩它。解压后将它放入合适的目录中。我们把它放在和 Android SDK 相同的目录下。记住你把它放在哪里了。
现在,你也许想要在路径设置中添加 NDK 工具。如果你在 Mac 或 Linux 下,你可以用你的原生路径设置来完成。如果你在 Windows 下的 Cygwin,你就需要设置 Cygwin 的路径设置。
第 2 步:创建项目
创建一个常规的 Android 项目。为了避免日后的问题,你的项目的路径必须不包含空格。我们的项目有个叫做“com.mamlambo.sample.ndk1”的包,带有一个叫做“AndroidNDK1SampleActivity”的默认 Activity——你之后还会看到它们。
在这个项目的顶层创建一个叫做“jni”的目录——这是你放置原生代码的地方。如果你很熟悉 JNI,那你就会知道 Android NDK 很大程度上基于 JNI 的概念——它本质上是个只有有限的 C 语言编译头文件的 JNI。
第 3 步:添加一些 C 语言代码
现在,在 jni 文件夹中,创建一个叫做 native.c 的文件。一开始将以下 C 语言代码写入该文件,我们以后再添加另一个函数:
- #include
- #include
- #include
- #define DEBUG_TAG "NDK_AndroidNDK1SampleActivity"
- void Java_com_mamlambo_sample_ndk1_AndroidNDK1SampleActivity_helloLog(JNIEnv * env, jobject this, jstring logThis)
- 10.
11. {
- 12.
- 13. jboolean isCopy;
- 14.
- 15. const char * szLogThis = (*env)->GetStringUTFChars(env, logThis, &isCopy);
- 16.
- 17. __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", szLogThis);
- 18.
- 19. (*env)->ReleaseStringUTFChars(env, logThis, szLogThis);
- 20.
21. }
- 22.
这个函数实际上非常浅显。它获取一个 Java 对象的字符串参数,将它转换为 C-string,然后将它写入到 LogCat 中。
不过该函数的名字很重要。它遵循了以“Java”的特定字样开头,后面跟着包名称,然后类名称,然后方法名称,和 Java 中定义的一样。每一部分都由一根下划线隔开,而不是点。
该函数的头两个参数也很重要。第一个参数是 JNI 环境,它与 helper 函数会被频繁调用。第二个参数是该函数所属的 Java 对象。
第 4 步:从 Java 中调用原生代码
既然你已经写好了原生代码,让我们回头看看 Java 这边。在默认的 Activity 中,按照你的喜好创建一个按钮,并添加一个按钮处理器。从按钮处理器中,对 helloLog 作调用:
23. helloLog("This will log to LogCat via the native call.");
然后你必须在 Java 这边添加函数声明。在你的 Activity 类中添加如下声明:
24. private native void helloLog(String logThis);
它告诉编译和链接系统该方法将在原生代码中实现。
最后,你需要加载原生代码最终编译到的库。在 Activity 类中添加如下的静态初始化程序来根据名称加载库(库的名字随你决定,在下一步还会用到):
- 25.
- 26.
27. static {
- 28.
- 29. System.loadLibrary("ndk1");
- 30.
31. }
- 32.
第 5 步:添加原生代码的 Make 文件
在 jni 文件夹中,现在你需要添加在编译中要用到的 makefile。该文件必须以“Android.mk”命名,如果你之前命名的文件为 native.c,库为 ndk1,那么 Android.mk 的内容就应该是这样:
- 33.
- 34.
35. LOCAL_PATH := $(call my-dir)
- 36.
- 37.
- 38.
39. include $(CLEAR_VARS)
- 40.
- 41.
- 42.
43. LOCAL_LDLIBS := -llog
- 44.
- 45.
- 46.
47. LOCAL_MODULE := ndk1
- 48.
49. LOCAL_SRC_FILES := native.c
- 50.
- 51.
- 52.
53. include $(BUILD_SHARED_LIBRARY)
- 54.
第 6 步:编译原生代码
既然你的原生代码已完成,make 文件也已就绪,是时候编译原生代码了。在命令行下(Windows 用户在 Cygwin 下),你需要在你的项目的根目录下运行 ndk-build 命令。ndk-build 工具就在 NDK 工具目录中。将它添加到我们的路径中是最方便的办法。
在之后的编译中,如果你使用“ndk-build clean”命令,那么你可以确保所有的东西都被重新编译了。
第 7 步:运行代码
现在你已准备妥当可以运行代码了。在你最喜欢的模拟器或者手持设备中载入该项目,查看 LogCat,然后点击按钮。
可能有两件事情会发生。首先,它可能正常工作了。这样的话,恭喜你!不过你可能还是想要继续阅读下去。你也可能在 LogCat 中得到类似“Could not execute method of activity”这样的错误。这很正常。这只是说明你漏掉了某个步骤罢了。用 Eclipse 很容易发生这种情况。通常,Eclipse 被设置为自动重编译。如果它不知道有东西被修改了,它就不会自动重编译和重链接。在本例中,Eclipse 不知道你编译了原生代码。所以,“清除(cleaning)”该项目(在 Eclipse 工具栏中点击项目(Project)->清除(Clean)),强制 Eclipse 重编译。
第 8 步:添加另一个原生函数
接下来的函数将不仅演示返回值的能力,还会演示返回例如字符串这样的对象的能力。在 native.c 中添加如下函数:
- jstring Java_com_mamlambo_sample_ndk1_AndroidNDK1SampleActivity_getString(JNIEnv * env, jobject this, jint value1, jint value2)
- {
- char *szFormat = "The sum of the two numbers is: %i";
- char *szResult;
- // add the two values
- jlong sum = value1+value2;
- // malloc room for the resulting string
- szResult = malloc(sizeof(szFormat) + 20);
- // standard sprintf
- 10. sprintf(szResult, szFormat, sum);
- 11. // get an object string
- 12. jstring result = (*env)->NewStringUTF(env, szResult);
- 13. // cleanup
- 14. free(szResult);
- 15. return result;
16. }
- 17.
- 18.
为了正常编译,你会需要添加一个 include stdio.h 的声明。而且,为了响应这个新的原生函数,请在你的 Activity Java 类中添加如下声明:
- 19.
20. private native String getString(int value1, int value2);
你现在可以随意设定其功能。我们使用如下两个调用和输出:
21. String result = getString(5,2);
22. Log.v(DEBUG_TAG, "Result: "+result);
23. result = getString(105, 1232);
24. Log.v(DEBUG_TAG, "Result2: "+result);
回到 C 语言函数中,你会注意到我们做了许多事情。首先,我们在使用 malloc() 函数中的 sprintf() 调用时需要创建一个缓冲器(buffer)。如果你不会忘记通过使用 free() 函数清理结果,那么这就很合理了。然后,为了传回结果,你可以使用一个叫作 NewStringUTF() 的 JNI helper 函数。该函数基本上就是获取一个 C 语言字符串,以之创建一个新的 Java 对象。这个新的字符串对象就可以在之后作为结果返回,你就可以在 Java 类中将它作为一个常规 Java 字符串对象使用了。
指令集、兼容性,等等
Android NDK 需要 Android SDK 1.5 或更高版本。在新版本的 NDK 中,有些新的头文件可用于扩大对某些 API 的访问——特别是 OpenGL ES 库。
不过,那些都不是我们要谈论的兼容性。这是原生代码,在使用时由处理器构架编译。因此,你要问自己的一个问题会是它支持何种处理器构架?在目前的 NDK 中(在本文撰写时)它只支持 ARMv5TE 和 ARMv7-A 指令集。默认设置下,目标架构被设置为 ARMv5TE,它可以在使用 ARM 芯片的 Android 设备上运行。
它预计未来将支持其他指令集(其中提到了 x86)。这其中有很有意思的潜在状况:NDK 解决方案无法适用于所有的设备。例如,市面上有使用 x86 指令集的英特尔(Intel)Atom 处理器的 Android 平板设备。
那么 NDK 在模拟器上如何呢?模拟器运行的是真正的虚拟机,包括完整的处理器虚拟。没错,这意味着在虚拟机中运行 Java 就等于是在虚拟机中运行了一个虚拟机。
转载于:https://www.cnblogs.com/yun111/archive/2012/11/28/2792079.html
8步教你打开Android之门 NDK入门教程相关推荐
- Mac下Android studio 之NDK配置教程(一)
Mac下Android studio 之NDK配置教程(一) 1.概述 近期项目全线转移到Mac下使用使用Android studio开发. 遇到关键代码封装到 ***native***层,此时在wi ...
- Mac下Android studio 之NDK配置教程(二)
Mac下Android studio 之NDK配置教程(二) (一)简述 从上一篇NDK配置教程(一) 中,我 简单的阐述了MAC下NDK的基本解压和环境配置步骤. 本节我讲详细描述android s ...
- Android百度地图开发入门教程
Android百度地图开发入门教程 1.平台注册登录 2.创建应用 3.Android studio配置 4.代码编写 5.最终效果(建议真机) 1.平台注册登录 登录百度地图开放平台网站注册并登录 ...
- flex+android+教程,android开发flex4.5入门教程.pdf
android开发flex4.5入门教程 中国矿业大学教务部 教务通知(2013 )第33 号 关于做好各级"大学生创新训练计划" 项目中期检查和结题验收的通知 各学院: 为加强我 ...
- 华南x79主板u盘装系统教程_学不会不收费 几步教你安装Android x86
1安装Android x86其实并不困难 话说最近操作系统这个话题的确是非常火爆.也许是借助于Windows 8消费者预览版的光芒,凡是与系统搭边的东西大家好像都喜欢与Windows 8进行比较.不管 ...
- Android平台Qt开发入门教程
很多人会问,Android平台可以不使用Java开发应用程序?? 我做Android平台native开发之前,也有这么想过,但是我又想,底层系统全是c/c++代码,用c/c++开发Android平台程 ...
- 米狗族 Android平台Qt开发入门教程
很多人会问,Android平台可以不使用Java开发应用程序?? 我做Android平台native开发之前,也有这么想过,但是我又想,底层系统全是c/c++代码,用c/c++开发Android平台程 ...
- Android NDK入门教程之快速定位Crash问题
文章目录 首先制造一个 so crash问题 addr2line addr2line工具位置 addr2line命令如下: 分析crash log so strip 欢迎联系.指正.批评 首先制造一个 ...
- creo如何更改打开时显示方式_Creo4.0入门教程(3):设置工作目录和打开以及保存文件...
原标题:Creo4.0入门教程(3):设置工作目录和打开以及保存文件 目标: 1.设置工作目录 2.文件的打开和保存 1.设置工作目录 工作目录是打开和保存文件的指定位置.通常默认工作目录为启动 Cr ...
最新文章
- android从放弃到精通第11天 勿忘初心
- Linux命令删除某目录下的所有.svn文件
- 第11章 linux文档的压缩与打包
- 【OpenCV3】cv::Mat中的数据按行列写入txt文件中
- android最大json,Android:解析大型JSON文件
- 18行代码AC——PTA 二叉树的遍历 (10分)——解题报告
- 物体运动到一个点停止_运用SolidWorks运动仿真来做的最速降线及其验证,来看看我的办法...
- 《C#与.net高级编程》——第一支柱:C#的封装
- python如何强制结束主线程_强制结束线程
- js传中文参数 java取_js中文转码传输java后台 适用于用url传递中文参数
- Redis中使用Lua语言
- PostgreSQL的使用-02-在Linux下的基本配置
- TensorFlow神经网络:模块化的神经网络八股
- [UIKit学习]03.关于UILable
- Vue 全站缓存之 vue-router-then :前后页数据传递
- 软件开发人员的简历项目经验
- 考研报考人数的灰色模型
- python利用matplotlib库绘制三维图学习
- 【Win10系统右下角网络图标消失、无法联网、网络Internet状态闪退等问题解决方法】
- (Java实习生)每日10道面试题打卡——Java基础知识篇2
热门文章
- pythonos模块_Python3 入门教程——os模块使用(文件/目录操作)
- metasploitable3渗透测试
- 找到最大回文子串_使用O(1)空间复杂度找到最大的回文子串
- android studio资源二进制,无法自动检测ADB二进制文件 – Android Studio
- 605. 种花问题 golang 切片越界问题和逻辑操作符的思考
- 软件工程学习笔记《目录》
- C语言中 \r, \n, \b
- mysql 存储过程 循环结构 命名_mysql存储过程----循环结构
- linux运算_CentOS「linux」学习笔记22:算术运算符、逻辑运算符、关系运算符
- 二叉树题目----2 检查两颗树是否相同 和 对称二叉树的判定