偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Android Robolectric加載運(yùn)行本地So動(dòng)態(tài)庫(kù)

移動(dòng)開發(fā) Android
Robolectric 是 Android 的單元測(cè)試框架,運(yùn)行無需 Android 真機(jī)環(huán)境直接運(yùn)行在 JVM 之上,所以在 test case 運(yùn)行速度效率上有了很大提升,接近于 Java JUnit test(JUnit test > Robolectric ≫ androidTest)。不過框架本身并不支持 so 本地庫(kù)的加載使用,加載時(shí)會(huì)直接報(bào)錯(cuò),因?yàn)閷?shí)際上運(yùn)行環(huán)境是電腦機(jī)器,而我們打出的 so 文件是給手機(jī)上用的所以當(dāng)然會(huì)報(bào)錯(cuò)。所以下面說說在項(xiàng)目中 Robolectric 要怎么解決需要加載運(yùn)行本地 so 庫(kù)這個(gè)問題。

前言

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

動(dòng)態(tài)庫(kù)

動(dòng)態(tài)庫(kù)又稱動(dòng)態(tài)鏈接庫(kù)(Dynamic-link library 縮寫 DLL),是一個(gè)包含可由多個(gè)程序同時(shí)使用的代碼和數(shù)據(jù)的庫(kù),DLL 不是可執(zhí)行文件。動(dòng)態(tài)鏈接提供了一種方法,使進(jìn)程可以調(diào)用不屬于其可執(zhí)行代碼的函數(shù)。函數(shù)的可執(zhí)行代碼位于一個(gè) DLL 中,該 DLL 包含一個(gè)或多個(gè)已被編譯、鏈接并與使用它們的進(jìn)程分開存儲(chǔ)的函數(shù)。DLL 還有助于共享數(shù)據(jù)和資源。多個(gè)應(yīng)用程序可同時(shí)訪問內(nèi)存中單個(gè)DLL 副本的內(nèi)容。DLL 是一個(gè)包含可由多個(gè)程序同時(shí)使用的代碼和數(shù)據(jù)的庫(kù)。Windows下動(dòng)態(tài)庫(kù)為 .dll 后綴(一般為 PE 格式),在 Linux 在為 .so 后綴(一般為 ELF 格式),macOS下為 .dylib 后綴(一般為 Mach-O 格式)。由于 CPU 架構(gòu)和動(dòng)態(tài)庫(kù)文件格式的不同因而在不同平臺(tái)下不能通用。其它細(xì)節(jié)的東西就不展開了因?yàn)橐膊粫?huì) :-)

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

Robolectric 中使用動(dòng)態(tài)庫(kù)

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

Note: 注意動(dòng)態(tài)庫(kù)名稱已 lib 開頭。

Linux 下 Robolectric 中使用動(dòng)態(tài)庫(kù)

Android 與 Linux 同氣連枝,所以底層的東西很多是通用的,動(dòng)態(tài)庫(kù)也一樣。我們 Android 使用 so 時(shí)一般也要對(duì)不同 CPU 架構(gòu)的手機(jī)下使用不同的 so 文件,譬如:armeabi-v7a、mips、x86。而我們使用的 LInux 發(fā)行版一般都是 64 位的,所以原理上我們使用x86-64 的動(dòng)態(tài)庫(kù)是可以的,不過可能需要處理依賴庫(kù)問題如果你的本地代碼里有 include 其它依賴的話。如果沒加進(jìn)來 Robolectric 運(yùn)行就會(huì)報(bào)如下的錯(cuò)誤:

  1. java.lang.UnsatisfiedLinkError: xxx/xxx.so xxx 動(dòng)態(tài)庫(kù)找不到。 

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

 

 

 

注意 ndk-bundle 里的 so 也是只能在 Linux 下用的,如果用于其它平臺(tái)會(huì)報(bào)錯(cuò),原因前面已說明。

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

macOS 下 Robolectric 中使用動(dòng)態(tài)庫(kù)

前面已提到,不同平臺(tái)下動(dòng)態(tài)鏈接庫(kù)是不通用的,所以必須對(duì)源碼重新編譯打包以移植到不同平臺(tái)下,如果你的 so 沒有源碼的話那在 macOS 和 Windows 下就行不通了。重新打包我們可以按如下兩步進(jìn)行:

  1. # 先生成 .o ,-I 后加進(jìn) Java jni 的編譯依賴 
  2.  
  3. cc -c -I/System/Library/Frameworks/JavaVM.framework/Headers *.cpp 
  4.  
  5. # 打包成 .dylib 
  6.  
  7. g++ -dynamiclib -undefined suppress -flat_namespace *.o -o something.dylib  

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

Windows 下 Robolectric 中使用動(dòng)態(tài)庫(kù)

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

Sample

下面是簡(jiǎn)單的處理代碼示例。首先新建一個(gè)包含 jni 的工程,里面寫個(gè)基本的本地庫(kù),如下:

正常流程 

// native-lib.cpp 

  1.  
  2.  
  3. #include <jni.h> 
  4. #include <string> 
  5.  
  6. extern "C" 
  7. jstring 
  8. Java_xyz_rocko_rsnl_nativeinterface_NativeSample_stringFromJNI( 
  9.        JNIEnv *env, 
  10.        jobject /* this */) { 
  11.  
  12.    // 簡(jiǎn)單返回個(gè)字符串 
  13.    std::string hello = "Hello from Native."
  14.    return env->NewStringUTF(hello.c_str()); 
  15.  

然后在 Application 啟動(dòng)時(shí)會(huì)加載這個(gè)本地庫(kù):

// NativeLibsApplication.java

  1. public class NativeLibsApplication extends Application { 
  2.  
  3.  // Used to load the 'native-lib' library on application startup. 
  4.  static { 
  5.    System.loadLibrary("native-lib"); 
  6.  } 
  7.  

此時(shí)運(yùn)行 Robolectric 的 test case 就發(fā)生如下報(bào)錯(cuò):

  1. java.lang.UnsatisfiedLinkError: no native-lib in java.library.path  

 

 

 

處理后的流程

首先流程應(yīng)該在我們的代碼里避免可以直接加載 so 動(dòng)態(tài)庫(kù),然后 Robolectric 在啟動(dòng)時(shí)自己去加載需要的動(dòng)態(tài)庫(kù)。

// NativeLibsApplication.java

  1. public class NativeLibsApplication extends Application { 
  2.  
  3.  @Override public void onCreate() { 
  4.    super.onCreate(); 
  5.    loadNativeLibraries(); 
  6.  } 
  7.  
  8.  /** 
  9.   * 簡(jiǎn)單讓子類可自己實(shí)現(xiàn) 
  10.   */ 
  11.  protected void loadNativeLibraries() { 
  12.  
  13.     // 代碼里真正加載本地庫(kù)的地方,當(dāng)然你自己的可以處理地更解耦一點(diǎn)。 
  14.    NativeLibrariesManager.loadNativeLibraries(); 
  15.  } 
  16.  

然后我們的 Robolectric 里自定義自己的 Application,里面根據(jù)需要在不同運(yùn)行平臺(tái)下自己加載需要的本地動(dòng)態(tài)庫(kù),首先復(fù)制我們給 Robolectric 用的本地庫(kù)到 test 的 libs 文件夾里,按不同平臺(tái)分類,如下圖: 

 

 

 

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

然后重新移植打出 macOS 下的動(dòng)態(tài)庫(kù),簡(jiǎn)單寫個(gè)打包腳本如下:

// make_macOS_dylib.sh

  1. #!/usr/bin/env bash 
  2.  
  3. OUTPUT=../../../build/intermediates/dylibs 
  4. mkdir -p ${OUTPUT
  5.  
  6. # .o file 
  7. cc -c -I/System/Library/Frameworks/JavaVM.framework/Headers *.cpp -o ${OUTPUT}/libnative-lib.o 
  8.  
  9. # .dylib file 
  10. g++ -dynamiclib -undefined suppress -flat_namespace ${OUTPUT}/*.o -o ${OUTPUT}/libnative-lib.dylib  

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

然后我們自定義 Application 處理加載這些動(dòng)態(tài)庫(kù):

// RobolectricApplication.java

  1. public class RobolectricApplication extends NativeLibsApplication { 
  2.  
  3.  static { 
  4.    ShadowLog.stream = System.out; //Android logcat output
  5.  } 
  6.  
  7.  @Override protected void loadNativeLibraries() { 
  8.    //Disable super class load so file. 
  9.    //super.loadNativeLibraries(); 
  10.    Log.d(TAG, "=====>> Robolectric start native libraries."); 
  11.  
  12.    String libsBasePath = 
  13.        new File(new File("").getAbsolutePath() + "/src/test/libs").getAbsolutePath(); 
  14.    String os = System.getProperty("os.name"); 
  15.    os = !TextUtils.isEmpty(os) ? os : ""
  16.    List<File> soFileList = new ArrayList<>(); 
  17.    String systemArchPath = libsBasePath + "/framework/"
  18.    //!!! 64 位機(jī)器下處理 
  19.    if (os.contains("Mac")) { 
  20.      //load system library if need 
  21.      String macSysSoBasePath = systemArchPath + "macOS/"
  22.      soFileList.addAll(addLibs(macSysSoBasePath)); 
  23.      // App so... 
  24.      String macAppSoPath = libsBasePath + "/macOS_x86-64/"
  25.      // mac下so要使用macOS專用庫(kù) 
  26.      soFileList.addAll(addLibs(macAppSoPath)); 
  27.    } else if (os.contains("Linux")) { 
  28.      //load system library if need 
  29.      String linuxSysSoBasePath = systemArchPath + "arch_x86-64/"
  30.      soFileList.addAll(addLibs(linuxSysSoBasePath)); 
  31.      // App so... 
  32.      String linuxAppSoPath = libsBasePath + "/linux_x86-64/"
  33.      soFileList.addAll(addLibs(linuxAppSoPath)); 
  34.    } else if (os.contains("Windows")) { 
  35.      // ignore 
  36.    } 
  37.  
  38.    for (File soFie : soFileList) { 
  39.      System.load(soFie.getAbsolutePath()); 
  40.    } 
  41.  } 
  42.  
  43.  private List<File> addLibs(@NonNull String path) { 
  44.    File[] basePathFiles = new File(path).listFiles(); 
  45.    List<File> pathFilesList = new ArrayList<>(); 
  46.    if (basePathFiles != null && basePathFiles.length > 0) { 
  47.      pathFilesList.addAll(Arrays.asList(basePathFiles)); 
  48.    } 
  49.    return pathFilesList; 
  50.  } 
  51.  

現(xiàn)在就可以加載了,運(yùn)行如下 test case,結(jié)果如下圖,成功了。

  1. @Test public void testLoadNativeLibrariesSuccess() throws Exception { 
  2.       String nativeExcepted = "Hello from Native."
  3.       String result = NativeSample.stringFromJNI(); 
  4.       Log.d(TAG, "result: " + result); 
  5.       assertEquals(nativeExcepted, result); 
  6.   

 

 

 

End

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

項(xiàng)目實(shí)例源碼:RobolectricSupportNativeLibs

參考

Core Java APIs and the Java Runtime on OS X

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2017-01-03 15:23:56

Android動(dòng)態(tài)加載SO庫(kù)

2015-10-26 10:14:13

Android開發(fā)環(huán)境robolectric

2023-12-06 08:45:01

WasmJavaScript

2016-12-02 20:43:34

Android動(dòng)態(tài)加載DL框架

2021-11-29 05:27:49

UPS電源滿載運(yùn)行

2009-08-28 16:14:26

C#實(shí)現(xiàn)加載動(dòng)態(tài)庫(kù)

2023-10-09 18:22:35

2024-03-25 00:02:00

Android移動(dòng)開發(fā)視頻

2011-06-02 09:08:09

Android 文件

2024-05-27 09:52:57

反射技術(shù).NET動(dòng)態(tài)庫(kù)

2014-04-29 13:16:42

OpenGLAndroid庫(kù)加載過程

2023-02-17 08:22:05

AndroidGlide

2021-10-24 06:50:52

AndroidClassLoaderJava

2021-03-15 08:33:01

CC++動(dòng)態(tài)庫(kù)

2024-09-12 10:04:06

內(nèi)存程序系統(tǒng)

2023-11-16 12:35:00

Java程序

2019-11-26 09:00:29

動(dòng)態(tài)庫(kù)靜態(tài)庫(kù)運(yùn)行時(shí)庫(kù)

2011-05-31 11:05:16

ListView 數(shù)據(jù)

2021-04-18 07:20:09

CMS系統(tǒng)模塊

2025-01-20 09:09:59

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)