使用NDK进行JNI编程与实践一:使用Android studio进行JNI开发

一、 环境需求:
java是安卓应用开发的主要语言,但是有时候,我们没有办法直接调用安卓提供的java接口来实现对framework层以下的开发,这个时候,就需要JNI上场了,使用java本地接口,在下面利用C/C++实现对java本地接口的实现,关于JNI编程,网上的资料和博客很多,但是有的很零散,特别是使用的AS版本和gradle版本等,导致差异和可实用性不大,在实际的学习过程中遇到了很多坑,这里通过博客重新记录一下。
①JNI开发推荐博客:

https://www.jianshu.com/p/919719964ec4

②JNI开发推荐书籍(比较老了,仅做参考):

《Android C++高级编程使用NDK》
《Android高级开发实战 UI、NDK与安全》

二、 本地环境:
Android studio:3.5
NDK:r20(由Android studio的SDK tool下载)
JDK:1.8

三、 JNI编译步骤:

  1. 在java类中声明本地接口:
    /* JNI返回一个hello的字符串 */
    public native String helloJNI();
  1. 借助JDK提供的javah,编译出实现本地接口的头文件。jni目录通常与java目录同级,使用AS在app/src/main下面创建jni目录,然后进入该目录,输入如下指令:
javah -d ../jni -jni com.example.jnitest.JNITest

javah是jdk提供的工具,-d是指定输出目录,生成的头文件将输出到与java目录同级的jni目录中,-jni是生成JNI样式的头文件,后面的全包名 + 类名即生成的JNI头文件前缀,执行此命令之后,生成的头文件为:

com_example_jnitest_JNITest.h

在这里插入图片描述
3. 在jni目录下创建对用的c/c++文件和Android.mk,Android.mk编写如下:

LOCAL_PATH := $(call my-dir)    #接收当前Android.mk文件的绝对路径

include $(CLEAR_VARS)           #类似于工具初始化的操作

LOCAL_MODULE := libhellojni     #模块名
LOCAL_SRC_FILES := hello.cpp    #依赖的源文件

LOCAL_LDLIBS    := -llog        #引用log对应的库文件

include $(BUILD_SHARED_LIBRARY) #将该模块编译成一个动态库
  1. 在c/c++文件中编写一个返回字符串的函数,在java层接收并显示:
JNIEXPORT jstring JNICALL Java_com_example_jnitest_JNITest_helloJNI(JNIEnv *env, jobject jobj) {
    LOGE("hello JNI!");
    return env->NewStringUTF("hello JNI!");
}
  1. 编译so库,编译动态库有两种方式,一种是使用Cmake,还有一种是使用NDK,这里记录使用NDK编译so库:
    在Android.mk对应的目录使用cmd进行ndk-build编译:
ndk-build

编译成功如图:
在这里插入图片描述
可以看到,在jni同级目录下,生成了libs和obj文件夹,libs是生成多个架构下的so库,obj是生成的中间文件,因为我的测试机是arm64架构的,所以只需要将armeabi-v7a文件夹复制到app目录下的libs里面即可;
6. 在app的build.gradle中添加如下代码:

    sourceSets {
        main {
            jni.srcDirs = ['src/main/jni', 'src/main/jni/']
            jniLibs.srcDirs = ['libs']
        }
    }

这里的jniLibs.srcDirs可以理解为一个映射,表示JNI的库文件在APP下面的libs中去找,当然也可以自己制定路径,只要修改这个值就可以了。
注意:一定要将代表架构名的文件夹复制到app/libs下面,如果仅仅是复制库,那么apk运行起来是加载不到的,会闪退,这个原因我也不太清楚,看网上说之前的版本是可以直接将so复制到这个文件夹下面就能用的;
完整的build.gradle文件如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.1"
    defaultConfig {
        applicationId "com.example.jnitest"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            jni.srcDirs = ['src/main/jni', 'src/main/jni/']
            jniLibs.srcDirs = ['libs']
        }
    }
    externalNativeBuild {
        ndkBuild {
            path file('src/main/jni/Android.mk')
        }
    }


}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
  1. 在含本地方法的类中加载库:
    static {
        System.loadLibrary("hellojni");
    }

加载库去掉前缀lib和后缀.so;

四、演示:
在java文件中调用此方法,可通过打印或者toast来显示出来。

代码路径:

https://github.com/jiyi666/apk-demo/tree/master/JNITest
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值