Principio JNI Análisis del código fuente de System.loadLibrary

Prefacio

Este artículo describe el principio de System.loadLibrary.

Flujo de código fuente de la capa Java

System # loadLibrary
-> Runtime # loadLibrary0
-> BaseDexClassLoader # findLibrary
-> DexPathList # findNativeLibrary
-> Runtime # nativeLoad , 走向 c 层 源码

  • Principalmente desde el nativeLibraryPathElementshallazgo en el camino, a través de todos los caminos encontrados para que exista, no existe se lanza UnsatisfiedLinkError. Abra la lógica so en la capa C. Si la capa c devuelve falso, la cadena de error se empalmará y la capa Java también la arrojará después de recibirla UnsatisfiedLinkError.
    image.pngimage.png
  • Puede imprimir nativeLibraryPathElements a través de la reflexión para comprender mejor este fragmento de código
            Field pathListF = BaseDexClassLoader.class.getDeclaredField("pathList");
            pathListF.setAccessible(true);
            ClassLoader dexPathClassLoader = TestSocketFragment.class.getClassLoader();
            Object pathList = pathListF.get(dexPathClassLoader);

            Field nativeLibraryPathElementsF = Class.forName("dalvik.system.DexPathList").getDeclaredField("nativeLibraryPathElements");
            nativeLibraryPathElementsF.setAccessible(true);
            Object nativeLibraryPathElements = nativeLibraryPathElementsF.get(pathList);
            android.util.Log.e("mLogU", nativeLibraryPathElements.toString());

            Object[] arr = (Object[]) nativeLibraryPathElements;
            for (Object o : arr) {
    
    
                android.util.Log.e("mLogU", o.toString());
            }

Proceso de código fuente de capa nativa

Cargue desde la capa c y devuelva falso se lanzará en la capa Java UnsatisfiedLinkError. El proceso es el siguiente:

-> nativeLoad
-> libcore / ojluni / src / main / native / Runtime.c # Runtime_nativeLoad
-> art / openjdkjvm / OpenjdkJvm.cc # JVM_NativeLoad
-> art / runtime / jni / java_vm_ext.cc # LoadNativeLibnrary
-> art / native_loader.cpp # OpenNativeLibrary

bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                  const std::string& path,
                                  jobject class_loader,
                                  std::string* error_msg) {
    
    
  error_msg->clear();

  /*
    *  1. 从缓存中获取该library,
    *  2. 如果存在则比较classLoader是否相同,如果相同则返回成功。不同则返回失败。
    *  3. 不存在则进行加载,并放入缓存中
    */
  SharedLibrary* library;
  Thread* self = Thread::Current();
  {
    
    
    MutexLock mu(self, *Locks::jni_libraries_lock_);
    library = libraries_->Get(path);
  }
  void* class_loader_allocator = nullptr;
  {
    
    
    ScopedObjectAccess soa(env);
    ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);

    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
    if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
    
    
      loader = nullptr;
      class_loader = nullptr;
    }

    // 获取当前classloader的Allocator
    class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
    CHECK(class_loader_allocator != nullptr);
  }
  
  /*
    * The JNI spec says we can't load the same library into more than one class loader.
    * JNI规范要求不能加载相同库到同一个classLoader中
    */
  if (library != nullptr) {
    
    
    // Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
    if (library->GetClassLoaderAllocator() != class_loader_allocator) {
    
    
      // .... 拼接一大堆str
      return false;
    }

    // 已经加载过了,问题不大
    if (!library->CheckOnLoadResult()) {
    
    
      return false;
    }
    return true;
  }

  // dlopen该库,并返回句柄(指针)
  void* handle = android::OpenNativeLibrary(env,
                                            runtime_->GetTargetSdkVersion(),
                                            path_str,
                                            class_loader,
                                            library_path.get(),
                                            &needs_native_bridge,
                                            error_msg);

  if (handle == nullptr) {
    
    
    // 打开失败
    return false;
  }


  bool created_library = false;
  {
    
    
    std::unique_ptr<SharedLibrary> new_library(
        new SharedLibrary(env,
                          self,
                          path,
                          handle,
                          needs_native_bridge,
                          class_loader,
                          class_loader_allocator));

    MutexLock mu(self, *Locks::jni_libraries_lock_);
    library = libraries_->Get(path);
    if (library == nullptr) {
    
      // We won race to get libraries_lock.
      library = new_library.release();
      // 放入缓存
      libraries_->Put(path, library);
      created_library = true;
    }
  }
  if (!created_library) {
    
    
    return library->CheckOnLoadResult();
  }
  
  /*
    * 加载成功,dlsym获取JNI_OnLoad句柄,并调用JNI_OnLoad方法,根据返回值判断设置是否加载成功
    */
  bool was_successful = false;
  void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
  if (sym == nullptr) {
    
    
    was_successful = true;
  } else {
    
    
    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
    int version = (*jni_on_load)(this, nullptr);

    if (version == JNI_ERR) {
    
    
      StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
    } else if (JavaVMExt::IsBadJniVersion(version)) {
    
    
      StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
                    path.c_str(), version);
    } else {
    
    
      was_successful = true;
    }
  }

  library->SetResult(was_successful);
  return was_successful;
}

Conclusión

Se puede ver en el código fuente que System # loadLibrary solo llamará al JNI_OnLoadmétodo una vez en circunstancias normales ;
entre ellos, SharedLibrary llamará a dlclose en el destructor para cerrar la biblioteca;
y en el documento oficial JNI tips, se puede ver que Por lo general, se coloca en un bloque de código estático para cargar. El ciclo de vida de este está vinculado al ciclo de vida de la clase.
Este artículo clasifica el proceso del código fuente del sistema # loadLibrary, corríjame si hay algún error.

Supongo que te gusta

Origin blog.csdn.net/u014099894/article/details/111655768
Recomendado
Clasificación