美文网首页
三、System.loadLibrary("fmlive")源码

三、System.loadLibrary("fmlive")源码

作者: ChiangCMBA | 来源:发表于2018-04-07 21:04 被阅读0次

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;
}

相关文章

网友评论

      本文标题:三、System.loadLibrary("fmlive")源码

      本文链接:https://www.haomeiwen.com/subject/wrhuhftx.html