官方文档介绍如下:
The Android NDK is a toolset that lets you implement parts of your app in native code, using languages such as C and C++. For certain types of apps, this can help you reuse code libraries written in those languages.
大概意思就是NDK它是一个工具集, 它可以让你使用C/C++ 语言来实现APP的某些部分。对于某些类型的应用,NDK可以帮助你使用C/C++重写代码库。
与NDK密切相关的另一个词汇则是JNI,它是NDK开发中的枢纽,Java与C/C++层交互基本上就是是通过它来完成的,那么什么是JNI?
JNI:Java Native Interface 也就是java本地接口,它是一个协议,这个协议用来沟通java代码和本地代码(c/c++)。通过这个协议,Java类的某些方法可以使用原生实现,同时让它们可以像普通的Java方法一样被调用和使用,而原生方法也可以使用Java对象,调用和使用Java方法。也就是说,使用JNI这种协议可以实现:java代码调用c/c++ 代码,而c/c++代码也可以调用java代码。
使用AS创建好一个Project后,需要注意几个地方的配置:
1.在项目gradle.properties文件中加上以下代码,表示我们要使用NDK进行开发。
android.useDeprecatedNdk=true
2.在项目local.properties中加入ndk和sdk的路径
// 这是我的ndk及sdk安装路径
ndk.dir=D\:\\software\\JAVA\\android-sdk-windows\\ndk-bundle
sdk.dir=D\:\\software\\JAVA\\android-sdk-windows
3.在app文件夹下的build.gradle中的defaultConfig里加入如下代码
android {
......
defaultConfig {
......
ndk {
moduleName "MyFirstJinTest"//指定生成的so文件名
abiFilters "armeabi-v7a", "x86"//cpu的类型
}
}
......
}
第一步:创建好一个类
创建一个Java类,例如我的是MyFirstJniTest.java,具体代码如下:
package com.example.androidqunyinhui.jni;
public class MyFirstJniTest {
static {
//此名字必须和build.gradle中的ndk下moduleName一致
try {
System.loadLibrary("MyFirstJinTest");
Log.i("JNI", "MyFirstJinTest load success");
} catch (Exception e) {
Log.e("JNI", "MyFirstJinTest load error");
e.printStackTrace();
}
}
public static native String getString();
public static native int add(int a, int b);
}
第二步:创建对应的.h头文件
在完成第一步之后,我们在AS的命令行中输入如下命令:
// 进入到java目录
cd app/src/main/java
// javah是进行头文件生成
// 例如 我的为 avah -jni com.example.androidqunyinhui.jni.MyFirstJniTest
javah -jni 包名.类名
此时在java目录下会看到com_example_androidqunyinhui_jni_MyFirstJniTest.h文件
内容大致如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_example_androidqunyinhui_jni_MyFirstJniTest */
#ifndef _Included_com_example_androidqunyinhui_jni_MyFirstJniTest
#define _Included_com_example_androidqunyinhui_jni_MyFirstJniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_androidqunyinhui_jni_MyFirstJniTest
* Method: getString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_androidqunyinhui_jni_MyFirstJniTest_getString
(JNIEnv *, jclass);
/*
* Class: com_example_androidqunyinhui_jni_MyFirstJniTest
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_androidqunyinhui_jni_MyFirstJniTest_add
(JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
粗略的看,我们会看到两个和我们在MyFirstJniTest类中定义函数名相同的方法:getString和add,只不过前面加了一堆前缀。
第三步:新建jni文件夹,并拷贝上面生成的.h文件到jni目录
选中src,右键:New->Folder->JNI Folder
点击finish,此时我们可以看到在src目录下创建了一个jni目录。
将上面生成的.h文件拷贝到该jni文件加下。
第四步:在jni目录下,右键新建C文件,文件名任意
在jni目录下,右键新建C文件,文件名任意。
该.C文件的作用就是对.h文件的方法进行实现,例如创建的文件名为MyFirstJniTest.c,具体内容如下:
#include
//返回一个字符串
JNIEXPORT jstring JNICALL Java_com_example_androidqunyinhui_jni_MyFirstJniTest_getString
(JNIEnv *env, jclass jobj){
return (*env)->NewStringUTF(env,"MyFirstJniTest 我是用jni调用出来的字符串");
}
//返回 a+b的结果
JNIEXPORT jint JNICALL Java_com_example_androidqunyinhui_jni_MyFirstJniTest_add
(JNIEnv *env, jclass jobj, jint a, jint b){
return a+b;
}
第五步:选择 Build->Make Project
选择 Build->Make Project,这时候可以看到在app/build/intermediates/ndk/debug/lib目录下生成了对于的.so文件。
如果没有生成,选择 Build->Clean Project,等clean完成后,再Build->Rebuild Project,一般经过上面两步以后都能够解决问题。
完成以上步骤,基本上算是完成了一个小型的JNI程序,我们编写C/C++代码,Java层如何去调用。
但是。。但是。。。
在真实的项目开发中,我们到这里并没有结束,通常情况下,我们不会将C相关代码也写到Android 工程文件下,Android中需要调用C/C++相关代码,一般都是通过so文件加载的,so文件通常是放在app/jniLibs或者是app/libs目录下。这样的好处相信大家都明白,不在说明。
需要注意的是,当我们把so文件放在app/libs目录下的时候,还需要在app下的build.gradle文件做如下配置:
android {
......
sourceSets {
main {
jniLibs.srcDir 'libs'
}
}
......
}
这是因为在加载so文件的时候,默认是app/jniLibs文件夹,因此当改变文件夹的时候,需要对jniLibs.srcDir 指定路径。
下面看看我的demo最终示例效果
public class JniTestActivity extends AppCompatActivity {
public static void startActivity(Context context){
Intent intent = new Intent(context, JniTestActivity.class);
context.startActivity(intent);
}
private TextView tvShowInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jni_test);
initView();
}
private void initView(){
tvShowInfo = (TextView) findViewById(R.id.tv_show_info);
findViewById(R.id.btn_show_info).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String text = MyFirstJniTest.getString();
int sum = MyFirstJniTest.add(1, 2);
tvShowInfo.setText(text+" sum="+sum);
}
});
}
}
https://www.cnblogs.com/guanmanman/p/6769240.html
https://blog.csdn.net/u011652925/article/details/78272406