我們平時(shí)使用的動(dòng)態(tài)庫(kù)都是由C/C++開(kāi)發(fā)最后生成的.so文件。
可以先看看一個(gè)JNI的開(kāi)發(fā)過(guò)程。
一. 開(kāi)發(fā)JNI
有兩種方式,現(xiàn)在一種比較快的方式是AndroidStudio你在創(chuàng)建項(xiàng)目選擇Module的時(shí)候它會(huì)給你個(gè)JNI的模板,直接使用那個(gè)就行。
但是我還是比較喜歡傳統(tǒng)的方法。
簡(jiǎn)單來(lái)說(shuō)傳統(tǒng)的方式就是你用命令來(lái)把java文件變成C++的頭文件
簡(jiǎn)單演示一遍,先寫(xiě)個(gè)java類
public class TestJni {
static {
System.loadLibrary("KylimTest");
}
public static native String getMsg();
}
定義了一個(gè)native修飾的方法,在代碼調(diào)用這個(gè)方法之后JNI就會(huì)自動(dòng)調(diào)用到動(dòng)態(tài)庫(kù)中相應(yīng)的方法。
將這個(gè)類用命令生成頭文件,來(lái)到文件夾路徑下輸入命令
可以看到默認(rèn)會(huì)生成一個(gè).h的頭文件,自動(dòng)命名為 包名_類名.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相關(guān)的, 如果你嫌用命令生成麻煩,你可以自己創(chuàng)建一個(gè).h文件然后方法命名就按照這樣的規(guī)范去寫(xiě)
頭文件只是為了定義,我們需要自己寫(xiě)原文件,所以要?jiǎng)?chuàng)建一個(gè).cpp結(jié)尾的文件
#include "com_kylim_nativetest_TestJni.h"
JNIEXPORT jstring JNICALL Java_com_kylim_nativetest_TestJni_getMsg
(JNIEnv *env, jclass cls){
jstring result = env->NewStringUTF("結(jié)果是");
return result;
}
方法命名是有規(guī)范的,看Demo也知道怎么規(guī)范了,沒(méi)必要多解釋,這樣兩端的代碼就寫(xiě)完了,但是僅僅這樣是無(wú)法運(yùn)行項(xiàng)目的。
還需要些一些配置,因?yàn)樵贏ndroidStudio中是Gradle去幫我們編譯C++的代碼,所以需要寫(xiě)這些配置。如果你不是用AS開(kāi)發(fā),你用其它工具開(kāi)發(fā)直接生成.so文件再丟進(jìn)AS中的話,可以忽略這一步。
先看看我的Jni目錄
要?jiǎng)?chuàng)建一個(gè)Android.mk
#固定寫(xiě)法
LOCAL_PATH:=$(call my-dir)
#固定寫(xiě)法
include $(CLEAR_VARS)
#生成so名稱
LOCAL_MODULE := KylimTest
LOCAL_SRC_FILES := testone.cpp
#固定寫(xiě)法
include $(BUILD_SHARED_LIBRARY)
具體的配置可自行去查找,這里不是主要講JNI的,所以就不講這么細(xì)。
還需要一個(gè)Application.mk
# 選擇不同的 ABI,多個(gè)使用空格作為分隔符,全部是all
# APP_ABI := armeabi armeabi-v7a
APP_ABI := armeabi-v7a
# 指定要使用的運(yùn)行時(shí)
APP_STL := c++_static
當(dāng)然這樣還不行,都說(shuō)了是Gradle進(jìn)行編譯,那么肯定還要在Gradle中寫(xiě)一些配置
android {
defaultConfig {
ndkBuild {
//指定 Application.mk 的路徑
arguments "NDK_APPLICATION_MK:=src/main/jni/Application.mk"
//指定生成哪些平臺(tái)的 so 文件
abiFilters "armeabi-v7a"
//cFlags 和 cppFlags 是用來(lái)設(shè)置環(huán)境變量的, 一般不需要?jiǎng)?
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')
}
}
}
這樣就能簡(jiǎn)單的跑一個(gè)JNI的Demo,總的來(lái)說(shuō)就是Java這邊寫(xiě)一個(gè)類定義一些native方法和加載,C++這邊寫(xiě)具體的方法實(shí)現(xiàn)。
二.用Go開(kāi)發(fā)動(dòng)態(tài)庫(kù)
上面說(shuō)的原生方法是用C/C++進(jìn)行開(kāi)發(fā)的,那么如果你不會(huì)C++的話怎么辦,C++的學(xué)習(xí)也并非這么容易,就拿兩邊的類型來(lái)說(shuō),一開(kāi)始新手肯定會(huì)碰到類型轉(zhuǎn)換的問(wèn)題,往往會(huì)先勸退一些人,但是Go不一樣,有Java基礎(chǔ)的話學(xué)起go還是挺快的。
那么用Go開(kāi)發(fā)的動(dòng)態(tài)庫(kù)是怎樣的?也是SO文件嗎,是不是也像C++一樣,編譯后經(jīng)過(guò)某步操作生成SO文件。
我看到網(wǎng)上有些文章確實(shí)是寫(xiě)怎么生成so的,但是說(shuō)得太少,感覺(jué)不可靠,直到我看到官方有寫(xiě)。
可以在官方中看到是有一個(gè)mobile的庫(kù)的
https://github.com/golang/mobile
讀下去它會(huì)指引你去wiki
https://github.com/golang/go/wiki/Mobile
可以看出它會(huì)打出一個(gè)aar的文件,那么aar對(duì)于我們接入來(lái)說(shuō)確實(shí)很方法,但我很想探究這個(gè)aar里面究竟是什么,所以我們需要打出一個(gè)aar然后解壓看看它里面到底是什么
這里先說(shuō)一下,下載這個(gè)庫(kù)之前,你本地肯定先要配置好Go的環(huán)境
然后按照這里的流程就行下載
go get -d golang.org/x/mobile/example/basic
但這輸入這條命令需要科學(xué)上網(wǎng)的方式才能下載,總的來(lái)說(shuō)很麻煩。
所以我們可以直接克隆mobile的庫(kù),就是上面的這個(gè)鏈接 https://github.com/golang/mobile
直接下載下來(lái),除此之外,還需要tools,這些都在Go中,鏈接 https://github.com/golang/tools
將這兩個(gè)下載下來(lái),然后拷貝到你的Go的以下路徑
go/src 創(chuàng)建一個(gè)文件夾golang.org/x ,把這兩個(gè)文件夾丟進(jìn)去
然后輸入命令
可以輸入命令 查看安裝配置是否成功
如果配置成功會(huì)給你一些提示
我感覺(jué)文檔寫(xiě)得還是不算清楚,但是它有告訴你用什么命令生成aar
gomobile bind -o app/hello.aar -target=android golang.org/x/mobile/example/bind/hello
你在GoPath中創(chuàng)建一個(gè)Go文件,我是用GoLand進(jìn)行開(kāi)發(fā)的,項(xiàng)目的目錄設(shè)置成GoPath,編寫(xiě)完之后,可以直接在文件中運(yùn)行
gomobile bind -o 輸入文件名.aar -target=android
這樣就能在文件夾中生成一個(gè)aar
接著我們看看aar里面是什么,解壓
首先可以看到生成這些ABI的so文件,再看看Manifest
這里有限制最低版本,所以如果你的版本比他還低的話就需要注意一下了
然后res里面是可以看到?jīng)]有文件的。
最后我們反編譯class文件
因?yàn)檫@不是Demo,直接是寫(xiě)公司的項(xiàng)目,所以有些地方要碼,但是不影響。
從這里看得出,go幫我們生成了一個(gè)java文件,這個(gè)java文件定義了再Go中命名的原生方法。
其實(shí)從這里就可以看出,Go用的也基本是我們最上面寫(xiě)的JNI的方法,只是他幫你封裝起來(lái)了而已
但是他的原生代碼是不是轉(zhuǎn)成C++的我就不清楚了,因?yàn)槲也粫?huì)反編譯SO文件。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
您可能感興趣的文章:- golang實(shí)踐-第三方包為私有庫(kù)的配置方案
- 完美解決golang go get私有倉(cāng)庫(kù)的問(wèn)題
- golang gopm get -g -v 無(wú)法獲取第三方庫(kù)的解決方案
- Golang: 內(nèi)建容器的用法
- 淺談golang 中time.After釋放的問(wèn)題
- golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析
- golang日志包logger的用法詳解
- Golang如何調(diào)用windows下的dll動(dòng)態(tài)庫(kù)中的函數(shù)