美文网首页
Android studio实现NDK打包so文件

Android studio实现NDK打包so文件

作者: WhenMeet | 来源:发表于2020-03-26 17:49 被阅读0次

  在我们的c++代码写好后,我们往往会需要打包成so文件给别人使用,那就要求我们知道如何把c++代码打包成so文件,这里介绍NDK打包的方式,想知道CMake的打包方式请前往我的下一篇。
我这里分五步走,咱一步步往下看。

  • 1.创建android工程
  • 2.关联NDK
  • 3.声明native方法,并创建头文件和C++文件
  • 4.创建Android.mk文件和Application.mk文件
  • 5.打包so文件
  • 1.创建android工程
选择普通工程
项目命名

  不出意外,项目一会就建立完成。

  • 2.关联NDK
打开项目配置
选择NDK安装位置

  选择好了之后,点击OK即可。
  这里说一下,然后如果没有配置NDK环境变量的,需要给系统配置一下环境变量,我的是mac直接运行命令行:

vim ./.bash_profile

打开后,添加

export NDK_HOME=填写你的NDK安装路径
export PATH=$PATH:$NDK_HOME
  • 3.声明native方法,并创建头文件和C++文件

  首先声明一个方法,如下:

public class JNIUtils {

    /**
     * java调C中的方法都需要用native声明且方法名必须和c的方法名一样
     * **/
    public native static String stringFromJNI();

}

  这里我们声明一个native方法,这就是我们待会在C++中会产生的方法。
  我们打开terminal准备运行命令行,可以先输入pwd,可以查看当前命令行所在文件夹的位置,然后进入java文件夹下,具体命令如下:


命令行进入java下

  然后执行命令行:

javac com/simple/ndktest/JNIUtils.java

  注意这里的com/simple/ndktest是对应我的包名com.simple.ndktest,如果你们和我的不一样,需要替换,然后点击enter运行,会产生一个JNIUtils.class,可能不会刷新出来,我们点一下我们的包名文件夹就可以了,然后我们再执行命令行:

javah com.simple.ndktest.JNIUtils

  然后我们就利用刚刚生成的class文件生成我们需要的C++头文件,名称为:com_simple_ndktest_JNIUtils.h,刚才的JNIUtils.class文件就可以删除了
内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_simple_ndktest_JNIUtils */

#ifndef _Included_com_simple_ndktest_JNIUtils
#define _Included_com_simple_ndktest_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_simple_ndktest_JNIUtils
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_simple_ndktest_JNIUtils_stringFromJNI
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

  有了这个头文件我们就可以去建立我们的C++文件了,我们新建一个文件为:com_simple_ndktest_JNIUtils.cpp,操作如下:


选择建立C++文件
给文件命名

  这样就建立成功了,但是里面是空空如也的,我们需要导入头文件,并且复制头文件中的方法,最终效果如下:

#include "com_simple_ndktest_JNIUtils.h"

JNIEXPORT jstring JNICALL Java_com_simple_ndktest_JNIUtils_stringFromJNI
  (JNIEnv *env, jclass){
  return env->NewStringUTF("Hello from C++");
  }

  我这里返回了一个字符串"Hello from C++"
  到此,我们的native方法,C++及其头文件就建立好了,开始下一步。

  • 4.创建Android.mk文件和Application.mk文件

  Android.mk文件是一个负责向NDK构建系统描述NDK项目的GNU Makefile片段,是每一个NDK项目的必备组件,如下。
Android.mk

# Android.mk必须以LOCAL_PATH开头,注释#除外
# 设置工作目录,而my-dir则会返回Android.mk文件所在的目录
LOCAL_PATH := $(call my-dir)

# 借助CLEAR_VARS变量清除除LOCAL_PATH外的所有LOCAL_<name>变量
include $(CLEAR_VARS)

# 设置模块的名称,即编译出来.so文件名
# 注,要和上述步骤中build.gradle中NDK节点设置的名字相同
LOCAL_MODULE := native-lib

# 指定参与模块编译的C/C++源文件列表,多文件用"\"隔开
LOCAL_SRC_FILES := com_simple_ndktest_JNIUtils.cpp


# 必须在文件结尾定义编译类型,指定生成的静态库或者共享库在运行时依赖的共享库模块列表。
# BUILD_SHARED_LIBRARY 共享库,供java或者其他共享库调用
# BUILD_STATIC_LIBRARY 静态库,供共享库调用,不能直接被java调用
include $(BUILD_SHARED_LIBRARY)

需要复制的同学只需要更改com_simple_ndktest_JNIUtils.cpp即可,其他不需要更改。
Application.mk

# 最常用的APP_ABI字段:指定需要基于哪些CPU平台的.so文件
# 常见的平台有armeabi x86 mips,其中移动设备主要是armeabi平台
# 默认情况下,Android平台会生成所有平台的.so文件,即同APP_ABI := armeabi x86 mips
# 指定CPU平台类型后,就只会生成该平台的.so文件,即上述语句只会生成armeabi平台的.so文件
# APP_ABI := armeabi armeabi-v7a mips x86
APP_ABI := all
APP_PLATFORM := android-23

  好了,俩个文件创建完毕。
  细心的同学可能注意到了Android.mk中有一句:

LOCAL_MODULE := native-lib

  这个其实就是我们最终要生成的so文件的名称,那么我们一开始的JNIUtils如果想使用native方法,就必须要加载这个so库,更改后如下:

public class JNIUtils {

    /**
     * 加载生成的so库文件
     * **/
    static {
        System.loadLibrary("native-lib");
    }

    /**
     * java调C中的方法都需要用native声明且方法名必须和c的方法名一样
     * **/
    public native static String stringFromJNI();

}

  需要加载的so文件和我们即将生成的so文件名称需要保持一致。
然后我们需要在app下的build.gradle中也声明一下,效果如下:

defaultConfig {
...
        ndk{
            //模块名称,即编译的.so文件名
            moduleName "native-lib"

            //"log"表示加入Android的调试日志,只要再导入#include <android/log.h>
            //就可以使用__android_log_print方法打印日志到logcat中
            ldLibs "log"
        }
    }

  至此我们共生成了五个文件,除了JNIUtils,其余四个我们在他们放在一起,我这里是,在app下建了一个jni的包,然后全部放在里面,效果如下:


类放置位置
  • 5.打包so文件

  仍然进入命令行,这里说一下,cd xxx 是进入到xxx文件夹下,cd ./ 是退出所在文件夹,pwd是展示当前所在位置,ls是展示当前文件下所有文件名称,通过调整,我们进入到我们新建的jni文件夹下,执行:

ndk-build

  执行后效果如下:


生成so文件过程

  如不出意外,我们的so文件就可以生成了,在app的lib下,分为多个cpu架构,如图:


so文件示意图
  然后我们在我们的MainActivity中使用,如下:
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("JNIUtils", JNIUtils.stringFromJNI());
    }
}

  等等!我们的gradle还需要配置一下,在app的build.gradle的android下直接添加

    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

  这样其实就是指定我们的so文件路径,我们才可以正常使用,有的朋友可能问了,为什么有时候需要配置,有时候不需要呢,这是因为我们把so文件放在了lib下,这个调用so文件的用法来自于Eclipse,所以android studio保留了,但是android studio原本也有调用so文件的方式,路径是main文件下jniLibs,如果没有,可以新建,所以如果我们把我们的so文件放在我们main文件下jniLibs中,上面的这一配置就可以不用写了,因为默认就是调用这个地方。
  最后我们运行app,可以发现界面上会显示文字:

  到此,ndk打包so文件的方式介绍完毕。

相关文章

网友评论

      本文标题:Android studio实现NDK打包so文件

      本文链接:https://www.haomeiwen.com/subject/kubcuhtx.html