Android学习笔记-NDK开发(一)

来源:互联网 发布:怎样申请旺旺淘宝账号 编辑:程序博客网 时间:2024/05/16 05:19

在一些老的项目或老的Android教程中,有可能还使用着本篇文章所描述的方式来创建一个Android NDK项目,所以,就写了此篇文章以供参考。

下面以AS中一个简单的例子来按步骤说明:

第一步:创建一个新的Android项目(此处不勾选“include C++ support”,只按普通项目创建)。


第二步:在“src/main/java/你的包名”中,新建一个调用NDK函数的Java类,并且在Java类中定义(与NDK函数名称相同的)native方法。

package com.lonly.example.ndkdemo2;public class MyNDK {    public static native int sequare(int num);    static {        //参数是.so库的名称:libJniDemo.so 的名称为JniDemo        System.loadLibrary("JniDemo");    }}
第三步:准备头文件(.h)。

在AS的Terminal面板,输入命令定位到上一步所创建Java类的根目录,使用下面的命令自动生成头文件javah -jni -encoding UTF-8  com.lonly.example.ndkdemo2.MyNDK(全类名),按回车键,系统会自动在“src/main/java/”目录下生成一个.h文件。


打开后是这样子的:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_lonly_example_ndkdemo2_MyNDK */#ifndef _Included_com_lonly_example_ndkdemo2_MyNDK#define _Included_com_lonly_example_ndkdemo2_MyNDK#ifdef __cplusplusextern "C" {#endif/* * Class:     com_lonly_example_ndkdemo2_MyNDK * Method:    sequare * Signature: (I)I */JNIEXPORT jint JNICALL Java_com_lonly_example_ndkdemo2_MyNDK_sequare  (JNIEnv *, jclass, jint);#ifdef __cplusplus}#endif#endif
里面声明了我们定义的native方法。

第四步:创建jni目录,将生成的.h头文件移动到这个目录。

第五步:在jni目录下创建cpp文件,实现.h文件中的函数。


代码:

#include "com_lonly_example_ndkdemo2_MyNDK.h"//计算一个整数的平方JNIEXPORT jint JNICALL Java_com_lonly_example_ndkdemo2_MyNDK_sequare  (JNIEnv *env, jclass clz, jint num){        return num * num;  }
第六步:设置local.properties文件,指定Androidndk的根目录位置 (ndk.dir=E\:\\AndroidSDK\\ndk-bundle)。
## This file is automatically generated by Android Studio.# Do not modify this file -- YOUR CHANGES WILL BE ERASED!## This file must *NOT* be checked into Version Control Systems,# as it contains information specific to your local configuration.## Location of the SDK. This is only used by Gradle.# For customization when using a Version Control System, please read the# header note.#Wed Oct 25 16:32:30 CST 2017ndk.dir=E\:\\AndroidSDK\\ndk-bundlesdk.dir=E\:\\AndroidSDK

第七步:设置build.gradle文件,编译NDK程序,生成libJniDemo.so文件。

 ndk{            moduleName "JniDemo"        }

第八步:gradle.properties文件中加入android.useDeprecatedNdk=true。


第九步:调用NDK函数。

public class MainActivity extends AppCompatActivity {    // JAVA_packagename_classname_functionname    // jstring String  jint int    //1.建立一个调用NDK函数的Java类,并且在Java类中定义与NDK函数名称相同的native方法    //2. 准备头文件(.h)    // 使用下面的命令自动生成头文件javah -jni -encoding UTF-8  com.lonly.example.ndkdemo2.MyNDK    //3.创建jni目录,将头文件移动到这个目录    //4.在jni目录下创建cpp文件,实现.h文件中的函数    //5.设置local.properties文件,指定Androidndk的根目录位置 ndk.dir=E\:\\AndroidSDK\\ndk-bundle    //6. 设置build.gradle文件,编译NDK程序,生成libJniDemo.so文件    //7.调用NDK函数    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        TextView tv = (TextView) findViewById(R.id.textview);        tv.setText(String.valueOf(MyNDK.sequare(21)));    }}

知识拓展:

1..h文件中声明的函数参数JNIEnv与JavaVM、jclass与jobject。参考http://blog.csdn.net/freechao/article/details/7692239

  • JNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立。JavaVM是虚拟机在JNI层的代表,在一个虚拟机进程中只有一个JavaVM,因此该进程的所有线程都可以使用这个JavaVM。当后台线程需要调用JNI native时,在native库中使用全局变量保存JavaVM尤为重要,这样使得后台线程能通过JavaVM获得JNIEnv。native程序中频繁使用JNIEnv*和JavaVM*。
  • 当NDK方法声明为static静态方法时,参数使用jclass,非静态时,使用jobject。

2.javah命令生成.h头文件时报错误“错误: 编码GBK的不可映射字符”。

解决方法:应该使用-encoding参数指明编码方式,如: 

javah -jni -encoding UTF-8 com.example.XXXX.XXXX.MainActivity

3.编译运行时,出现以下异常:

Error:Execution failed for task ':app:compileDebugNdk'.> Error: Your project contains C++ files but it is not using a supported native build system.  Consider using CMake or ndk-build integration with the stable Android Gradle plugin:https://developer.android.com/studio/projects/add-native-code.html  or use the experimental plugin:http://tools.android.com/tech-docs/new-build-system/gradle-experimental.Information:BUILD FAILEDInformation:Total time: 1.303 secsInformation:1 errorInformation:0 warningsInformation:See complete output in console

这是因为项目中包含了jni文件夹和jni文件夹中需要ndk-build的c文件,AS会调用ndk-build去编译这些jni代码,但项目中却没有将“useDeprecatedNdk”设为true。解决方法:在gradle.properites中添加“android.useDeprecatedNdk=true”。

4.生成的.so文件的位置,如何使用.so文件?

此时AS每次编译build时会将jni中的代码编译成静态库并放到app/build/intermediates/ndk/debug/lib/下,APP运行时所调用的so库就是该目录下的so库,如果想只留下so库,而不再需要jni代码,需要做的是:①删除jni文件夹;②将app/build/intermediates/ndk/debug/lib/下已经编译好的so库拷贝至app/libs中,指定so存放目录build.gradle->sourceSets;③注释掉gradle.properites中android.useDeprecatedNdk=true。  

源码传送门:https://github.com/legallonly/NDKDemo2
原创粉丝点击