Android JNI之System.loadLibrary()流程

文档目的

JNI全称Java Native Interface,相当于java语言和C/C++语言打交道的桥梁。主要是用来在java语言中加载lib库。然后就可以在java中使用native中定义的方法。

用法如下:


static {
    System.loadLibrary("TestJni");
}
private static native String getResult();

本文主要来跟踪一下System.loadLibrary的调用流程,不对jni文件实现做探究。


Library查找过程

以Android N代码为例,System.loadLibrary源码位于:

./libcore/ojluni/src/main/java/java/lang/System.java

    /**
     * Loads the system library specified by the <code>libname</code>
     * argument. The manner in which a library name is mapped to the
     * actual system library is system dependent.
     * <p>
     * The call <code>System.loadLibrary(name)</code> is effectively
     * equivalent to the call
     * <blockquote><pre>
     * Runtime.getRuntime().loadLibrary(name)
     * </pre></blockquote>
     *
     * @param      libname   the name of the library.
     * @exception  SecurityException  if a security manager exists and its
     *             <code>checkLink</code> method doesn't allow
     *             loading of the specified dynamic library
     * @exception  UnsatisfiedLinkError  if the library does not exist.
     * @exception  NullPointerException if <code>libname</code> is
     *             <code>null</code>
     * @see        java.lang.Runtime#loadLibrary(java.lang.String)
     * @see        java.lang.SecurityManager#checkLink(java.lang.String)
     */
    public static void loadLibrary(String libname) {
        Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
    }

可以看到然后又调用了Runtime.java的loadLibrary0方法:

./libcore/ojluni/src/main/java/java/lang/Runtime.java

    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) {
            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 : getLibPaths()) {
            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);
    }

这个方法主要有两个作用:

    1. 找到lib的全称

    2. 调用doLoad加载lib库

首先会判断loader 不为空来执行上面两步,如果为空,则换个方法继续执行上面两步。

这个方法的第一个参数是ClassLoader,是获取系统的loader,一般情况下这个值不会为空,是通过ContextImpl.java中的getClassLoader来生成的,且实例化为PathClassLoader。PathClassLoader继承BaseDexClassLoader,BaseDexClassLoader继承ClassLoader。

所以loader.findLibrary(libraryName)最终是调用BaseDexClassLoader中的findLibrary。

代码位于:./libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java

    @Override
 
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android JNI 中引用 .a 静态文件的步骤如下: 1. 在 Android.mk 文件中指定静态库的路径和名称。例如: LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := native-lib LOCAL_SRC_FILES := native-lib.cpp LOCAL_LDLIBS := -llog LOCAL_STATIC_LIBRARIES := my-static-lib include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := my-static-lib LOCAL_SRC_FILES := libmy-static-lib.a include $(PREBUILT_STATIC_LIBRARY) 2. 将静态库文件放置在 jni 目录下,并在 Android.mk 文件中指定静态库的路径和名称。 3. 在 C++ 代码中使用 #include 指令引用静态库的头文件。 4. 在 C++ 代码中使用 extern "C" 指令声明静态库中的函数。 5. 在 C++ 代码中使用 System.loadLibrary() 函数加载动态库,并调用静态库中的函数。 下面是一个示例代码: #include <jni.h> #include <string.h> #include <android/log.h> #include "my-static-lib.h" extern "C" JNIEXPORT jstring JNICALL Java_com_example_native_1lib_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) { const char* message = my_static_lib_function(); return env->NewStringUTF(message); } 注意:在 Android Studio 中使用 CMake 构建系统时,可以在 CMakeLists.txt 文件中使用 target_link_libraries() 函数引用 .a 静态文件。例如: add_library(my-static-lib STATIC IMPORTED) set_target_properties(my-static-lib PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/my-static-lib.a) target_link_libraries(native-lib my-static-lib)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值