[劍走偏鋒] Android使用Golang代替C/C++進行Native開發(fā)

起因

事情是這樣的: 一次Android項目開發(fā)過程中,要用到數(shù)據(jù)的加密解密,因為數(shù)據(jù)運算量比較大,所以需要用到native進行開發(fā),但是又極不情愿去寫C/C++那種既耽誤時間又不好調試的語言,所以想方設法的尋找替代方案,正好最近在用Golang,尋思著,Golang不是號稱速度接近C++又能快速開發(fā)的嗎,所以琢磨著能不能用Golang來寫Android的native部分,然后就有了一系列的踩坑過程 (這坑我踩了,剩下的你看著辦吧)

配料表

既然是用Golang開發(fā),當然就需要用到Golang的開發(fā)環(huán)境,至于怎么搭建,你自己去找吧 (爛大街的玩意)
大致列一下需要用到的環(huán)境和SDK:

編譯準備

開搞
首先是寫一份Golang的源碼,打個比方說 hello.go

package main

import "C"

//export SayHello
func SayHello(name *C.char) *C.char{
    return  C.CString("Hello : " + name)
}

//export Sum
func Sum(a int, n int) int {
    return a + n
}

func main(){
    // 這個主方法一定要寫,不然不給編譯
}

是不是覺得一臉懵逼,那么有必要解釋一下

  • import "C" 這個是要告訴CGO我需要調用C的方法,使用C語言的東西
  • //export SayHello 這個注釋是必須要有的,就相當于C語言中的 extern 值得注意的是雙斜杠后面不能有空格,別問我為什么知道,所以 //export SayHello 就相當于C語言的 extern char* SayHello(char*)
  • 為什么這里用 *C.char 而不用 string 呢? 因為,如果使用Golang中的 string 來定義的話,在Java中就不能直接以String的類型來傳遞,而會被Golang定義為一種名為 GoString 的神奇類型,就像這樣
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
typedef _GoString_ GoString;

extern GoString SayHello(GoString);

使用string的好處就是,他會直接導致你的代碼量增加,因為你還要在Java中寫一個和GoString差不多的包含 String (字符串)int (字符串長度) 的類,而C語言中的 char* 直接對應的就是Java中的 String ,所以這里 建議使用 *C.char 來代替 Golang中的 string

  • 最后就是 main 方法,這個是必須要有的,不使用的話置空就好,沒有的話會導致編譯 .so 失敗,至于為什么,有待深入研究

參數(shù)配置

然后就開始編譯,在編譯之前,需要設置一下 go env 的參數(shù),從而達到我們想要的東西

Windows
set GOOS=android
## GOARCH可選平臺,需要和 CC CXX 對應
## arm (armeabi-v7a) CC=armv7a-linux-androideabi19-clang.cmd CXX=armv7a-linux-androideabi19-clang++.cmd
## arm64 (arm64-v8a) CC=aarch64-linux-android19-clang.cmd CXX=aarch64-linux-android19-clang++.cmd
## 386 (x86) CC=i686-linux-android19-clang.cmd CXX=i686-linux-android19-clang++.cmd
## amd64 (x86_64) CC=x86_64-linux-android21-clang.cmd CXX=x86_64-linux-android21-clang++.cmd
set GOARCH=arm
## 這個一定要,不然你編譯出來的so各種未定義
set CGO_ENABLED=1
## 設置NDK的編譯器路徑,需要和 GOARCH 對應
set CC=${你的NDK目錄}\toolchains\llvm\prebuilt\windows-x86_64\bin\armv7a-linux-androideabi19-clang.cmd
set CXX=${你的NDK目錄}\toolchains\llvm\prebuilt\windows-x86_64\bin\armv7a-linux-androideabi19-clang++.cmd
Linux
export GOOS=android
export GOARCH=arm
export CGO_ENABLED=1
export CC=${你的NDK目錄}\toolchains\llvm\prebuilt\linux-x86_64\bin\armv7a-linux-androideabi19-clang
export CXX=${你的NDK目錄}\toolchains\llvm\prebuilt\linux-x86_64\bin\armv7a-linux-androideabi19-clang++
mac
我窮逼用不起蘋果,你們自己百度

之后就是開始編譯 .so 文件

go build -buildmode=c-shared -o libhello.so hello.go

要想優(yōu)化一下編譯后的產物大小可以這樣

go build -ldflags "-s -w" -buildmode=c-shared -o libhello.so hello.go

-s 參數(shù)是去掉編譯后的符號信息. -w 參數(shù)是去掉DWARF調試信息,不過需要注意的是,-w參數(shù)得到的產物無法進行調試,當然可以在發(fā)布的時候使用-w參數(shù)編譯.
這里編譯完就可以拿到 armeabi-v7aso 文件了,如果需要其他架構的,請修改 GOARCH= arm/arm64/386/amd64 中的任意一個,并修改 CC CXX 為對應架構的編譯器,然后重新編譯得到對應架構的 so 文件

食用方法

將下載的 JNA 解壓,復制 dist 目錄下的 jna-platform.jarjna-min.jarlibs目錄下,并將 android-armv7.jar android-aarch64.jar android-x86.jar android-x86-64.jar 解壓,得到里邊的 libjnidispatch.so 放到 jniLibs 的對應目錄下,將編譯Golang源碼得到的 so 復制到Android項目的 jniLibs 目錄的對應目錄下,如圖:
(假裝有圖)

Project
└─ app
   ├─ libs
   │  ├─ jna-min.jar
   │  └─ jna-platform.jar
   └─ src
      └─ main
         ├─ Androidmanifest.xml
         ├─ java
         ├─ res
         └─ jniLibs
            ├─ armeabi-v7a
            │  ├─ libjnidispatch.so
            │  └─ libhello.so
            ├─ arm64-v8a
            │  ├─ libjnidispatch.so
            │  └─ libhello.so
            ├─ x86
            │  ├─ libjnidispatch.so
            │  └─ libhello.so
            └─ x86_64
               ├─ libjnidispatch.so
               └─ libhello.so

在項目中新建一個接口,名稱隨意,繼承 com.sun.jna.Library

package com.demo.golang;

import com.sun.jna.Library;
import com.sun.jna.Native;

public interface Hello extends Library {

    // 加載libhello.so
    Hello ins = Native.load("hello", Hello.class);

    /**
     * 對應 Golang 中的 SayHello 方法
     */
    String SayHello(String name);

    /**
     * 對應 Golang 中的 Sum 方法
     */
    int Sum(int a, int n);
}

然后在你想要調用的位置調用 Hello.ins.SayHello("Golang for Android with JNA") 比如我在 MainActivity 里邊調用

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((TextView) findViewById(R.id.text_view))
            .setText(Hello.ins.SayHello("Golang for Android with JNA ") + Hello.ins.Sum(500, 20));
    }
}

跑起來看看,是不是非常完美?是不是感覺新技能 get 有木有覺得比 C/C++ 簡單得多?

總結

總體上來說,相對直接使用C/C++要簡單方便,但是,也有一定的缺陷,暫時我還沒研究出從 Golang 調用 Java 代碼的方法, 所以簡單來說就是只能通過 Java 調用Golang
簡單的總結一下相對 C/C++ JNI 來寫的一些缺點

  • 暫時沒法從 Golang 調用 Java (當然這個我感覺應該不難)
  • Java 調用 Golang 只能傳基本數(shù)據(jù)類型,沒辦法傳遞對象 (這個不知道定義一個結構體能不能實現(xiàn))
  • Golang 調用 C/C++ 的鏈接庫不是很方便

暫時就這么多,后邊會花時間研究一下怎么簡化流程和調用更高級的API,以達到使用純 Golang 開發(fā) Android 的目的

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌依疼,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倍谜,死亡現(xiàn)場離奇詭異小压,居然都是意外死亡玉工,警方通過查閱死者的電腦和手機丁逝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門汁胆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人霜幼,你說我怎么就攤上這事嫩码。” “怎么了罪既?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵铸题,是天一觀的道長。 經(jīng)常有香客問我萝衩,道長称开,這世上最難降的妖魔是什么脸秽? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮糙麦,結果婚禮上祭刚,老公的妹妹穿的比我還像新娘牌捷。我一直安慰自己墙牌,他們只是感情好,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布暗甥。 她就那樣靜靜地躺著喜滨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撤防。 梳的紋絲不亂的頭發(fā)上虽风,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機與錄音寄月,去河邊找鬼辜膝。 笑死,一個胖子當著我的面吹牛漾肮,可吹牛的內容都是我干的厂抖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼克懊,長吁一口氣:“原來是場噩夢啊……” “哼忱辅!你這毒婦竟也來了?” 一聲冷哼從身側響起谭溉,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤墙懂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后夜只,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垒在,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年扔亥,在試婚紗的時候發(fā)現(xiàn)自己被綠了场躯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡旅挤,死狀恐怖踢关,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情粘茄,我是刑警寧澤签舞,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站柒瓣,受9級特大地震影響儒搭,放射性物質發(fā)生泄漏。R本人自食惡果不足惜芙贫,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一搂鲫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧磺平,春花似錦魂仍、人聲如沸拐辽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俱诸。三九已至,卻和暖如春赊舶,著一層夾襖步出監(jiān)牢的瞬間睁搭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工锯岖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留介袜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓出吹,卻偏偏與公主長得像遇伞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子捶牢,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內容