当前位置 主页 > 服务器问题 > Linux/apache问题 >

    Golang开发动态库的实现

    栏目:Linux/apache问题 时间:2019-12-01 10:20

    我们平时使用的动态库都是由C/C++开发最后生成的.so文件。

    可以先看看一个JNI的开发过程。

    一. 开发JNI

    有两种方式,现在一种比较快的方式是AndroidStudio你在创建项目选择Module的时候它会给你个JNI的模板,直接使用那个就行。

    但是我还是比较喜欢传统的方法。

    简单来说传统的方式就是你用命令来把java文件变成C++的头文件

    简单演示一遍,先写个java类

    public class TestJni {
    
      static {
        System.loadLibrary("KylimTest");
      }
    
      public static native String getMsg();
    
    }

    定义了一个native修饰的方法,在代码调用这个方法之后JNI就会自动调用到动态库中相应的方法。

    将这个类用命令生成头文件,来到文件夹路径下输入命令

    javah -jni 包名.类名

    可以看到默认会生成一个.h的头文件,自动命名为 包名_类名.h

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_kylim_nativetest_TestJni */
    
    #ifndef _Included_com_kylim_nativetest_TestJni
    #define _Included_com_kylim_nativetest_TestJni
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:   com_kylim_nativetest_TestJni
     * Method:  getMsg
     * Signature: (I)Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_kylim_nativetest_TestJni_getMsg
     (JNIEnv *, jclass);
    
    #ifdef __cplusplus
    }
    #endif
    #endif

    主要的核心就是这句

    JNIEXPORT jstring JNICALL Java_com_kylim_nativetest_TestJni_getMsg
     (JNIEnv *, jclass);

    其它的我也不清楚,都是C相关的, 如果你嫌用命令生成麻烦,你可以自己创建一个.h文件然后方法命名就按照这样的规范去写

    头文件只是为了定义,我们需要自己写原文件,所以要创建一个.cpp结尾的文件

    #include "com_kylim_nativetest_TestJni.h"
    
    JNIEXPORT jstring JNICALL Java_com_kylim_nativetest_TestJni_getMsg
        (JNIEnv *env, jclass cls){
    
      jstring result = env->NewStringUTF("结果是");
      return result;
    }

    方法命名是有规范的,看Demo也知道怎么规范了,没必要多解释,这样两端的代码就写完了,但是仅仅这样是无法运行项目的。

    还需要些一些配置,因为在AndroidStudio中是Gradle去帮我们编译C++的代码,所以需要写这些配置。如果你不是用AS开发,你用其它工具开发直接生成.so文件再丢进AS中的话,可以忽略这一步。

    先看看我的Jni目录

    要创建一个Android.mk

    #固定写法
    LOCAL_PATH:=$(call my-dir)
    #固定写法
    include $(CLEAR_VARS)
    #生成so名称
    LOCAL_MODULE := KylimTest
    LOCAL_SRC_FILES := testone.cpp
    #固定写法
    include $(BUILD_SHARED_LIBRARY)

    具体的配置可自行去查找,这里不是主要讲JNI的,所以就不讲这么细。

    还需要一个Application.mk

    # 选择不同的 ABI,多个使用空格作为分隔符,全部是all
    # APP_ABI := armeabi armeabi-v7a
    APP_ABI := armeabi-v7a
    
    # 指定要使用的运行时
    APP_STL := c++_static

    当然这样还不行,都说了是Gradle进行编译,那么肯定还要在Gradle中写一些配置

    android {
       defaultConfig {
            ndkBuild {
            //指定 Application.mk 的路径
            arguments "NDK_APPLICATION_MK:=src/main/jni/Application.mk"
            //指定生成哪些平台的 so 文件
            abiFilters "armeabi-v7a"
            //cFlags 和 cppFlags 是用来设置环境变量的, 一般不需要动
            cFlags "-DTEST_C_FLAG1", "-DTEST_C_FLAG2"
            cppFlags "-DTEST_CPP_FLAG2", "-DTEST_CPP_FLAG2"
          }
       }
    
      sourceSets { main { jni.srcDirs = ['src/main/jni'] } }
    
      externalNativeBuild {
        ndkBuild {
          path file('src/main/jni/Android.mk')
        }
      }
    
    }