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

开发环境信息

列举下本篇文章编写的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,可能为了有很好的区分,便有了JDKNDK,所以我们有的时候常说的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功能键

sdk-dir


编写带有Native方法的类

1.创建JniDemoClass文件并创建一个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文件夹中。

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,输入后我们再次编译,这次编译成功,但发现一个警告:

ndk-build-warning

警告信息:

<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

ndk-build-result

我们可以看到,生成了一系列的*.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.我觉得应该是我们既然了默认的一些参数,当然就要对其它参数做出相应的修改


好了,看完这篇文章,我们基本实现了我们的学习目标的第一点,后面两点请看后续系列文章


  1. 此处SDK包含NDK与安卓SDK


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



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

location of gradle.properties



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值