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

在一些老的项目或老的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 __cplusplus
extern "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 2017
ndk.dir=E\:\\AndroidSDK\\ndk-bundle
sdk.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 FAILED
Information:Total time: 1.303 secs
Information:1 error
Information:0 warnings
Information: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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值