Based on Android7.1.0
static {
System.loadLibrary("fmlive");#1
}
1.System#loadLibrary
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);#2
}
2. Runtime#loadLibrary0
synchronized void loadLibrary0(ClassLoader loader, String libname) {
if (libname.indexOf((int)File.separatorChar) != -1) {throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
String libraryName = libname;
if (loader != null) {
//查找动态链接库文件名称; PathClassLoader extends BaseDexClassLoader;
String filename = loader.findLibrary(libraryName);#3
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);#4//加载动态链接库
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
String filename = System.mapLibraryName(libraryName);#5
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : getLibPaths()) {//#7
String candidate = directory + filename;
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {//
String error = doLoad(candidate, loader);//#8
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);
}
3.PathClassLoader#findLibrary()
/**
* Returns the absolute path name of a native library. The VM invokes this
* method to locate the native libraries that belong to classes loaded with
* this class loader. If this method returns <tt>null</tt>, the VM
* searches the library along the path specified as the
* "<tt>java.library.path</tt>" property. </p>
*
* @param libname
* The library name
* @return The absolute path of the native library
*/
public class PathClassLoader extends BaseDexClassLoader {
> public PathClassLoader(String dexPath, ClassLoader parent) {
>
> super(dexPath, null, null, parent);
>
> }
>
> public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) {
>
> super(dexPath, null, libraryPath, parent);
>
> }
}
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
> public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
>
> > super(parent);
> >
> > //
>
> > this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
>
> }
>
> @Override
>
> public String findLibrary(String name) {
>
> return pathList.findLibrary(name);#6
>
> }
......
}
4.DexPathList#findLibrary()
final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
/** class definition context */
private final ClassLoader definingContext;
/**
* List of dex/resource (class path) elements.
* Should be called pathElements, but the Facebook app uses reflection
* to modify 'dexElements' ([http://b/7726934](http://b/7726934)).
*/
private final Element[] dexElements;
/** List of native library path elements. */
private final Element[] nativeLibraryPathElements;
/** List of application native library directories. */
private final List<File> nativeLibraryDirectories;
/** List of system native library directories. */
private final List<File> systemNativeLibraryDirectories;
/**
* Constructs an instance.
*
* @param definingContext the context in which any as-yet unresolved
* classes should be defined
* @param dexPath list of dex/resource path elements, separated by
* {@code File.pathSeparator}
* @param libraryPath list of native library directory path elements,
* separated by {@code File.pathSeparator}
* @param optimizedDirectory directory where optimized {@code .dex} files
* should be found and written to, or {@code null} to use the default
* system directory for same
*/
public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) {
> ......
>
> this.definingContext = definingContext;
>
> ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
>
> // save dexPath for BaseDexClassLoader
>
> this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
>
> // Native libraries may exist in both the system and
>
> // application library paths, and we use this search order:
>
> //
>
> // 1\. This class loader's library path for application libraries (libraryPath):
>
> // 1.1\. Native library directories
>
> // 1.2\. Path to libraries in apk-files
>
> // 2\. The VM's library path from the system property for system libraries
>
> // also known as java.library.path
>
> //
>
> // This order was reversed prior to Gingerbread; see [http://b/2933456](http://b/2933456).
>
> this.nativeLibraryDirectories = splitPaths(libraryPath, false);
>
> this.systemNativeLibraryDirectories =splitPaths(System.getProperty("java.library.path"), true);
>
> List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
>
> allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
>
> this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null, suppressedExceptions);
>
> ......
}
/**
* Splits the given dex path string into elements using the path
* separator, pruning out any elements that do not refer to existing
* and readable files. (That is, directories are not included in the
* result.)
*/
private static List<File> splitDexPath(String path) {
return splitPaths(path, false);
}
/**
* Splits the given path strings into file elements using the path
* separator, combining the results and filtering out elements
* that don't exist, aren't readable, or aren't either a regular
* file or a directory (as specified). Either string may be empty
* or {@code null}, in which case it is ignored. If both strings
* are empty or {@code null}, or all elements get pruned out, then
* this returns a zero-element list.
*/
private static List<File> splitPaths(String searchPath, boolean directoriesOnly) {
List<File> result = new ArrayList<>();
if (searchPath != null) {
//File.pathSeparator-->On UNIX systems, this character is ':';
// on Microsoft Windows systems it is ';'.
> for (String path : searchPath.split(File.pathSeparator)) {
>
> > > if (directoriesOnly) {
>
> > > > try {
>
> > > > StructStat sb = Libcore.os.stat(path);
>
> > > > > if (!S_ISDIR(sb.st_mode)) {
>
> > > > > continue;
>
> > > > > }
>
> > > > } catch (ErrnoException ignored) {
>
> > > > continue;
>
> > > > }
>
> > > }
>
> > > result.add(new File(path));
>
> > }
> }
return result;
}
/**
* Makes an array of dex/resource path elements, one per element of
* the given array.
*/
private static Element[] makePathElements(List<File> files, File optimizedDirectory,
> List<IOException> suppressedExceptions) {
>
> List<Element> elements = new ArrayList<>();
>
> /*
>
> * Open all files and load the (direct or contained) dex files
>
> * up front.
>
> */
>
> for (File file : files) {
> > File zip = null;
>
> > File dir = new File("");
>
> > DexFile dex = null;
>
> > String path = file.getPath();
>
> > String name = file.getName();
> > if (path.contains(zipSeparator)) {
>
> > > String split[] = path.split(zipSeparator, 2);
>
> > > zip = new File(split[0]);
>
> > > dir = new File(split[1]);
>
> > } else if (file.isDirectory()) {
>
> > > // We support directories for looking up resources and native libraries.
>
> > > // Looking up resources in directories is useful for running libcore tests.
>
> > > elements.add(new Element(file, true, null, null));
>
> > } else if (file.isFile()) {
>
> > > if (name.endsWith(DEX_SUFFIX)) {
>
> > > > // Raw dex file (not inside a zip/jar).
>
> > > > try {
>
> > > > dex = loadDexFile(file, optimizedDirectory);
>
> > > > } catch (IOException ex) {
>
> > > > System.logE("Unable to load dex file: " + file, ex);
>
> > > > }
>
> > > } else {
>
> > > > zip = file;
>
> > > > try {
>
> > > > dex = loadDexFile(file, optimizedDirectory);
>
> > > > } catch (IOException suppressed) {
>
> > > }
>
> > > }
>
> > } else {
>
> > System.logW("ClassLoader referenced unknown path: " + file);
>
> > }
> > if ((zip != null) || (dex != null)) {
>
> > elements.add(new Element(dir, false, zip, dex));
>
> > }
> }
>
> return elements.toArray(new Element[elements.size()]);
}
/**
* Constructs a {@code DexFile} instance, as appropriate depending
* on whether {@code optimizedDirectory} is {@code null}.
*/
private static DexFile loadDexFile(File file, File optimizedDirectory)throws IOException {
> if (optimizedDirectory == null) {
>
> return new DexFile(file);
>
> } else {
>
> String optimizedPath = optimizedPathFor(file, optimizedDirectory);
>
> return DexFile.loadDex(file.getPath(), optimizedPath, 0);
>
> }
}
/**
* Finds the named native code library on any of the library
* directories pointed at by this instance. This will find the
* one in the earliest listed directory, ignoring any that are not
* readable regular files.
*
* @return the complete path to the library or {@code null} if no
* library was found
*/
public String findLibrary(String libraryName) {
> String fileName = System.mapLibraryName(libraryName);
>
> for (Element element : nativeLibraryPathElements) {
> > String path = element.findNativeLibrary(fileName);
>
> > if (path != null) {
>
> > return path;
>
> > }
> }
>
> return null;
}
/**
* Element of the dex/resource file path
*/
static class Element {
private final File dir;
private final boolean isDirectory;
private final File zip;
private final DexFile dexFile;
private ZipFile zipFile;
private boolean initialized;
public Element(File dir, boolean isDirectory, File zip, DexFile dexFile) {
> this.dir = dir;
>
> this.isDirectory = isDirectory;
>
> this.zip = zip;
>
> this.dexFile = dexFile;
}
> public synchronized void maybeInit() {
>
> if (initialized) {
>
> return;
>
> }
>
> initialized = true;
>
> if (isDirectory || zip == null) {
>
> return;
>
> }
>
> try {
>
> zipFile = new ZipFile(zip);
>
> } catch (IOException ioe) {
>
> zipFile = null;
>
> }
>
> }
>
> public String findNativeLibrary(String name) {
>
> maybeInit();
>
> if (isDirectory) {
>
> String path = new File(dir, name).getPath();
>
> if (IoUtils.canOpenReadOnly(path)) {
>
> return path;
>
> }
>
> } else if (zipFile != null) {
>
> String entryName = new File(dir, name).getPath();
>
> if (isZipEntryExistsAndStored(zipFile, entryName)) {
>
> return zip.getPath() + zipSeparator + entryName;
>
> }
>
> }
>
> return null;
>
> }
>
> public synchronized void maybeInit() {
>
> if (initialized) {
>
> return;
>
> }
>
> initialized = true;
>
> if (isDirectory || zip == null) {
>
> return;
>
> }
>
> try {
>
> zipFile = new ZipFile(zip);
>
> } catch (IOException ioe) {
>
> zipFile = null;
>
> }
>
> }
}
}
5.Runtime#getLibPaths()
private String[] getLibPaths() {
> if (mLibPaths == null) {
> > synchronized(this) {
>
> > > if (mLibPaths == null) {
>
> > > mLibPaths = initLibPaths();
>
> > > }
>
> > }
> }
>
> return mLibPaths;
}
private static String[] initLibPaths() {
> String javaLibraryPath = System.getProperty("java.library.path");// /System/lib: /Vendor/lib
>
> if (javaLibraryPath == null) {
>
> return EmptyArray.STRING;
>
> }
>
> String[] paths = javaLibraryPath.split(":");
>
> // Add a '/' to the end of each directory so we don't have to do it every time.
>
> for (int i = 0; i < paths.length; ++i) {
>
> if (!paths[i].endsWith("/")) {
>
> paths[i] += "/";
>
> }
>
> }
>
> return paths;
}
6.System.mapLibraryName(libraryName)
/**
* Maps a library name into a platform-specific string representing
* a native library.
*
* @param libname the name of the library.
* @return a platform-dependent native library name.
*/
public static native String mapLibraryName(String libname);
7.android-7.1.0_r1\libcore\ojluni\src\main\native\System.c#System_mapLibraryName
#define JNI_LIB_PREFIX "lib"
#define JNI_LIB_SUFFIX ".so"
JNIEXPORT jstring JNICALL System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
{
> int len;
>
> int prefix_len = (int) strlen(JNI_LIB_PREFIX);
>
> int suffix_len = (int) strlen(JNI_LIB_SUFFIX);
>
> jchar chars[256];
>
> if (libname == NULL) {
>
> JNU_ThrowNullPointerException(env, 0);
>
> return NULL;
>
> }
>
> len = (*env)->GetStringLength(env, libname);
>
> if (len > 240) {
>
> JNU_ThrowIllegalArgumentException(env, "name too long");
>
> return NULL;
>
> }
>
> cpchars(chars, JNI_LIB_PREFIX, prefix_len);
>
> (*env)->GetStringRegion(env, libname, 0, len, chars + prefix_len);
>
> len += prefix_len;
>
> cpchars(chars + len, JNI_LIB_SUFFIX, suffix_len);
>
> len += suffix_len;
>
> return (*env)->NewString(env, chars, len);
}
8.Runtime#doLoad(filename, loader)
private String doLoad(String name, ClassLoader loader) {
> // LD_LIBRARY_PATH是Linux[环境变量](https://baike.baidu.com/item/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F)名,该环境变量主要用于指定查找共享库([动态链接](https://baike.baidu.com/item/%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5)库)时除了默认路径之外的其他路径。
>
> //Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
>
> // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.
>
> // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
>
> // libraries with no dependencies just fine, but an app that has multiple libraries that
>
> // depend on each other needed to load them in most-dependent-first order.
>
> // We added API to Android's dynamic linker so we can update the library path used for
>
> // the currently-running process. We pull the desired path out of the ClassLoader here
>
> // and pass it to nativeLoad so that it can call the private dynamic linker API.
>
> // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
>
> // beginning because multiple apks can run in the same process and third party code can
>
> // use its own BaseDexClassLoader.
>
> // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
>
> // dlopen(3) calls made from a .so's JNI_OnLoad to work too.
>
> // So, find out what the native library search path is for the ClassLoader in question...
>
> String librarySearchPath = null;
>
> if (loader != null && loader instanceof BaseDexClassLoader) {
>
> BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
>
> librarySearchPath = 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, librarySearchPath);
>
> }
}
9.BaseDexClassLoader#getLdLibraryPath()
public String getLdLibraryPath() {
> StringBuilder result = new StringBuilder();
>
> for (File directory : pathList.getNativeLibraryDirectories()) {
>
> if (result.length() > 0) {
>
> result.append(':');
>
> }
>
> result.append(directory);
>
> }
>
> return result.toString();
}
// TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
private static native String nativeLoad(String filename, ClassLoader loader, String librarySearchPath);
对应于native函数:
10.android-7.1.0_r1\libcore\ojluni\src\main\native\Runtime.c#Runtime_nativeLoad
JNIEXPORT jstring JNICALL Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader, jstring javaLibrarySearchPath)
{
return JVM_NativeLoad(env, javaFilename, javaLoader, javaLibrarySearchPath);
}
11.android-7.1.0_r1\art\runtime\openjdkjvm\OpenjdkJvm.cc#JVM_NativeLoad
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, jobject javaLoader,
jstring javaLibrarySearchPath) {
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == NULL) {
return NULL;
}
std::string error_msg;
{
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader,javaLibrarySearchPath, &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());
}
// A smart pointer that provides read-only access to a Java string's UTF chars.
class ScopedUtfChars {
> public:
>
> ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
> > if (s == NULL) {
>
> > utf_chars_ = NULL;
>
> > jniThrowNullPointerException(env, NULL);
>
> > } else {
>
> > utf_chars_ = env->GetStringUTFChars(s, NULL);
>
> > }
> }
>
> ~ScopedUtfChars() {
> > if (utf_chars_) {
>
> > env_->ReleaseStringUTFChars(string_, utf_chars_);
>
> > }
> }
>
> ......
>
> private:
>
> JNIEnv* env_;
>
> jstring string_;
>
> const char* utf_chars_;
>
> const char* c_str() const {
>
> return utf_chars_;
>
> }
>
> ......
};
Last but the most important:
12.android-7.1.0_r1\art\runtime\Java_vm_ext.cc#LoadNativeLibrary
std::unique_ptr<Libraries> libraries_;
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader, jstring library_path, std::string* error_msg) {
error_msg->clear();
// See if we've already loaded this library. If we have, and the class loader
// matches, return successfully without doing anything.
// TODO: for better results we should canonicalize the pathname (or even compare
// inodes). This implementation is fine if everybody is using System.loadLibrary.
SharedLibrary* library;
Thread* self = Thread::Current();
{
// TODO: move the locking (and more of this logic) into Libraries.
MutexLock mu(self, *Locks::jni_libraries_lock_);
library = libraries_->Get(path);
}
void* class_loader_allocator = nullptr;
{
ScopedObjectAccess soa(env);
// As the incoming class loader is reachable/alive during the call of this function,
// it's okay to decode it without worrying about unexpectedly marking it alive.
mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(class_loader);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (class_linker->IsBootClassLoader(soa, loader)) {
loader = nullptr;
class_loader = nullptr;
}
class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader);
CHECK(class_loader_allocator != nullptr);
}
if (library != nullptr) {
// Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
if (library->GetClassLoaderAllocator() != class_loader_allocator) {
// The library will be associated with class_loader. The JNI
// spec says we can't load the same library into more than one
// class loader.
StringAppendF(error_msg, "Shared library \"%s\" already opened by " "ClassLoader %p; can't open in ClassLoader %p",
path.c_str(), library->GetClassLoader(), class_loader);
LOG(WARNING) << error_msg;
return false;
}
VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
<< " ClassLoader " << class_loader << "]";
if (!library->CheckOnLoadResult()) {
StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt ""to load \"%s\"", path.c_str());
return false;
}
return true;
}
// Open the shared library. Because we're using a full path, the system
// doesn't have to search through LD_LIBRARY_PATH. (It may do so to
// resolve this library's dependencies though.)
// Failures here are expected when java.library.path has several entries
// and we have to hunt for the lib.
// Below we dlopen but there is no paired dlclose, this would be necessary if we supported
// class unloading. Libraries will only be unloaded when the reference count (incremented by
// dlopen) becomes zero from dlclose.
Locks::mutator_lock_->AssertNotHeld(self);
const char* path_str = path.empty() ? nullptr : path.c_str();
void* handle = android::OpenNativeLibrary(env, runtime_->GetTargetSdkVersion(), path_str,
class_loader, library_path);
bool needs_native_bridge = false;
if (handle == nullptr) {
if (android::NativeBridgeIsSupported(path_str)) {
handle = android::NativeBridgeLoadLibrary(path_str, RTLD_NOW);
needs_native_bridge = true;
}
}
VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";
if (handle == nullptr) {
*error_msg = dlerror();
VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
return false;
}
if (env->ExceptionCheck() == JNI_TRUE) {
LOG(ERROR) << "Unexpected exception:";
env->ExceptionDescribe();
env->ExceptionClear();
}
// Create a new entry.
// TODO: move the locking (and more of this logic) into Libraries.
bool created_library = false;
{
// Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
std::unique_ptr<SharedLibrary> new_library(
new SharedLibrary(env, self, path, handle, 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) {
LOG(INFO) << "WOW: we lost a race to add shared library: "<< "\"" << path << "\" ClassLoader=" << class_loader;
return library->CheckOnLoadResult();
}
VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
bool was_successful = false;
void* sym;
if (needs_native_bridge) {
library->SetNeedsNativeBridge();
}
//查找JNI_OnLoad函数
sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
was_successful = true;
} else {
// Call JNI_OnLoad. We have to override the current class
// loader, which will always be "null" since the stuff at the
// top of the stack is around Runtime.loadLibrary(). (See
// the comments in the JNI FindClass function.)
ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
self->SetClassLoaderOverride(class_loader);
VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
int version = (*jni_on_load)(this, nullptr);
if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
fault_manager.EnsureArtActionInFrontOfSignalChain();
}
self->SetClassLoaderOverride(old_class_loader.get());
if (version == JNI_ERR) {
StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
} else if (IsBadJniVersion(version)) {
StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
path.c_str(), version);
// It's unwise to call dlclose() here, but we can mark it
// as bad and ensure that future load attempts will fail.
// We don't know how far JNI_OnLoad got, so there could
// be some partially-initialized stuff accessible through
// newly-registered native method calls. We could try to
// unregister them, but that doesn't seem worthwhile.
} else {
was_successful = true;
}
VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")<< " from JNI_OnLoad in \"" << path << "\"]";
}
library->SetResult(was_successful);
return was_successful;
}
该方法大致做以下几步:
1) 调用android::OpenNativeLibrary打开lib库
2) 调用library->FindSymbol("JNI_OnLoad", nullptr)找到lib中的JNI_OnLoad这个方法
3) 执行JNI_OnLoad方法。
13.SharedLibrary#CheckOnLoadResult()
// Check the result of an earlier call to JNI_OnLoad on this library.
/ If the call has not yet finished in another thread, wait for it.
//
bool CheckOnLoadResult()
REQUIRES(!jni_on_load_lock_) {
Thread* self = Thread::Current();
bool okay;
{
MutexLock mu(self, jni_on_load_lock_);
if (jni_on_load_thread_id_ == self->GetThreadId()) {
// Check this so we don't end up waiting for ourselves. We need to return "true" so the
// caller can continue.
LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\"";
okay = true;
} else {
while (jni_on_load_result_ == kPending) {
VLOG(jni) << "[" << *self << " waiting for \"" << path_ << "\" " << "JNI_OnLoad...]";
jni_on_load_cond_.Wait(self);
}
okay = (jni_on_load_result_ == kOkay);
VLOG(jni) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "<< (okay ? "succeeded" : "failed") << "]";
}
}
return okay;
}
网友评论