现在使用Ndk开发的场景还蛮多,游戏引擎、音视频开发等都能涉及到,以前的工程大部分都是Eclipse的工程目录,但是App开发现在大部分都是在AndroidStudio开发工具中进行的,那就有个问题了?怎么在As中搭建Ndk的环境呢。这就是本篇文章所要解答的,并且会创建一个小例子,编译成.so文件,且在项目中使用。Come on….
在没具体动手之前我们想一想怎么实现比较好吧,假如我们新建一个As2.0的工程,然后按照他的目录结构把jni层文件放到指定的目录下,然后进行开发,固然可行,可行是可行,但是代价就是Sdk开发和Ndk开发就分离了,不在一个工程目录下,不符合我们的预期,那就换种思路吧。庆幸的是,As的gradle脚本功能比较强大,可以指定源码文件,操作任务等。如果只是修改一下配置文件,就把事情给做好了,那岂不是皆大欢喜。好吧 开始做了。
首先在工程Module的src目录下新建一个jni目录(存放我们的c、c++源码及mk文件)
然后最关键的是要在build.gradle配置一下说明jni里面的代码是咱们的ndk编译代码位置,及做一些编译任务的处理 下面配置代码由 成都 小妖 提供,特别感谢他
?
<code class = "hljs mel" >task buildNative(type: Exec, description: 'Compile JNI source via NDK' ) {
def ndkDir = android.ndkDirectory
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "$ndkDir/ndk-build.cmd" ,
'-C' , file( 'src/jni' ).absolutePath, // Change src/main/jni the relative path to your jni source
'-j' , Runtime.runtime.availableProcessors(),
'all' ,
'NDK_DEBUG=1'
} else {
commandLine "$ndkDir/ndk-build" ,
'-C' , file( 'src/jni' ).absolutePath, // Change src/main/jni the relative path to your jni source
'-j' , Runtime.runtime.availableProcessors(),
'all' ,
'NDK_DEBUG=1'
}
}
//在 mac 中 commandLine "$ndkDir/ndk-build", 但是在windows中 commandLine "$ndkDir/ndk-build.cmd",额,坑
//
task cleanNative(type: Exec, description: 'Clean JNI object files' ) {
def ndkDir = android.ndkDirectory
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "$ndkDir/ndk-build.cmd" ,
'-C' , file( 'src/jni' ).absolutePath, // Change src/main/jni the relative path to your jni source
'clean'
} else {
commandLine "$ndkDir/ndk-build.cmd" ,
'-C' , file( 'src/jni' ).absolutePath, // Change src/main/jni the relative path to your jni source
'clean'
}
}
//
clean.dependsOn 'cleanNative'
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildNative
}</code>
|
注意上面的有一个变化的地方就是jni的路径在哪,src/jni 如果跟我不一样的,应该写你的具体位置
然后在我们的代码中添加一个Native方法,就让他打印一个helloworld吧
public native void printHelloworld();
然后使用javah命令 生成对应的.h头文件
这样会在下面目录生成2个文件,当然下面的那个文件不用管它了
然后把生成的这俩文件放到刚才创建的jni里面
下面就是编写cpp文件和Android.mk Application.mk文件了
.h文件代码
?
<code class = "hljs vala" > /* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_qbao_ticket_MainActivity */
#ifndef _Included_com_qbao_ticket_MainActivity
#define _Included_com_qbao_ticket_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_qbao_ticket_MainActivity
* Method: add
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_qbao_ticket_MainActivity_printHelloworld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif</jni.h></code>
|
.cpp文件代码
?
<code class = "hljs vala" >#include <jni.h>
#include
#include "com_qbao_ticket_MainActivity.h"
#define LOG_TAG "System.out.c"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_qbao_ticket_MainActivity_printHelloworld
(JNIEnv *, jobject){
LOGI( "hello world! come from jni" );
}</android></jni.h></code>
|
注意新建写的时候 一定要加上
?
<code class = "hljs ruleslanguage" >#include "com_qbao_ticket_MainActivity.h" </code>
|
然后写咱们的mk文件,先来Android.mk
?
<code class = "hljs ruby" >LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lalala
LOCAL_SRC_FILES := com_qbao_ticket_MainActivity.cpp
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)</code>
|
要改的地方是咱们的module名称和源文件列表 这里根据自己的代码设置就好了
接下来Application.mk
?
<code class = "hljs makefile" >APP_ABI := all
APP_PLATFORM := android- 8 </code>
|
设置了全平台,当然不设置也可以 默认就行了
点击这个按钮 开始编译
编译成功会在src目录下生成一个libs和obj.local目录
然后就生成了咱们命名的liblalala.so文件 格式为lib{模块名}.so
下面是怎么在代码中使用咱们生成的.so文件了
把我们的编译生成好的.so文件拷贝到代码的libs中armeabi目录下
然后是在我们的代码中使用它
?
<code class = "hljs java" > static {
System.loadLibrary( "lalala" );
}
public native void printHelloworld();</code>
|
注意load的时候写模块名 不是文件名 这里是lalala 然后在MainActivity里面调用这个printHelloworld方法
打印一下log
好了到这里在As2.0中搭建Ndk环境并且编译so文件,并使用so文件的介绍已经讲完了,后面会在这个基础上编译一些音视频模块的代码,然后封装使用。期待吧。。。
build.gradle全部配置查看
?
<code class = "hljs r" > import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
dependencies {
compile fileTree(dir: 'libs' , include: '*.jar' )
}
android {
compileSdkVersion 21
buildToolsVersion "22.0.1"
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [ 'src' ]
resources.srcDirs = [ 'src' ]
aidl.srcDirs = [ 'src' ]
renderscript.srcDirs = [ 'src' ]
res.srcDirs = [ 'res' ]
assets.srcDirs = [ 'assets' ]
jniLibs.srcDirs = [ 'libs' ]
}
// Move the tests to tests/java, tests/res, etc...
instrumentTest.setRoot( 'tests' )
// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
// This moves them out of them default location under src/<type>/... which would
// conflict with src/ being used by the main source set.
// Adding new build types or product flavors should be accompanied
// by a similar customization.
debug.setRoot( 'build-types/debug' )
release.setRoot( 'build-types/release' )
}
buildTypes {
debug {
//proguardFile '/Users/xuchuang/Desktop/Stifler/WorkSpace/QBaoTicket_2.0/qbaoticket_2.0/proguard-project.txt'
//signingConfig signingConfigs.qbaoticket
}
release {
//signingConfig signingConfigs.qbaoticket
//minifyEnabled true
//proguardFile '/Users/xuchuang/Desktop/Stifler/WorkSpace/QBaoTicket_2.0/qbaoticket_2.0/proguard-project.txt'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
dexOptions {
jumboMode true
}
}
task buildNative(type: Exec, description: 'Compile JNI source via NDK' ) {
def ndkDir = android.ndkDirectory
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "$ndkDir/ndk-build.cmd" ,
'-C' , file( 'src/jni' ).absolutePath, // Change src/main/jni the relative path to your jni source
'-j' , Runtime.runtime.availableProcessors(),
'all' ,
'NDK_DEBUG=1'
} else {
commandLine "$ndkDir/ndk-build" ,
'-C' , file( 'src/jni' ).absolutePath, // Change src/main/jni the relative path to your jni source
'-j' , Runtime.runtime.availableProcessors(),
'all' ,
'NDK_DEBUG=1'
}
}
//在 mac 中 commandLine "$ndkDir/ndk-build", 但是在windows中 commandLine "$ndkDir/ndk-build.cmd",额,坑
//
task cleanNative(type: Exec, description: 'Clean JNI object files' ) {
def ndkDir = android.ndkDirectory
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "$ndkDir/ndk-build.cmd" ,
'-C' , file( 'src/jni' ).absolutePath, // Change src/main/jni the relative path to your jni source
'clean'
} else {
commandLine "$ndkDir/ndk-build.cmd" ,
'-C' , file( 'src/jni' ).absolutePath, // Change src/main/jni the relative path to your jni source
'clean'
}
}
//
clean.dependsOn 'cleanNative'
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildNative
}</type></type></code>
|