golang跨平台, 性能强, 周边也算丰富

一直有一个想法, 把这东西写的代码编译成各个平台的库, 未来在 flutter 里用

开发环境和工具

  • MacOS(其他的暂时不考虑)
  • Go
    • Go 1.14.6, 版本太低可能不能编译成 android 的
    • Goland , 咱现在是正版用户, 用开源项目申请的All products License , 你也可以根据自己的情况选择别的 IDE 或使用文本编辑器(vscode 也不错)
  • Android
    • Android Studio
    • Cmake
    • Android SDK
    • Android NDK

Go 部分

go 源码

add_library.go

package mainimport "C"//export addfunc add(x, y int) int {     return x + y}//export remove_intfunc remove_int(x, y int) int {     return x - y}func main() {     }

这里有几点要注意

  1. package 一定要是 main(强制规定)
  2. 一定要包含 main 函数(强制规定)
  3. import “C”, 不能少, 因为要编译出 c(c++)的头文件
  4. 每个方法前要加//export 方法名, 这里要注意
    1. //export间不能有空格
    2. 方法名和 go 的方法名必须完全一样
    3. 方法名不能是 c 内置的方法名, 比如remove就不行

编译

我在项目里内置了两个脚本, 一个是编译 android 的, 一个是编译 macOS 的, 因为篇幅和主题的原因, macOS 的就不单独拿出来了

看看编译成安卓的脚本吧

export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/21.0.6113669export GOARCH=armexport GOOS=androidexport CGO_ENABLED=1export CC=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi21-clanggo build -buildmode=c-shared -o output/android/armeabi-v7a/libadd.so add_library.goecho "Build armeabi-v7a success"export GOARCH=arm64export GOOS=androidexport CGO_ENABLED=1export CC=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clanggo build -buildmode=c-shared -o output/android/arm64-v8a/libadd.so add_library.goecho "Build arm64-v8a success"

简单来说 有几个环境变量要设置一下

  1. GOARCH=CPU 类型
  2. GOOS=设备类型
  3. CGO_ENABLED=1
  4. CC=ndk 里的 clang,需要是对应 CPU 版本的, API 呢建议是最低
    1. 这里单独说明一下, 高版本的 ndk 用的是 clang, 低版本用的是 gcc, 这个根据你情况来, 个人建议直接用 21.0.6113669版本, 这个我测试过, 没有问题, ndk20 我也试过, 也是 ok 的
  5. go build 命令
    1. -buildmode=c-shared 构建类型, 使用 go help buildmode查看, 安卓一般用c-shared就可以了, 意思就是 c 类型的共享库(动态库)
    2. -o 后面跟输出的位置, 一般建议使用 libxxx.so 的格式, 相对的, 头文件也会被生成在同一目录下, xxx 就是库的名字, 对应到 Java 里加载库的方法就是System.loadLibrary("xxx") 而不是~~System.loadLibrary("libxxx")~~
    3. 最后一个就是需要编译的 go 文件了

CPU 类型和设备类型可以使用go tool dist list查看

我这里 1.14.6 包含的 android 对应的是:

$ go tool dist list|grep androidandroid/386android/amd64android/armandroid/arm64

arm 和 arm64 对应 v7 和 v8

386 amd64 应该对应的是 x86 和 x86_64, 这个没有实测, 如果有需要的朋友可以自己尝试一下

运行脚本./build_android_on_mac.sh即可完成编译

安卓

创建项目

就是简单的在 Android Studio 中 New Project

CMakeLists.txt

自己创建一个CMakeLists.txt, 注意大小写敏感, 其实这个文件名可以随便写, 但是约定俗成是这样, 建议不要变

cmake_minimum_required(VERSION 3.10.2)project(android_lib)set(GO_BUILD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../go/output/android)set(SRC ${GO_BUILD_PATH}/${CMAKE_ANDROID_ARCH_ABI})include_directories(${SRC})find_library(        std-lib        android)message("CPU ABI: ${CMAKE_ANDROID_ARCH_ABI}")if (${CMAKE_ANDROID_ARCH_ABI} EQUAL "armeabi") ## 根据ABI的不同, 复制so文件到对应的文件夹    file(COPY ${GO_BUILD_PATH}/${CMAKE_ANDROID_ARCH_ABI}/libadd.so DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}) # 复制库文件else ()    file(COPY ${SRC}/libadd.so DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}) # 复制库文件endif ()file(COPY ${SRC}/libadd.h DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni) # 复制头文件file(GLOB JNI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni/*.h) # 扫描jni目录下的头文件和库文件, 并设置给JNI_SRCadd_library(my_jni SHARED ${JNI_SRC}) # 添加jni中转库file(GLOB OUTPUT_LIBRARY libs/${CMAKE_ANDROID_ARCH_ABI}/*.so) # 找到对应cpu的so库文件target_link_libraries(        my_jni        ${std-lib} # 连接android标准库给jni, 因为golang用到了一些标准库的定义        ${OUTPUT_LIBRARY} # 连接给jni库)

我在其中加入了详细的注释, 其中具体的 Cmake 语法没法展开讲

编写代码

C 代码

首先是 c 代码, 我们在 CMake 中定义了一个 my_jni的库, 这个库包含${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni下所有的 c 和 h 文件

然后吧, 在src/main/jni下创建 c 文件, 我这里定义的是 jni.c,别的什么也没关系

#include #include "libadd.h"//// Created by jinglong cai on 2020/8/14.//JNIEXPORT jint JNICALLJava_top_kikt_usegolibrary_JniLibrary_add(JNIEnv *env, jclass clazz, jint x, jint y) {         return add(x, y);//    return x + y;}JNIEXPORT jint JNICALLJava_top_kikt_usegolibrary_JniLibrary_remove(JNIEnv *env, jclass clazz, jint x, jint y) {         return remove_int(x, y);}

嗯, 其实就两个方法, 对应了 go 里定义的两个方法, 这里是 jni 的写法

Java 代码

JniLibrary.class

package top.kikt.usegolibrary;public class JniLibrary {         static {             System.loadLibrary("my_jni");    }    public static native int add(int x, int y);    public static native int remove(int x, int y);}

这里的两个 native 方法就是指向jni.h里定义的方法的

MainActivity.kt

package top.kikt.usegolibraryimport androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport kotlinx.android.synthetic.main.activity_main.*class MainActivity : AppCompatActivity() {         override fun onCreate(savedInstanceState: Bundle?) {             super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        textView.text = "come from jni ${       JniLibrary.add(300, 150)}"        textView2.text = "come from jni ${       JniLibrary.remove(300, 150)}"    }}

就是简单的调用了这两个方法

运行

结果如下

后记

项目代码: https://github.com/caijinglong/BuildGoForAndroid

本篇为系列的第一篇, 不出意外, 后续应该会有对应的 iOS 篇, flutter-dart:ffi , wasm 篇则待定

以上

更多相关文章

  1. android应用基础--由官方帮助文件翻译
  2. android不支持tab补全和ctrl+c的解决办法
  3. [置顶] 关于Android(安卓)NDK如何成功调用stl的使用分析
  4. Android添加自定义公共so库
  5. Android文件操作总结
  6. android 获取文件大小
  7. Android(安卓)瘦身之道 ---- so文件
  8. AndroidManifest.xml文件讲解
  9. Android之更换皮肤

随机推荐

  1. 解读:什么是Java的递归算法?
  2. 双Hadoop集群&双Kerberos kdc认证跨域互
  3. 数据项目生命周期的7个步骤——在业务中
  4. Linux学习--第14周
  5. 圣怀布局,网格(容器,项目,单元,轨道,间距
  6. DSaaS,一个创新的云密码服务
  7. Node实战篇:Nodejs 链接 Mariadb 实例
  8. Veeam v11 重量级功能 不可变存储库(Linux
  9. Z投稿 | Zabbix如何通过ODBC对接Oracle获
  10. 苹果Mac笔记本电脑如何开启热点分享网络?