您的位置:首页 > 移动开发 > Android开发

Android JNI/NDK开发之基本姿势<一>

2016-10-23 09:57 302 查看

开发环境信息

列举下本篇文章编写的Demo基本信息

操作系统Windows 10 家庭中文版
开发工具Android Studio 2.1
SDKnew
NDKnew

扫盲之SDK、JDK、NDK的区别

SDK软件开发工具包;英语全称:Software Development Kit
JDKJava语言的软件开发工具包;英语全称: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
文本

public class JniDemo {

    public native String getHelloWordText();

}

编译含有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++所支持的头文件类型。内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class com_jay_ndkdemo_JniDemo */

#ifndef _Included_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);

#ifdef __cplusplus

}

#endif

#endif

本文示例生成的名称叫:
com_jay_ndkdemo_JniDemo.h
,很明显,以包名+类名生成一个文件名,我们在工程中创建一个文件夹
jni
,此目录与工程中的
java
目录同级,并把生成的
*.h
文件放置到
jni
文件夹中。



编写C/C++代码并实现*.h中声明的方法

创建
*.c<C>
*.cpp<C++>
文件,编写Code,本文编写的是
*.c
文件,也就是采用C语法来实现

#include <com_jay_ndkdemo_JniDemo.h>

/*

 * 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


JNIEXPORT jstring JNICALL Java_com_jay_ndkdemo_JniDemo_getHelloWordText

  (JNIEnv * env, jobject obj)

{

 return (*env)->NewStringUTF(env,"Hello Word From Jni");

}

FAILURE: Build failed with an exception.

* What went wrong:

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.

* Try:

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


After updating Android Studio to version 1.3.0
I am getting “NDK integration is deprecated in the current plugin” Error

add
gradle.properties
file to root folder of your project
add
'android.useDeprecatedNdk=true'
to
gradle.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 :



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: