开发环境信息
列举下本篇文章编写的Demo基本信息
操作系统 | Windows 10 家庭中文版 |
---|---|
开发工具 | Android Studio 2.1 |
SDK | new |
NDK | new |
扫盲之SDK、JDK、NDK的区别
SDK | 软件开发工具包;英语全称:Software Development Kit |
---|---|
JDK | Java语言的软件开发工具包;英语全称:Java Development Kit |
NDK | 原生软件开发工具包;英语全称:Native Development Kit;被Google称为NDK |
由此可见,其实不管什么XDK
,都可以叫SDK
,可能为了有很好的区分,便有了JDK
、NDK
,所以我们有的时候常说的SDK
并不是特指安卓开发工具包
,而只是我们都是同行,交流的时候都知道指的是什么,其实你们会发现,我们常常接三方平台的时候,那些工具包也是叫SDK
,但可能我们在交流的时候就会加个前缀,比如:微信分享SDK、支付宝SDK、xxSDK。
学习目标
1.配置NDK
环境并学会合理利用Android Studio
工具进行NDK
的编译
2.点击某个按钮显示由native
方法返回的一段文本信息;Java > native
3.点击某个按钮调用某个native
方法,在由此native
方法调用java
方法;java > native > java
创建工程并配置NDK路径
快速利用Android Studio
创建一个简单的Hello Word
工程,相信这个大家都已经熟门熟路了,如果你还不知道使用Android Studio
,我只能说你太不open
了。
配置工程NDK有两种方法,和配置SDK一模一样,这里就说说两个SDK
1 的配置方法吧
1.直接在
local.properties
文件中手动配置ndk.dir=E:\\Android\\sdk\\ndk-bundle //NDK路径
sdk.dir=E:\\Android\\sdk //SDK路径
2.Open Module Settings
选中工程名,鼠标右键>Open Module Settings或直接按下F4功能键
编写带有Native方法的类
1.创建JniDemo
Class文件并创建一个native
方法public native String getHelloWordText()
,用来获取Hello Word
文本
编译含有Native方法的类
javac JniDemo.java
得到JniDemo.class
文件后继续用javah
命令编译JniDemo.class
,格式:javah package name + class name
,示例:
javah com.jay.ndkdemo.JniDemo
其中com.jay.ndkdemo
是此类所在的包名,编译成功会在当前目录生成一个*.h
文件,这种文件类是C或C++所支持的头文件类型。内容如下:
#include <jni.h>
/* Header for class com_jay_ndkdemo_JniDemo */
#define _Included_com_jay_ndkdemo_JniDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jay_ndkdemo_JniDemo
* Method: getHelloWordText
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_jay_ndkdemo_JniDemo_getHelloWordText
(JNIEnv *, jobject);
}
#endif
#endif
本文示例生成的名称叫:com_jay_ndkdemo_JniDemo.h
,很明显,以包名+类名生成一个文件名,我们在工程中创建一个文件夹jni
,此目录与工程中的java
目录同级,并把生成的*.h
文件放置到jni
文件夹中。
编写C/C++代码并实现*.h中声明的方法
创建*.c<C>
或*.cpp<C++>
文件,编写Code,本文编写的是*.c
文件,也就是采用C语法来实现
* Class: com_jay_ndkdemo_JniDemo
* Method: getHelloWordText
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_jay_ndkdemo_JniDemo_getHelloWordText
(JNIEnv * env, jobject obj)
{
return (*env)->NewStringUTF(env,"Hello Word From Jni");
}
简单说下编写方法:
1.include 下我们前面生成的*.h
文件
2.实现*.h
中未实现的方法,注意方法名要与*.h
中保持一致
到这里,我们的工作已经完成了90%,剩下的只是配置与调用了
NDK编译
这个时候我们就要发挥Android Studio工具的方便性了,怎么利用NDK
编译了?前面我们已经配置好了NDK
路径,那么直接利用Android Studio
的菜单build > Rebuild
(JNIEnv * env, jobject obj)
{
return (*env)->NewStringUTF(env,"Hello Word From Jni");
}
Execution failed for task ':app:compileDebugNdk'.
> Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.
Run with --stacktrace option to get the stack trace. Run with --info or --debug
其实这个错误信息中已经告诉我们怎么解决
Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration
叫我们在gradle.properties
文件中输入android.useDeprecatedNdk=true
,输入后我们再次编译,这次编译成功,但发现一个警告:
警告信息:
<code class="hljs applescript has-numbering">Warning: Native C/C++ source code <span class="hljs-keyword">is</span> found, <span class="hljs-keyword">but</span> <span class="hljs-keyword">it</span> seems <span class="hljs-keyword">that</span> NDK option <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> configured. Note <span class="hljs-keyword">that</span> <span class="hljs-keyword">if</span> you have an Android.mk, <span class="hljs-keyword">it</span> <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> used <span class="hljs-keyword">for</span> compilation. The recommended workaround <span class="hljs-keyword">is</span> <span class="hljs-keyword">to</span> remove <span class="hljs-keyword">the</span> default jni source code directory <span class="hljs-keyword">by</span> adding: android { sourceSets { main { jni.srcDirs = [] } } } <span class="hljs-keyword">to</span> build.gradle, manually compile <span class="hljs-keyword">the</span> code <span class="hljs-keyword">with</span> ndk-build, <span class="hljs-keyword">and</span> <span class="hljs-keyword">then</span> place <span class="hljs-keyword">the</span> resulting shared object <span class="hljs-keyword">in</span> src/main/jniLibs.</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul><div class="save_code tracking-ad" style="display: none;" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>
大概意思就是我们缺少一个文件:Android.mk
,但人家给了我们推荐的方法,那就是在对应module工程中的build.gradle
文件中添加如下代码:
<code class="hljs bash has-numbering"><span class="hljs-built_in">source</span>Sets { main { jni.srcDirs = [] } }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul><div class="save_code tracking-ad" style="display: none;" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>
添加后我们再次编译,这次编译成功并没错误也没警告,终于NDK编译通过了,我们查看编译结果:\NdkDemo\app\build\intermediates\ndk
我们可以看到,生成了一系列的*.so
文件,是不是感觉很熟悉了?但我们发现*.so
文件名叫libapp.so
,这个文件名是怎么来的了?可以更改吗?答案是肯定的。
先说说默认文件名的生成格式:lib + module name.so
更改默认文件名名称:
<code class="hljs lasso has-numbering">android { <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span> defaultConfig { <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span> ndk { moduleName <span class="hljs-string">'jnidemo'</span><span class="hljs-comment">//自定义名称</span> } } }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul><div class="save_code tracking-ad" style="display: none;" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>
好,我们再次编译,编译完成后我们查看编译路径下,闷B了吧,没看到ndk
目录了,有人就会说了,你这个坑货,骗人的,友谊的小船说翻就翻。
<code class="hljs cs has-numbering">sourceSets { main { <span class="hljs-comment">// jni.srcDirs=[]</span> jniLibs.srcDir <span class="hljs-string">'src/main/jni_src'</span><span class="hljs-comment">//告知jni源码目录</span> } }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul><div class="save_code tracking-ad" style="display: none;" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>
还记得这个配置不,对,就是我们之前第一次编译的时候解决一个警告按照警告的推荐写的配置代码,只要改成和上面一样,编译后就又可以看到编译目录下的ndk
文件夹了,查看编译后的*.so
文件,发现文件名已经改了,并且生成的格式和我之前说的一样。
为什么我们按照推荐方法会有问题了?
1.可能是bug
2.我觉得应该是我们既然了默认的一些参数,当然就要对其它参数做出相应的修改
好了,看完这篇文章,我们基本实现了我们的学习目标的第一点,后面两点请看后续系列文章
- 此处
SDK
包含NDK与安卓SDK ↩
- add
gradle.properties
file to root folder of your project - add
'android.useDeprecatedNdk=true'
togradle.properties
file - Here is my gradle.properties :
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true
And add it to root of your project :