Android 使用ndk-build指令编译so库小述

一、前言

在项目中添加第三方依赖的时候,经常会使用到它的so文件,可一直没有去想过so文件是怎么编译出来的,编译出来的so文件又是怎么供别的项目使用的,今天趁着闲余时间,准备查查资料好好研究一下这块。因为我现在使用的是android studio2.3.3,ndk-r16b,所以在编写过程中,可能会存在差异,不过应该问题不大。

二、环境搭建

在正式开始之前,如果你的电脑还没有配置NDK环境,那么请先去下载NDK吧,或者你也可以使用AS在Setting=>System Settings=>Android SDK=>SDK Tools中勾选NDK下载,下载之后解压文件,例如我将解压后的文件放在了D盘的ndk目录之下

        D:\ndk\android-ndk-r16b

随后配置一下环境变量,我的电脑=>属性=>高级系统设置=>环境变量,在PATH中添加ndk路径

        D:\ndk\android-ndk-r16b;

在我的File=>Project Structure=>SDK Location=>Android NDK Location,填上我们刚才的ndk路径,之后点击ok

        D:\ndk\android-ndk-r16b

在项目中的local.properties文件中添加

        ndk.dir=D\:\\ndk\\android-ndk-r16b

在gradle.properties该文件中添加过时ndk版本支持

        Android.useDeprecatedNdk=true

三、编译so库并加载到项目中

这是我们编写的java类,并添加了native方法

    public class MyJniUtils {

        //获取一个最简单的helloWord字符串
        public static native String getHelloWorld();
    }

我们 Build=>Make Project,并找到MyJniUtils编译出来的class文件,本地所在目录如下:

Android 使用ndk-build指令编译so库小述_第1张图片

我们View=>Tool Windows=>Terminal打开终端,并cd到上图目录结构中debug目录之下:

        E:\MyJniTest>cd app/build/intermediates/classes/debug

使用javah命令生成MyJniUtils.class的JNI的头文件

        javah zmj.myjnitest.MyJniUtils

上面两步的图例如下

Android 使用ndk-build指令编译so库小述_第2张图片

你会发现在debug目录下生成了zmj_myjnitest_MyJniUtils.h头文件,我们将该头文件复制到当前项目下的jni目录,如果没有则新建,jni所在目录如下

Android 使用ndk-build指令编译so库小述_第3张图片

在jni目录下编写一个.c文件实现该JNI头文件中的函数,c文件名字自己随意定义,本例中为MyJniTest.c,也可以使用cpp的实现文件,但两者在代码编写上有差异

    #include <zmj_myjnitest_MyJniUtils.h>
    JNIEXPORT jstring JNICALL Java_zmj_myjnitest_MyJniUtils_getHelloWorld     (JNIEnv *env, jobject jObj){
        return (*env)->NewStringUTF(env, "Hello World");
    }

编写Android.mk文件

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)

    LOCAL_MODULE    := myJniTest
    LOCAL_SRC_FILES := MyJniTest.c

    include $(BUILD_SHARED_LIBRARY)

编写Application.mk文件

    APP_STL := gnustl_static
    APP_CPPFLAGS := -frtti -fexceptions
    APP_ABI := all
    APP_PLATFORM := android-14

jni目录结构图如下

Android 使用ndk-build指令编译so库小述_第4张图片

利用ndk-build生成动态链接库,将终端cd 到jni目录之下,执行ndk-build指令,编译出该动态so库

Android 使用ndk-build指令编译so库小述_第5张图片

编译完成之后,你会发现,libs目录下已经自动生成了.so文件,并在main目录下自动生成了obj文件夹

Android 使用ndk-build指令编译so库小述_第6张图片

在build.gradle中,android{ }内添加

    sourceSets {
            main() {
                jniLibs.srcDirs = ['src/main/libs']
                jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程
            }
        }

在我们的HomePageActivity类中加载刚刚编译好的so库

    public class HomePageActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_home_page);
            Log.i("Tag",MyJniUtils.getHelloWorld());
        }

        static {
            System.loadLibrary("myJniTest");
        }
    }

到这里我们就已经介绍完了生成so文件的过程,也知道了怎么去实现java层调用Native代码,运行下我们的项目,结果如下

这里写图片描述

四、其他项目中使用该so库

新建一个新项目,将我们so文件放置到该项目的相应libs目录下,其实最好放在项目main目录下的jniLibs文件夹之下,这两者在build.gradle的内容稍有不同,前者需要指定jniLibs.srcDirs = [‘libs’]

Android 使用ndk-build指令编译so库小述_第7张图片

在java目录下新建zmj.myjnitest包(zmj.myjnitest为我们编译so文件所在的项目包名),之后将我们的MyJniUtils类复制到zmj.myjnitest目录下(记得要跟so文件所在项目内的MyJniUtils类路径一致),目录结果图如下

Android 使用ndk-build指令编译so库小述_第8张图片

在我们需要的地方调用

    public class MyJniTestActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_my_jni_test);
            Log.i("Tag", MyJniUtils.getHelloWorld());
        }

        static {
            System.loadLibrary("myJniTest");
        }
    }

在build.gradle的android{ }内添加

    sourceSets {
            main() {
                jni.srcDirs = []
                jniLibs.srcDirs = ['libs']//如果将libs文件下的一系列arm文件夹,放在项目main目录下的jniLibs文件夹里,则不需要
            }
        }

最后运行一下我们的程序,结果如下

这里写图片描述

五、总结

到这里我们就已经介绍完了最简单的JNI的使用,其实JNI的调用方式主要有两种,一种是Java代码调用Native的代码,这也是最常见的,另一种则恰好相反,就是在Native层调用Java代码,这种方式如果以后有机会会另行介绍。还有在本文中没有对.c和.mk文件中的每一行进行含义说明,也没有介绍JNI和NDK的相关概念,我会在后面博客中详细解释,本文最重要的是把该项目运行起来。

demo下载

你可能感兴趣的:(android随笔)