大佬教程收集整理的这篇文章主要介绍了深入分析Android加载so文件源码,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
Android系统中使用Ndk进行编程,有很多的好处(Java的跨平台特性导致其本地交互的能力不够强大,一些和操作系统相关的特性Java无法完成;代码的保护:由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大;可以方便地使用C/C++开源库;便于移植,用C/C++写的库可以方便在其他平台上再次使用;提供程序在某些特定情形下的执行效率,但是并不能明显提升Android程序的性能)。
要使用Ndk进行编程,在Java层就必须要对so进行加载。Java层加载so的函数有两个:
两个函数的区别就是load函数的参数是so文件的绝对地址。loadLibrary的参数是so的名称,这个so文件必须放在apk的lib目录下,而且so的名称必须去掉前面的lib和后边的“.so”。如下所示:
System.java
load和loadLibraray函数在/android6.0/libcore/luni/src/main/java/java/lang/System.java中:
public static void load(String pathName) { Runtime.getRuntime().load(pathName,VMStack.getCallingClassLoader()); } /** * See {@link Runtime#loadLibrary}. */ public static void loadLibrary(String libName) { Runtime.getRuntime().loadLibrary(libName,VMStack.getCallingClassLoader()); }
Runtime.java
getRuntime()函数用于获取Runtime的一个实例。
public static Runtime getRuntime() { return mRuntime; }
loadLibrary():
public void loadLibrary(String nickName) { loadLibrary(nickname,VMStack.getCallingClassLoader()); } void loadLibrary(String libraryName,ClassLoader loader) { if (loader != null) { String filename = loader.findLibrary(libraryName); if (filename == null) { // It's not necessarily true that the ClassLoader used // System.mapLibraryName,but the default setup does,and it's // misleading to say we didn't find "libMyLibrary.so" when we // actually searched for "liblibMyLibrary.so.so". throw new UnsatisfiedLinkError(loader + " Couldn't find \"" + System.mapLibraryName(libraryName) + "\""); } String error = doLoad(filename,loader); if (error != null) { throw new UnsatisfiedLinkError(error); } return; } String filename = System.mapLibraryName(libraryName); List<String> candidates = new ArrayList<String>(); String lastError = null; for (String directory : mLibPaths) { String candidate = directory + filename; candidates.add(candidatE); if (IoUtils.canOpenReadOnly(candidatE)) { String error = doLoad(candidate,loader); if (error == null) { return; // We successfully loaded the library. Job done. } lastError = error; } } if (lastError != null) { throw new UnsatisfiedLinkError(lastError); } throw new UnsatisfiedLinkError("Library " + libraryName + " not found; 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的实现。
private String doLoad(String name,ClassLoader loader) { String ldLibraryPath = null; String dexPath = null; if (loader == null) { // We use the given library path for the boot class loader. This is the path // also used in loadLibraryName if loader is null. ldLibraryPath = System.getProperty("java.library.path"); } else if (loader instanceof BaseDexClassLoader) { BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader; ldLibraryPath = dexClassLoader.getLdLibraryPath(); } // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless // of how many ClassLoaders are in the system,but dalvik doesn't support synchronized // internal natives. synchronized (this) { return nativeLoad(name,loader,ldLibraryPath); } }
获得libbrary的路径;
java_lang_Runtime.cc
文件位置:/android6.0.1_r66/art/runtime/native/java_lang_Runtime.cc
static jString Runtime_nativeLoad(jnienv* env,jclass,jString javaFilename,jobject javaLoader,jString javaLdLibraryPathJstr) { ScopedUtfChars filename(env,javaFileName); if (filename.c_str() == nullptr) { return nullptr; } SetLdLibraryPath(env,javaLdLibraryPathJstr); std::string error_msg; { JavaVMExt* vm = Runtime::Current()->GetJavaVM(); bool success = vm->LoadNativeLibrary(env,filename.c_str(),javaLoader,&error_msg); if (success) { return nullptr; } } // Don't let a pending exception from JNI_OnLoad cause a checkJNI issue with NewStringUTF. env->ExceptionClear(); return env->NewStringUTF(error_msg.c_str()); }
nativeLoad()主要做了两件事:
第一件事:利用SetLdLibraryPath()将Java的library的path转换成native的。
第二件事情:调用LoadNativeLibrary进行加载。<关键>
java_vm_ext.cc
位置:/android6.0/art/runtime/java_vm_ext.cc
bool JavaVMExt::LoadNativeLibrary(jnienv* env,const std::string& path,jobject class_loader,std::string* error_msg) { ... const char* 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) << "[No JNI_OnLoad found in \"" << path << "\"]"; was_successful = true; } else {
利用dlsym()调用so文件中的JNI_OnLoad方法,开始so文件的执行。
@H_616_117@
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
以上是大佬教程为你收集整理的深入分析Android加载so文件源码全部内容,希望文章能够帮你解决深入分析Android加载so文件源码所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。