深入分析Android加载so文件源码

深入分析Android加载so文件源码,第1张

概述Android系统中使用ndk进行编程,有很多的好处(Java的跨平台特性导致其本地交互的能力不够强大,一些和 *** 作系统相关的特性Java无法完成;代码的保护:由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大;可

AndroID系统中使用ndk进行编程,有很多的好处(Java的跨平台特性导致其本地交互的能力不够强大,一些和 *** 作系统相关的特性Java无法完成;代码的保护:由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大;可以方便地使用C/C++开源库;便于移植,用C/C++写的库可以方便在其他平台上再次使用;提供程序在某些特定情形下的执行效率,但是并不能明显提升AndroID程序的性能)。

要使用ndk进行编程,在Java层就必须要对so进行加载。Java层加载so的函数有两个:

System.load(String pathname)System.loadlibraray(String libname)

两个函数的区别就是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中:

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()方法。

第一步目前我不关心,不去深究。主要看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的路径;

调用native函数nativeLoad()进行加载加载。

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 {

利用dlopen()打开so文件,得到函数的指针

利用dlsym()调用so文件中的JNI_OnLoad方法,开始so文件的执行。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

您可能感兴趣的文章:Android Studio如何快速导入jar和.so文件浅谈Android Studio如何Debug对应so文件C/C++代码实例详解android studio如何导入.so文件的方法Android Studio工程引用第三方so文件的方法Android Studio导入so文件到项目中的实例详解详解Android studio中正确引入so文件的方法 总结

以上是内存溢出为你收集整理的深入分析Android加载so文件源码全部内容,希望文章能够帮你解决深入分析Android加载so文件源码所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1142882.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存