发布时间:2025-12-11 03:20:38 浏览次数:9
要使用ndk进行编程,在Java层就必须要对so进行加载。Java层加载so的函数有两个:
System.load(StringpathName)System.loadLibraray(StringlibName)
两个函数的区别就是load函数的参数是so文件的绝对地址。loadLibrary的参数是so的名称,这个so文件必须放在apk的lib目录下,而且so的名称必须去掉前面的lib和后边的“.so”。如下所示:
System.load("/data/local/tmp/libhello.so");System.loadLibrary("hello");System.java
load和loadLibraray函数在/android6.0/libcore/luni/src/main/java/java/lang/System.java中:
publicstaticvoidload(StringpathName){Runtime.getRuntime().load(pathName,VMStack.getCallingClassLoader());}/***See{@linkRuntime#loadLibrary}.*/publicstaticvoidloadLibrary(StringlibName){Runtime.getRuntime().loadLibrary(libName,VMStack.getCallingClassLoader());}Runtime.java
getRuntime()函数用于获取Runtime的一个实例。
publicstaticRuntimegetRuntime(){returnmRuntime;}loadLibrary():
publicvoidloadLibrary(Stringnickname){loadLibrary(nickname,VMStack.getCallingClassLoader());}voidloadLibrary(StringlibraryName,ClassLoaderloader){if(loader!=null){Stringfilename=loader.findLibrary(libraryName);if(filename==null){//It'snotnecessarilytruethattheClassLoaderused//System.mapLibraryName,butthedefaultsetupdoes,andit's//misleadingtosaywedidn'tfind"libMyLibrary.so"whenwe//actuallysearchedfor"liblibMyLibrary.so.so".thrownewUnsatisfiedLinkError(loader+"couldn'tfind\""+System.mapLibraryName(libraryName)+"\"");}Stringerror=doLoad(filename,loader);if(error!=null){thrownewUnsatisfiedLinkError(error);}return;}Stringfilename=System.mapLibraryName(libraryName);List<String>candidates=newArrayList<String>();StringlastError=null;for(Stringdirectory:mLibPaths){Stringcandidate=directory+filename;candidates.add(candidate);if(IoUtils.canOpenReadOnly(candidate)){Stringerror=doLoad(candidate,loader);if(error==null){return;//Wesuccessfullyloadedthelibrary.Jobdone.}lastError=error;}}if(lastError!=null){thrownewUnsatisfiedLinkError(lastError);}thrownewUnsatisfiedLinkError("Library"+libraryName+"notfound;tried"+candidates);}loadLibrary()函数主要进行了两步操作。
第一步:获取library的path:
根据ClassLoader的不同,会有两种不同的处理方法。
如果ClassLoader非空,会利用ClassLoader的findLibrary()方法获取library的path。
如果ClassLoader为空,会通过传入的library name和System.mapLibraryName获得真正的library name。例如传入的是hello,
得到的是libhello.so,然后在mLibPaths查找`libhello.so',最终确定library的path。
第二步:调用doLoad()方法。
第一步目前我不关心,不去深究。主要看doLoad的实现。
privateStringdoLoad(Stringname,ClassLoaderloader){StringldLibraryPath=null;StringdexPath=null;if(loader==null){//Weusethegivenlibrarypathforthebootclassloader.Thisisthepath//alsousedinloadLibraryNameifloaderisnull.ldLibraryPath=System.getProperty("java.library.path");}elseif(loaderinstanceofBaseDexClassLoader){BaseDexClassLoaderdexClassLoader=(BaseDexClassLoader)loader;ldLibraryPath=dexClassLoader.getLdLibraryPath();}//nativeLoadshouldbesynchronizedsothere'sonlyoneLD_LIBRARY_PATHinuseregardless//ofhowmanyClassLoadersareinthesystem,butdalvikdoesn'tsupportsynchronized//internalnatives.synchronized(this){returnnativeLoad(name,loader,ldLibraryPath);}}获得libbrary的路径;
调用native函数nativeLoad()进行加载加载。
java_lang_Runtime.cc
文件位置:/android6.0.1_r66/art/runtime/native/java_lang_Runtime.cc
staticjstringRuntime_nativeLoad(JNIEnv*env,jclass,jstringjavaFilename,jobjectjavaLoader,jstringjavaLdLibraryPathJstr){ScopedUtfCharsfilename(env,javaFilename);if(filename.c_str()==nullptr){returnnullptr;}SetLdLibraryPath(env,javaLdLibraryPathJstr);std::stringerror_msg;{JavaVMExt*vm=Runtime::Current()->GetJavaVM();boolsuccess=vm->LoadNativeLibrary(env,filename.c_str(),javaLoader,&error_msg);if(success){returnnullptr;}}//Don'tletapendingexceptionfromJNI_OnLoadcauseaCheckJNIissuewithNewStringUTF.env->ExceptionClear();returnenv->NewStringUTF(error_msg.c_str());}nativeLoad()主要做了两件事:
第一件事:利用SetLdLibraryPath()将Java的library的path转换成native的。
第二件事情:调用LoadNativeLibrary进行加载。<关键>
java_vm_ext.cc
位置:/android6.0/art/runtime/java_vm_ext.cc
boolJavaVMExt::LoadNativeLibrary(JNIEnv*env,conststd::string&path,jobjectclass_loader,std::string*error_msg){...constchar*path_str=path.empty()?nullptr:path.c_str();void*handle=dlopen(path_str,RTLD_NOW);...if(needs_native_bridge){library->SetNeedsNativeBridge();sym=library->FindSymbolWithNativeBridge("JNI_OnLoad",nullptr);}else{sym=dlsym(handle,"JNI_OnLoad");}if(sym==nullptr){VLOG(jni)<<"[NoJNI_OnLoadfoundin\""<<path<<"\"]";was_successful=true;}else{利用dlopen()打开so文件,得到函数的指针
利用dlsym()调用so文件中的JNI_OnLoad方法,开始so文件的执行。
关于Android中怎么加载so文件源码问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注本站行业资讯频道了解更多相关知识。