Ubuntu下NDK编译环境搭建及在Android Studio中集成NDK

首先需要下载NDK编译工具,目前官方最新的NDK版本是r11c,为了保证稳定性,我下的是r11b。

能翻墙的可以从谷歌官网下载,地址为: https://developer.android.com/ndk/downloads/index.html

不能翻墙的也可以从下面网站下载,该网站提供了Android开发各种工具的下载: http://www.androiddevtools.cn/index.html

配置环境变量

将下载下来的文件进行解压,我这里解压到~/android/android-ndk-r11b。

然后在~/.profile文件中加入如下内容,将NDK目录加入到PATH中:

#set ndk environment
export NDK_HOME=~/android/android-ndk-r11b
export PATH=$PATH:$NDK_HOME

最后执行“source ~/.profile”使之生效。

配置好环境变量之后,需要验证一下是否搭建成功,在命令行下输入ndk-build,有如下提示则表示搭建成功了。

在Android Studio中集成NDK

创建Android项目

在Android Studio中创建一个Android工程,我这里创建一个名为NDKJNIDemo的工程。

配置ndk.dir

在项目中的local.properties文件中添加如下代码来指定ndk的目录:


ndk.dir=~/android/android-ndk-r11b

配置gradle的ndk模块

在module的build.gradle文件的android.defaultConfig添加如下代码:

ndk {
    moduleName "NDKJNIDemo"
}

这里配置的moduleName就是编译生成so库的名字,比如这里生成的so库名字为“libNDKJNIDemo.so”。

ndk还可以配置更多选项,如下:

ndk { 
    moduleName "NDKJNIDemo"
    cFlags "-DANDROID_NDK -D_DEBUG DNULL=0"    // Define some macros
    ldLibs "EGL", "GLESv3", "dl", "log"        // Link with these libraries!
    stl "stlport_shared"                       // Use shared stlport library
    abiFilters "armeabi", "armeabi-v7a", "x86" // Set the platform
}

配置编译平台(可选)

Android Studio默认会编译所有平台下的so库。

如果你只想编译指定平台的so库,可以加入如下配置,在module的build.gradle文件的android中加入如下代码:

ndk {
      moduleName "NDKJNIDemo"
      abiFilters "armeabi", "armeabi-v7a", "x86"
}

效果如下:

创建包含native方法的类

我这里创建了一个JniUtils类,并且创建了一个名为getStringFromC()的nativie方法,该方法作用就是简单的从JNI层返回一个字符串给Java层,代码如下:

package com.liuling.ndkjnidemo;
/**
 * Created by liuling on 16-5-16.
 */
public class JniUtils {
    static {
        System.loadLibrary("NDKJNIDemo");//与build.gradle里面设置的so名字,必须一致
    }
    public static native String getStringFromC();
}

这里得注意loadLibrary加载的so库的名字必须和上面第3步配置的moduleName保持一直,否则会报找不到库文件的异常。

创建C/C++源代码

生成头文件

进入module/build/intermediates/classes/debug目录下,在命令行下使用javah生成头文件,我这里是这样的:

cd app/build/intermediates/classes/debug
javah -jni com.liuling.ndkjnidemo.JniUtils

完了之后会在module/build/intermediates/classes/debug目录下生成相应的头文件,我这里生成的是com_liuling_ndkjnidemo_JniUtils.h,内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <string.h>
/* Header for class com_liuling_ndkjnidemo_JniUtils */
#ifndef _Included_com_liuling_ndkjnidemo_JniUtils
#define _Included_com_liuling_ndkjnidemo_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_liuling_ndkjnidemo_JniUtils
 * Method:    getStringFormC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_liuling_ndkjnidemo_JniUtils_getStringFromC
  (JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

创建jni目录

在module/src/main/下面新建jni目录,ndk会默认编译该目录下的源文件。目录结构如下图:

当然,你也可以自定义C/C++源代码目录,在module的build.gradle文件的android中添加如下代码:

sourceSets {
    main {
        jni.srcDirs 'src/main/jnisrc'
    }
}

拷贝头文件到C/C++源代码目录并创建C源文件

将前面生成的头文件拷贝到C/C++源代码目录,并创建相应的C代码文件,我这里创建了com_liuling_ndkjnidemo_JniUtils.c,内容如下:

#include "com_liuling_ndkjnidemo_JniUtils.h"
/*
 * Class:     com_liuling_ndkjnidemo_JniUtils
 * Method:    getStringFormC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_liuling_ndkjnidemo_JniUtils_getStringFromC
        (JNIEnv *env, jclass obj) {
    return (jstring)(*env)-> NewStringUTF(env, "I am string from jni-jnisrc");
}

Java_com_liuling_ndkjnidemo_JniUtils_getStringFromC方法就是对应JniUtils里的native方法getStringFromC()。

运行效果

Gradle配置覆盖默认NDK编译

大家会发现几个问题:

  • 为什么运行程序之后在main下面没找到so库呢?

  • 为什么编译时不需要Android.mk文件呢?

默认情况下ndk将生成的so库放到了build下面去了,同时也会使用一个默认的Android.mk文件进行编译,如下图:

那我们能否覆盖默认的设置呢?答案时肯定的!

首先在项目gradle文件的android{}中添加如下代码:

sourceSets {
    main {
        jni.srcDirs = []   //屏蔽gradle的jni生成过程
        jniLibs.srcDir 'src/main/libs'  //指定引用so库的目录
    }
}

然后在gradle文件最底部添加一个ndk编译task:

task ndkBuild(type: Exec) {
    commandLine 'ndk-build', '-C', file('src/main/jni').absolutePath
}
tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn ndkBuild
}

注意:使用上述命令必须确定ndk的环境变量配置好了,也就是确定ndk的目录加到PATH中去了,否则该任务会执行失败。

最后自己在jni目录下面建立Android.mk和Application.mk文件,直接点击AS上的运行就会先执行NDK编译的过程。

Android.mk:

LOCAL_PATH := $(call my-dir)
local_c_includes := \
	$(NDK_PROJECT_PATH) \
	
include $(CLEAR_VARS)
# so库名字
LOCAL_MODULE    := NDKJNIDemo 
LOCAL_SRC_FILES := com_liuling_ndkjnidemo_JniUtils.c
# 添加log模块
LOCAL_LDLIBS    := -lm -llog
include $(BUILD_SHARED_LIBRARY)

Application.mk:

APP_ABI := armeabi x86
#使NDK支持string
APP_STL := stlport_shared
APP_STL := stlport_static

可以看到,默认情况下可以在gradle中的ndk中配置以上这些信息:

ndk {
    moduleName "NDKJNIDemo"
    ldLibs "log"
    stl "stlport_shared"
    abiFilters "armeabi", "x86" 
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值