日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android 动态库 后缀,Android Robolectric加载运行本地So动态库

發布時間:2025/3/11 Android 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 动态库 后缀,Android Robolectric加载运行本地So动态库 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

Robolectric 是 Android 的單元測試框架,運行無需 Android 真機環境直接運行在 JVM 之上,所以在 test case 運行速度效率上有了很大提升,接近于 Java JUnit test(JUnit test > Robolectric ? androidTest)。不過框架本身并不支持 so 本地庫的加載使用,加載時會直接報錯,因為實際上運行環境是電腦機器,而我們打出的 so 文件是給手機上用的所以當然會報錯。雖然在 GitHub 上很多人問過關于使用 so 的問題但基本都建議說不要在單元測試中去加載本地庫,這在原則上是要這么做,但可能有些項目中做起來就有些困難了,比如在代碼結構不夠好、依賴耦合較大或者本身就對 so 庫依賴很大的情況下。所以下面說說在項目中 Robolectric 要怎么解決需要加載運行本地 so 庫這個問題。

動態庫

動態庫又稱動態鏈接庫(Dynamic-link library 縮寫 DLL),是一個包含可由多個程序同時使用的代碼和數據的庫,DLL 不是可執行文件。動態鏈接提供了一種方法,使進程可以調用不屬于其可執行代碼的函數。函數的可執行代碼位于一個 DLL 中,該 DLL 包含一個或多個已被編譯、鏈接并與使用它們的進程分開存儲的函數。DLL 還有助于共享數據和資源。多個應用程序可同時訪問內存中單個DLL 副本的內容。DLL 是一個包含可由多個程序同時使用的代碼和數據的庫。Windows下動態庫為 .dll 后綴(一般為 PE 格式),在 Linux 在為 .so 后綴(一般為 ELF 格式),macOS下為 .dylib 后綴(一般為 Mach-O 格式)。由于 CPU 架構和動態庫文件格式的不同因而在不同平臺下不能通用。其它細節的東西就不展開了因為也不會 :-)

而 Android 本身是 Linux 系統,所以用的動態庫也是 .so 的文件,因而運行與 JVM 的 Robolectric 是不能直接加載使用的(Linux 某些情況下可用,下面提到)。

Robolectric 中使用動態庫

我們知道動態庫一般都是打給特定平臺、特定 CPU 架構用的,所以要解決在 Robolectric 下加載運行 so 動態庫的問題的思路就是在不同 Robolectric 運行平臺下去處理加載不同的動態庫,所以你要在 Ronbolectriv 中使用的 so 動態庫***要有源碼不然在 macOS 和 Windows 下就不就好處理了。

Note: 注意動態庫名稱已 lib 開頭。

Linux 下 Robolectric 中使用動態庫

Android 與 Linux 同氣連枝,所以底層的東西很多是通用的,動態庫也一樣。我們 Android 使用 so 時一般也要對不同 CPU 架構的手機下使用不同的 so 文件,譬如:armeabi-v7a、mips、x86。而我們使用的 LInux 發行版一般都是 64 位的,所以原理上我們使用x86-64 的動態庫是可以的,不過可能需要處理依賴庫問題如果你的本地代碼里有 include 其它依賴的話。如果沒加進來 Robolectric 運行就會報如下的錯誤:

java.lang.UnsatisfiedLinkError:?xxx/xxx.so?xxx?動態庫找不到。

xxx.so 就是你所使用 so 的依賴,比如把新浪微博 SDK 的 x86-64 的 libweibosdkcore.so 加載進來的話就會報 liblog.so 等找不到,因為 libweibosdkcore 中有對 Android liblog 等 so 庫的依賴。那這個問題怎么解決呢。我們想想打包 so 庫時用的是 ndk,需要使用 ndk-bundle 工具,我們想想,跟編譯 apk 差不多,apk 打包需要 sdk 工具,compileSdk 里就是我們編譯的依賴,里面有android.jar。所以我們可以到 ndk-bundle 里找找,***我們發現不同 CPU 架構下的 so 依賴庫都是有的,像我們一般的電腦 64 位 CPU 即可使用 arch-x86_64 下的 so 動態庫,所以我們只需要在加載我們程序的 so 庫之前加載這些必須的依賴即可。處理代碼后面貼出。

注意 ndk-bundle 里的 so 也是只能在 Linux 下用的,如果用于其它平臺會報錯,原因前面已說明。

java.lang.UnsatisfiedLinkError:?xxx.so:?unknown?file?type,firsteight?bytes:?0x7F?0x45?0x4C?0x46?0x02?0x01?0x01?0x00

macOS 下 Robolectric 中使用動態庫

前面已提到,不同平臺下動態鏈接庫是不通用的,所以必須對源碼重新編譯打包以移植到不同平臺下,如果你的 so 沒有源碼的話那在 macOS 和 Windows 下就行不通了。重新打包我們可以按如下兩步進行:

#?先生成?.o?,-I?后加進?Java?jni?的編譯依賴

cc?-c?-I/System/Library/Frameworks/JavaVM.framework/Headers?*.cpp

#?打包成?.dylib

g++?-dynamiclib?-undefined?suppress?-flat_namespace?*.o?-o?something.dylib

某些依賴庫可以到 /usr/lib 下找找,比如 libc 和 libstdc++ 。

Windows 下 Robolectric 中使用動態庫

本人沒有在 Windows 下開發所以這部分就略過了,思路是一樣的。

Sample

下面是簡單的處理代碼示例。首先新建一個包含 jni 的工程,里面寫個基本的本地庫,如下:

正常流程

//?native-lib.cpp#include?

#include?

extern?"C"

jstring

Java_xyz_rocko_rsnl_nativeinterface_NativeSample_stringFromJNI(

JNIEnv?*env,

jobject?/*?this?*/)?{

//?簡單返回個字符串

std::string?hello?=?"Hello?from?Native.";

returnenv->NewStringUTF(hello.c_str());

}

然后在 Application 啟動時會加載這個本地庫:

// NativeLibsApplication.java

publicclass?NativeLibsApplication?extends?Application?{

//?Used?toloadthe'native-lib'libraryonapplication?startup.

static{

System.loadLibrary("native-lib");

}

}

此時運行 Robolectric 的 test case 就發生如下報錯:

java.lang.UnsatisfiedLinkError:nonative-libinjava.library.path

處理后的流程

首先流程應該在我們的代碼里避免可以直接加載 so 動態庫,然后 Robolectric 在啟動時自己去加載需要的動態庫。

// NativeLibsApplication.java

publicclass?NativeLibsApplication?extends?Application?{

@Override?publicvoid?onCreate()?{

super.onCreate();

loadNativeLibraries();

}

/**

*?簡單讓子類可自己實現

*/

protected?void?loadNativeLibraries()?{

//?代碼里真正加載本地庫的地方,當然你自己的可以處理地更解耦一點。

NativeLibrariesManager.loadNativeLibraries();

}

}

然后我們的 Robolectric 里自定義自己的 Application,里面根據需要在不同運行平臺下自己加載需要的本地動態庫,首先復制我們給 Robolectric 用的本地庫到 test 的 libs 文件夾里,按不同平臺分類,如下圖:

Linux 下的我們從 ndk-bundle 里復制我們需要的 .so,然后我們自己的本地庫打一個 x86-64 的即可,注意 compileSdkVersion 選上高一點支持 x86-64 的版本。

然后重新移植打出 macOS 下的動態庫,簡單寫個打包腳本如下:

// make_macOS_dylib.sh

#!/usr/bin/env?bash

OUTPUT=../../../build/intermediates/dylibs

mkdir?-p?${OUTPUT}

#?.o?file

cc?-c?-I/System/Library/Frameworks/JavaVM.framework/Headers?*.cpp?-o?${OUTPUT}/libnative-lib.o

#?.dylib?file

g++?-dynamiclib?-undefined?suppress?-flat_namespace?${OUTPUT}/*.o?-o?${OUTPUT}/libnative-lib.dylib

libnative-lib.dylib 就是我們要的。

然后我們自定義 Application 處理加載這些動態庫:

// RobolectricApplication.java

publicclass?RobolectricApplication?extends?NativeLibsApplication?{

static{

ShadowLog.stream?=?System.out;?//Android?logcatoutput.

}

@Override?protected?void?loadNativeLibraries()?{

//Disable?super?class?loadso?file.

//super.loadNativeLibraries();

Log.d(TAG,?"=====>>?Robolectric?start?native?libraries.");

String?libsBasePath?=

new?File(new?File("").getAbsolutePath()?+"/src/test/libs").getAbsolutePath();

String?os?=?System.getProperty("os.name");

os?=?!TextUtils.isEmpty(os)???os?:?"";

List?soFileList?=?new?ArrayList<>();

String?systemArchPath?=?libsBasePath?+?"/framework/";

//!!!?64?位機器下處理

if?(os.contains("Mac"))?{

//loadsystem?library?if?need

String?macSysSoBasePath?=?systemArchPath?+?"macOS/";

soFileList.addAll(addLibs(macSysSoBasePath));

//?App?so...

String?macAppSoPath?=?libsBasePath?+?"/macOS_x86-64/";

//?mac下so要使用macOS專用庫

soFileList.addAll(addLibs(macAppSoPath));

}?elseif?(os.contains("Linux"))?{

//loadsystem?library?if?need

String?linuxSysSoBasePath?=?systemArchPath?+?"arch_x86-64/";

soFileList.addAll(addLibs(linuxSysSoBasePath));

//?App?so...

String?linuxAppSoPath?=?libsBasePath?+?"/linux_x86-64/";

soFileList.addAll(addLibs(linuxAppSoPath));

}?elseif?(os.contains("Windows"))?{

//?ignore

}

for(File?soFie?:?soFileList)?{

System.load(soFie.getAbsolutePath());

}

}

private?List?addLibs(@NonNull?String?path)?{

File[]?basePathFiles?=?new?File(path).listFiles();

List?pathFilesList?=?new?ArrayList<>();

if?(basePathFiles?!=?null&&?basePathFiles.length?>?0)?{

pathFilesList.addAll(Arrays.asList(basePathFiles));

}

returnpathFilesList;

}

}

現在就可以加載了,運行如下 test case,結果如下圖,成功了。

@Testpublicvoid?testLoadNativeLibrariesSuccess()?throws?Exception?{

String?nativeExcepted?=?"Hello?from?Native.";

String?result?=?NativeSample.stringFromJNI();

Log.d(TAG,?"result:?"+?result);

assertEquals(nativeExcepted,?result);

}

End

Linux 下使用最快速方便,只需要打包程序的 so 時順便打包出 x86-64 的 so ,然后復制 ndk-bundle 的 so 加上需要的依賴即可。macOS 和 Windows 下就需要自己打包出各自平臺下的動態庫才可使用,如果代碼里有 Android 自帶 so 依賴的話那就需要自己去重新移植編譯打包 ndk-bundle 里的動態庫了。

參考

【編輯推薦】

【責任編輯:枯木 TEL:(010)68476606】

點贊 0

總結

以上是生活随笔為你收集整理的android 动态库 后缀,Android Robolectric加载运行本地So动态库的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。