ǰÑÔ
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 ÊDz»ÄÜÖ±½Ó¼ÓÔØÊ¹ÓõÄ(Linux ijЩÇé¿öÏ¿ÉÓã¬ÏÂÃæÌáµ½)¡£
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 Àï¾ÍÊÇÎÒÃDZàÒëµÄÒÀÀµ£¬ÀïÃæÓÐandroid.jar¡£ËùÒÔÎÒÃÇ¿ÉÒÔµ½ ndk-bundle
ÀïÕÒÕÒ£¬×îºóÎÒÃÇ·¢ÏÖ²»Í¬ CPU ¼Ü¹¹Ï嵀 so ÒÀÀµ¿â¶¼ÊÇÓеģ¬ÏñÎÒÃÇÒ»°ãµÄµçÄÔ 64 λ CPU ¼´¿ÉʹÓÃ
arch-x86_64 Ï嵀 so ¶¯Ì¬¿â£¬ËùÒÔÎÒÃÇÖ»ÐèÒªÔÚ¼ÓÔØÎÒÃdzÌÐòµÄ so ¿â֮ǰ¼ÓÔØÕâЩ±ØÐëµÄÒÀÀµ¼´¿É¡£´¦Àí´úÂëºóÃæÌù³ö¡£

×¢Òâ ndk-bundle ÀïµÄ so Ò²ÊÇÖ»ÄÜÔÚ Linux ÏÂÓõģ¬Èç¹ûÓÃÓÚÆäËüƽ̨»á±¨´í£¬ÔÒòÇ°ÃæÒÑ˵Ã÷¡£
java.lang.UnsatisfiedLinkError: xxx.so: unknown file type, first eight bytes:
0x7F 0x45 0x4C 0x46 0x02 0x01 0x01 0x00 |
macOS Ï Robolectric ÖÐʹÓö¯Ì¬¿â
Ç°ÃæÒÑÌáµ½£¬²»Í¬Æ½Ì¨Ï¶¯Ì¬Á´½Ó¿âÊDz»Í¨Óõģ¬ËùÒÔ±ØÐë¶ÔÔ´ÂëÖØÐ±àÒë´ò°üÒÔÒÆÖ²µ½²»Í¬Æ½Ì¨Ï£¬Èç¹ûÄãµÄ 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 |
ijЩÒÀÀµ¿â¿ÉÒÔµ½ /usr/lib ÏÂÕÒÕÒ£¬±ÈÈç libc ºÍ libstdc++ ¡£
Windows Ï Robolectric ÖÐʹÓö¯Ì¬¿â
±¾ÈËûÓÐÔÚ Windows Ï¿ª·¢ËùÒÔÕⲿ·Ö¾ÍÂÔ¹ýÁË£¬Ë¼Â·ÊÇÒ»ÑùµÄ¡£
Sample
ÏÂÃæÊǼòµ¥µÄ´¦Àí´úÂëʾÀý¡£Ê×ÏÈн¨Ò»¸ö°üº¬ jni µÄ¹¤³Ì£¬ÀïÃæÐ´¸ö»ù±¾µÄ±¾µØ¿â£¬ÈçÏ£º
Õý³£Á÷³Ì
// native-lib.cpp
#include <jni.h>
#include <string>
extern "C"
jstring
Java_xyz_rocko_rsnl_nativeinterface_NativeSample_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
// ¼òµ¥·µ»Ø¸ö×Ö·û´®
std::string hello = "Hello from Native.";
return env->NewStringUTF(hello.c_str());
} |
È»ºóÔÚ Application Æô¶¯Ê±»á¼ÓÔØÕâ¸ö±¾µØ¿â£º
public class NativeLibsApplication extends Application { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } } |
´ËʱÔËÐÐ Robolectric µÄ test case ¾Í·¢ÉúÈçϱ¨´í£º
java.lang.UnsatisfiedLinkError: no native-lib in java.library.path |
´¦ÀíºóµÄÁ÷³Ì

Ê×ÏÈÁ÷³ÌÓ¦¸ÃÔÚÎÒÃǵĴúÂëÀï±ÜÃâ¿ÉÒÔÖ±½Ó¼ÓÔØ so ¶¯Ì¬¿â£¬È»ºó Robolectric ÔÚÆô¶¯Ê±×Ô¼ºÈ¥¼ÓÔØÐèÒªµÄ¶¯Ì¬¿â¡£
// NativeLibsApplication.java
public class NativeLibsApplication extends Application { @Override public void 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
public class RobolectricApplication extends NativeLibsApplication { static { ShadowLog.stream = System.out; //Android logcat output. } @Override protected void loadNativeLibraries() { //Disable super class load so 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<File> soFileList = new ArrayList<>(); String systemArchPath = libsBasePath + "/framework/"; //!!! 64 λ»úÆ÷Ï´¦Àí if (os.contains("Mac")) { //load system 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)); } else if (os.contains("Linux")) { //load system 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)); } else if (os.contains("Windows")) { // ignore } for (File soFie : soFileList) { System.load(soFie.getAbsolutePath()); } } private List<File> addLibs(@NonNull String path) { File[] basePathFiles = new File(path).listFiles(); List<File> pathFilesList = new ArrayList<>(); if (basePathFiles != null && basePathFiles.length > 0) { pathFilesList.addAll(Arrays.asList(basePathFiles)); } return pathFilesList; } } |
ÏÖÔھͿÉÒÔ¼ÓÔØÁË£¬ÔËÐÐÈçÏ test case£¬½á¹ûÈçÏÂͼ£¬³É¹¦ÁË¡£
@Test public void 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 ÀïµÄ¶¯Ì¬¿âÁË¡£
ÏîĿʵÀýÔ´Â룺RobolectricSupportNativeLibs |