什么是NDK,跟JNI之间有什么关系
原生开发工具包 (NDK) :是一套工具,允许您为 Android 使用 C 和 C++ 代码,并提供众多平台库。
Java 原生接口 (JNI) :JNI是一个协议,这个协议用来沟通java代码和外部的本地代码(c/c++),外部的c/c++代码也可以调用java代码。
需要NDK的原因
- 进一步提升设备性能,以实现低延迟时间,或运行计算密集型应用,如游戏或物理模拟。
- 重复使用您自己或其他开发者的 C 或 C++ 库。
- 提高代码的安全性
如何使用NDK
要为您的应用编译和调试原生代码,您需要以下组件:
- Android 原生开发工具包 (NDK):这套工具允许您为 Android 使用 C 和 C++ 代码。
- CMake:一款外部构建工具,可与 Gradle 搭配使用来构建原生库。如果您只计划使用 ndk-build,则不需要此组件。
- LLDB:一种调试程序,Android Studio 使用它来调试原生代码。
说到构建,使用 NDK 构建代码又有三种方法:
Android Studio 用于的默认工具是 CMake。由于很多现有项目都使用构建工具包编译其原生代码,Android Studio 还支持 ndk-build。所以我们今天所讲的是用CMake来构建的。
说太多,不但记不住,反而容易困,下面我们先来体验一下成功的感觉。10秒开启ndk大法传给你
一路点击next,finish即可。
创建完成后,项目会自动使用,创建完成后可能有些人的as原来没有安装ndk的,这个时候需要安装一下,或者有些人的ndk另外指定了旧版的,那么也要重新指定一下路径。指定路径步骤如下:
在Android NDK location重新指定位置即可。
AS自动帮我们生成了.cpp文件,并且修改了build.gradle进行了ndk配置,最后在MainActivity进行调用
调用c语言的方法就这三部,剩下的就是要编写c的代码了。逻辑都写在了.cpp文件中,仔细看会发现c文件中的代码看起来有点吃力。其实这里是c的语法,但是要需要用java原生接口JNI库才能将java与c打通。
JNI的使用
不难发现,他的对象,数据类型,都跟两种语言有所区别,如string类型,在jni中被定义为jstring,还有其他类型也是差不多是这样的规律(j+原来的基本数据类型)
JNIWXPORT和JNICALL是宏。
JNIEnv*指向一个位置,该位置包含一个指向函数表的指针,表中的每一项都是一个指向JNI函数的指针,native方法通过JNI函数访问JVM的中的数据 ,如下所示:
第二个参数对于非静态方法为jobject,对于静态方法为jclass。jobject表示调用native方法对象自身的引用,如同C++中的this指针;jclass表示定义native方法的类的引用。
MainActivity.java
package com.example.administrator.ndkapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI()+"\n"
+stringFromJNI2()+"\n"
+ Arrays.toString(getIntArray(9))+"\n"
);
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native String stringFromJNI2();
public native int[] getIntArray(int count);
}
native-lib.cpp
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_administrator_ndkapplication_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
//方法名必须为本地方法的全类名点改为下划线,穿入的两个参数必须这样写,
//第一个参数为Java虚拟机的内存地址的二级指针,用于本地方法与java虚拟机在内存中交互
//第二个参数为一个java对象,即是哪个对象调用了这个 c方法
extern "C"
JNIEXPORT jstring
JNICALL Java_com_example_administrator_ndkapplication_MainActivity_stringFromJNI2(JNIEnv* env,
jobject obj){
//定义一个C语言字符串
char* cstr = "hello form c";
//返回值是java字符串,所以要将C语言的字符串转换成java的字符串
//在jni.h 中定义了字符串转换函数的函数指针
jstring jstr2 = env -> NewStringUTF(cstr);
return jstr2;
}
/**
* 从 Native 返回 int 数组,主要调用 set<Type>ArrayRegion 来填充数据,其他数据类型类似操作
*/
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_administrator_ndkapplication_MainActivity_getIntArray(JNIEnv *env, jobject instance,
jint num) {
jintArray intArray;
intArray = env->NewIntArray(num);
jint buf[num];
for (int i = 0; i < num; ++i) {
buf[i] = i * 2;
}
// 使用 setIntArrayRegion 来赋值
env->SetIntArrayRegion(intArray, 0, num, buf);
return intArray;
}
有关于jni还想要深入了解的,可以参阅官方文档,英文不好的只能问Google了。这里就不详细介绍了
.so文件如何生成
实际上在你跑项目的时候,so库就生成在build文件夹里了。路径为:app\build\intermediates\cmake\debug\obj,知道有部分同学不喜欢看文字,所以还是贴图吧。当你想应用到别的项目中时,这个so库直接拿过去用就可以了,非常方便。
源码传送门:https://download.csdn.net/download/u014736095/11177673
c调用java
使用jni调用java有三个步骤,如注释。
步骤2中的方法签名需要用到命令行,获取方法如下:
找到目标方法所在的,编译过后的class文件(在build\intermediates\classes\debuig文件夹下),通过命令行“javap -s 完整包名” 获取