1. 程式人生 > 程式設計 >Golang開發動態庫的實現

Golang開發動態庫的實現

我們平時使用的動態庫都是由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')
    }
  }

}

這樣就能簡單的跑一個JNI的Demo,總的來說就是Java這邊寫一個類定義一些native方法和載入,C++這邊寫具體的方法實現。

二.用Go開發動態庫

上面說的原生方法是用C/C++進行開發的,那麼如果你不會C++的話怎麼辦,C++的學習也並非這麼容易,就拿兩邊的型別來說,一開始新手肯定會碰到型別轉換的問題,往往會先勸退一些人,但是Go不一樣,有Java基礎的話學起go還是挺快的。

那麼用Go開發的動態庫是怎樣的?也是SO檔案嗎,是不是也像C++一樣,編譯後經過某步操作生成SO檔案。

我看到網上有些文章確實是寫怎麼生成so的,但是說得太少,感覺不可靠,直到我看到官方有寫。

可以在官方中看到是有一個mobile的庫的

https://github.com/golang/mobile

讀下去它會指引你去wiki

https://github.com/golang/go/wiki/Mobile

可以看出它會打出一個aar的檔案,那麼aar對於我們接入來說確實很方法,但我很想探究這個aar裡面究竟是什麼,所以我們需要打出一個aar然後解壓看看它裡面到底是什麼

這裡先說一下,下載這個庫之前,你本地肯定先要配置好Go的環境

然後按照這裡的流程就行下載

go get -d golang.org/x/mobile/example/basic

但這輸入這條命令需要科學上網的方式才能下載,總的來說很麻煩。

所以我們可以直接克隆mobile的庫,就是上面的這個連結 https://github.com/golang/mobile

直接下載下來,除此之外,還需要tools,這些都在Go中,連結 https://github.com/golang/tools

將這兩個下載下來,然後拷貝到你的Go的以下路徑

go/src 建立一個資料夾golang.org/x ,把這兩個資料夾丟進去

然後輸入命令

gomobile init

可以輸入命令 檢視安裝配置是否成功

gomobile bind -help

如果配置成功會給你一些提示

我感覺文件寫得還是不算清楚,但是它有告訴你用什麼命令生成aar

gomobile bind -o app/hello.aar -target=android golang.org/x/mobile/example/bind/hello

你在GoPath中建立一個Go檔案,我是用GoLand進行開發的,專案的目錄設定成GoPath,編寫完之後,可以直接在檔案中執行

gomobile bind -o 輸入檔名.aar -target=android

這樣就能在資料夾中生成一個aar

接著我們看看aar裡面是什麼,解壓

首先可以看到生成這些ABI的so檔案,再看看Manifest

這裡有限制最低版本,所以如果你的版本比他還低的話就需要注意一下了

然後res裡面是可以看到沒有檔案的。

最後我們反編譯class檔案

因為這不是Demo,直接是寫公司的專案,所以有些地方要碼,但是不影響。

從這裡看得出,go幫我們生成了一個java檔案,這個java檔案定義了再Go中命名的原生方法。

其實從這裡就可以看出,Go用的也基本是我們最上面寫的JNI的方法,只是他幫你封裝起來了而已

但是他的原生程式碼是不是轉成C++的我就不清楚了,因為我不會反編譯SO檔案。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。