Android Studio下JNI开发的基本步骤

  • 第一步 设置ndk路径 配置相应的开发环境

  • 第二步 配置快捷键 Settings -> External tools中配置javah,javap,ndk-build快捷方式,(这一步主要是为了简化命令行输入,使用原生命令行也是可以的)

javah参数配置(直接拷贝):
Program: $JDKPath$\bin\javah.exe
Parameters: -classpath . -jni -o $ModuleFileDir$/src/main/jni/$Prompt$ $FileClass$
Working directory: $ModuleFileDir$\src\main\Java
Parameters的另外一种写法: -classpath . -jni -d $ModuleFileDir$/src/main/jni $FileClass$

javap参数配置(直接拷贝):*
Program: $JDKPath$\bin\javap
Parameters: -s -p $FileClass$
Working directory: $ModuleFileDir$\build\intermediates\classes\debug

ndk-build参数配置(直接拷贝):

Program: D:\Android_NDK\android-ndk-r11b\ndk-build.cmd

Working directory: $ModuleFileDir$\src\main

  • 第三步 创建java类 引用即将创建的链接库,以及创建所需要的本地方法
1
2
3
4
5
static {
System.loadLibrary("MyJni");//导入指定的so库文件名称
}
public native String getStringFromNative();//本地方法
public native String getString_From_c();
  • 第四步 使用之前配置的javah快捷键快速生成.h头文件

此时 会自动创建jni目录并生成头文件

  • 第五步 参考头文件 在jni目录下开始编写C/C++代码

注:

项目结构切换成 Android状态时,jni文件夹显示成 cpp名字!

当切换成project时就显示成jni文件夹!!

如下图:

添加如下代码:

  • 第六步 配置gradle
1
2
3
4
5
 ndk{
moduleName "MyJni"//编译后so库的名字
ldLibs "log"//连接的库,可以有多个
abiFilters "armeabi","armeabi-v7a","x86"//指定so库运行的cpu架构,有armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64这些,常用的是armeabi和armeabi-v7a
}

点击Androidstudio 菜单栏 Build ->ReBuildProject 后自动生成Android.mk文件

把Android.mk文件拷贝到 main/jni文件夹下 然后右键—>External Tools –>ndk-build 生成 .so文件!!

其次 在项目的gradle.properties 文件中添加

1
android.useDeprecatedNdk=true
  • 第七步 运行java代码 调取c库

注意事项

  • 加载生成的动态库指定的文件名( System.loadLibrary(“MyJni”);)和生成.so时指定的名字(build.gradle中的ndk{moduleName “MyJni” }),还有Android.mk中LOCAL_MODULE := MyJni三者名称需要保持一致;

  • 异常提示不支持c++

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio.  Please switch to a supported build system.
    Consider using CMake or ndk-build integration. For more information, go to:
    https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
    To get started, you can use the sample ndk-build script the Android
    plugin generated for you at:
    E:\IT\youban\module_c++\build\intermediates\ndk\release\Android.mk
    Alternatively, you can use the experimental plugin:
    https://developer.android.com/r/tools/experimental-plugin.html
    To continue using the deprecated NDK compile for another 60 days, set
    android.deprecatedNdkCompileLease=1570504380180 in gradle.properties

    解决方案:在build.gradle中添加如下配置即可:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    android{

    //增加之后如下信息之后,右键项目的时候Link C++ Project with Gradle选项不再显示;
    // externalNativeBuild {
    // ndkBuild {
    // path file('src/main/jni/Android.mk')
    // }
    // }

    }

附:C调java代码

Java中代码如下:
1
2
3
4
5
6
7
8
static {

System.loadLibrary("MyJni");//导入指定的so库文件
}

public void show(){
System.out.println("hahaha C++调了我");
}
C中代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//在c代码里面调用java代码里面的方法

//1 . 找到java代码的 class文件
jclass dpclazz = (*env)->FindClass(env, "com/insworks/module_ccc/CCCTestActivity");
if (dpclazz == 0) {
return (*(*env)).NewStringUTF(env, "NDK 没有找到指定的类");
}

//2 寻找class里面的方法
jmethodID method1 = (*env)->GetMethodID(env, dpclazz, "show", "()V");
if (method1 == 0) {
return (*(*env)).NewStringUTF(env, "NDK 没有找到方法");
}

//3.实例化类
jobject jobject1 = (*env)->AllocObject(env, dpclazz);

//4 .调用这个方法
(*env)->CallVoidMethod(env, jobject1, method1);

注意事项:

  1. 原生方法在C和C++的调用方式不同,例如:
1
2
3
4
/* C */
return (*env)->NewStringUTF(env, "Hello World");
/* C++ */
return env->NewStringUTF("Hello World");

​ 在C语言中,JNIEnv是指向JNINativeInterface结构的指针,使用它必须要解引用。而第一个参数还是env,学过C和C++语言都知道,C语言是面向过程语言,NewStringUTF只是一个函数指针,调用该方法还不清楚调用者,所以要传递env,而C++就不用,因为C++是面向对象语言,这个就不解释咯

2.关于C++调用C函数或者变量

1
2
3
4
5
//在C++中引用C语言中的函数和变量,在包含C语言头文件时(假设为cExample.h),需进行以下处理:
//  extern "C"
//  {
//    #include "cExample.h";
//  }

3.关于so文件的名称问题

Android.mk文件中的LOCAL_MODULE 决定了so文件的名称,LOCAL_MODULE 的名称可以手动修改也可以在build.gradle中配置:

1
2
3
4
       ndk {
// moduleName "native_datahelp"//编译后so库的名称

}

4.关于System.loadLibrary();

为什么loadLibrary中填入的名称不一致,却能依然运行成功不报错?

目前自测发现 build文件中出现很多编译后的so库 有可能是旧so库未及时清除的原因,可以过研究研究build文件夹,里面藏着非常多的秘密

5.ndk-build不执行照样运行成功

C/C++源码文件改动后自动调用ndk-build编译生成新的so库存放在build文件夹中,可以拷贝直接使用,ndk-build生成正式的so库,这跟apk打包是同样的道理 Android.mk文件是必须存在的,否则无法编译生成so库,也无法编译识别源文件

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

0%