博客导航
#一、搭建NDK环境
NDK可以自己下载好,然后布置一下环境变量。不过我这里有个更加简便的方法。在Android studio2.2及更高版本中可以像插件那样安装NDK了。
设置流程如下:
点开File->Setting,弹出Setting窗口。
如图所示,在setting -> Appearance&Behavior -> System Setting -> Android SDK -> SDK Tools(视图右边),选择CMake、LLDB、NDK这三个下载。然后一边静静地等它下载安装。
如果你是建好工程再下载NDK,你还要在File ->Project Structure里面设置一下NDK地址。这里直接按“Select default NDK”就可以了。
它会自己生成路径:D:\Android\Sdk\ndk-bundle(这是我电脑NDK的地址)
并且会在local.properties里自动生成一行ndk目录地址。
在gradle.properties里面添加
android.useDeprecatedNdk=true
至此,你可以愉快地编写Jni了。
#二、Jni编写
##1)编写Java接口
在项目里新建一个Java类。
public class Jni {
public native String say(String str);
}
注意这里用到的是“native”修饰。
##2)编写c/c++接口及代码
Android studio有个Terminal工具,对应Windows系统的cmd窗口。
在cmd里面cd进入项目的Java文件夹里面,因为Java文件夹里面的是对应包名
package com.example.jnitest;
输入javah -jni com.example.jnitest.Hello然后生成.h文件。
项目目录下就多了一个com_example_jnitest_Jni.h文件。
这个就是c/c++的头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jnitest_Jni */
#ifndef _Included_com_example_jnitest_Jni
#define _Included_com_example_jnitest_Jni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_jnitest_Jni
* Method: say
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jnitest_Jni_say
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
新建JNI文件夹
然后把com_example_jnitest_Jni.h文件拉进去
在jni目录里新建c/c++源码文件
#include <stdio.h>
#include <stdlib.h>
#define TAG "JNI" //自定义的变量
#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG ,__VA_ARGS__)
#include "com_example_jnitest_Jni.h"
JNIEXPORT jstring JNICALL Java_com_example_jnitest_Jni_say
(JNIEnv *env, jobject, jstring str) {
LOGD("This is JNI");
return str;
}
上面的c/c++源码#include "com_example_jnitest_Jni.h"
就是引入刚刚生成的.h文件。Java_com_example_jnitest_Jni_say是对应Java类里的say方法。这个名字是要固定的格式写的,文件目录-文件名-方法名,如果你取其他名称也需准守起名规则。
总体的目录结构如下:
##3)生成so文件
打开app Module的build.gradle文件,在defaultConfig节点加入NDK的配置信息。
ndk {
moduleName "HelloWorld" //so文件名
ldLibs "log", "z", "m" //添加系统库文件
abiFilters "armeabi", "armeabi-v7a", "x86" //d对应芯片结构
}
moduleName是你的so文件名,lbLibs附加系统库文件,abIlity添加你要生成的库对应的芯片架构。
这里添加了log,在这个项目代码对应是
#define TAG "JNI" //自定义的变量
#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG ,__VA_ARGS__)
LOGD("This is JNI");
这个是在c/c++源码里添加log日记。
布置好了就可以编译了。
在工具来点击编译按钮,或者在Build -> Make Project来执行。
编译好了后,你会在你的项目module里面的build -> intermediates ->ndk ->发现对应的so文件。
注意你的项目所在的目录文件夹名字不能有空格,否则编译不通过了。
上面的配置在运行时候,每次都会编译一下的,你程序有对应的调用代码会调用到这个文件的,如果你不想每次都编译一下so库,你也可以把对应的so库拿出来用。
把刚刚生成的so文件连同文件夹复制到项目的libs文件夹里面,当然了,你也可以放到项目的其他位置。
注意要连同上面的“armeabli”,“armeabi-v7a”,"x86"文件夹一起复制,系统调用时候会根据不同的芯片架构来调用不同的so库。
然后在app的build.gradle指引一下配置目录,并设置不自动ndk编译。
android {
......
sourceSets.main {
jniLibs.srcDir 'libs'//放so文件的目录
jni.srcDirs = [] //disable automatic ndk-build call
}
}
好了上面几步你已经生成对应的so库了,下面就是调用。
##4)调用so库
先需要加载动态库,动态库的名称是编译时候在build.gradle设置的名称。
然后像普通的方法一样调用即可。
public class MainActivity extends AppCompatActivity {
static { // 加载动态库
System.loadLibrary("TestJNI");
}
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.text_hello);
Jni jni = new Jni();
tv.setText(jni.say("Hello world. This message is from Jni."));
}
}
源码下载地址:HelloWord