深入理解android卷1—java调用native代码

来源:互联网 发布:网络故障诊断工具作用 编辑:程序博客网 时间:2024/06/05 18:37

  • frameworks中关于media的代码
  • java和jni函数对应关系
  • SystemloadLibrary
  • libmedia_jniso


最近在阅读深入理解android卷1,把一些重要的思路记录下来,当然是书中没有细述的一些东西,便于后续翻阅。该书中jni是借助media相关代码展开。

frameworks中关于media的代码

build/core/pathmap.mk中hard code了frameworks的代码路径,其中media相关代码在frameworks/base/media下,

FRAMEWORKS_BASE_SUBDIRS := \    $(addsuffix /java, \        core \        graphics \        location \        media \        opengl \        sax \        telephony \        wifi \        vpn \        keystore \        voip \     )## A version of FRAMEWORKS_BASE_SUBDIRS that is expanded to full paths from# the root of the tree.  This currently needs to be here so that other libraries# and apps can find the .aidl files in the framework, though we should really# figure out a better way to do this.#FRAMEWORKS_BASE_JAVA_SRC_DIRS := \    $(addprefix frameworks/base/,$(FRAMEWORKS_BASE_SUBDIRS))

java和jni函数对应关系

在java代码中,如果调用的函数有native修饰,则是要调用native函数,在framworks/base/media/java/android/media/MediaScanner.java中,

private static native final void native_init();

则native_init即是native函数实现的,如何将java中函数和native对应起来?
例如,MediaScanner.java中native_init函数,全路径为android.media.MediaScanner.native_init函数,则native的函数名为将.换为下划线,即android_media_MediaScanner_native_init()但是也不是一定的,如果找不到,一般对应的Native函数一般都在形如“包名_类名”的cpp或者c中,即android_media_MediaScanner.cpp。

此外,找JNI层对应函数时,还可以通过搜索代码 包名/类名 的方式,例如MediaScanner.java中有native函数,包名为android/media,则可以搜索android/media/MediaScanner,因为在JNI函数动态注册的时候会调用AndroidRuntime::registerNativeMethods(),这个函数的第二个参数为形如android/media/MediaScanner的类名,表明这是类android/media/MediaScanner中调用的native函数。

System.loadLibrary

在framworks/base/media/java/android/media/MediaScanner.java类中,调用System.loadLibrary来加载libmedia_jni.so

public class MediaScanner{    static {        System.loadLibrary("media_jni");        native_init();    }    .............}

那么需要关注两个部分,一个就是System.loadLibrary函数,另一个就是media_jni库,当然在android中库的全称为libmedia_jni.so,先介绍System.loadLibrary,
在libcore\luni\src\main\java\java\lang\System.java中,

    public static void loadLibrary(String libName) {        SecurityManager smngr = System.getSecurityManager();        if (smngr != null) {            smngr.checkLink(libName);        }        Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());    }

其实是调用了Runtime中的loadLibrary,在libcore\luni\src\main\java\java\lang\Runtime.java中,

 /*     * Loads and links a library without security checks.     */    void loadLibrary(String libraryName, ClassLoader loader) {        if (loader != null) {            String filename = loader.findLibrary(libraryName);            if (filename == null) {                throw new UnsatisfiedLinkError("Couldn't load " + libraryName + ": " +                        "findLibrary returned null");            }            String error = nativeLoad(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 (new File(candidate).exists()) {                String error = nativeLoad(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);    }

都会调用nativeLoad(filename, loader)函数,去完成库的加载,而libcore\luni\src\main\java\java\lang\Runtime.java中,nativeLoad是一个native函数,

private static native String nativeLoad(String filename, ClassLoader loader);

那么我们就要去找native函数,按照前面的方法,nativeLoad的native函数应该名为java_lang_Runtime_nativeLoad()函数,但是没找到。所以又去java_lang_Runtime.c或者cpp中找,最终在java_lang_Runtime.c找到了。

 const DalvikNativeMethod dvm_java_lang_Runtime[] = {    { "freeMemory",          "()J",        Dalvik_java_lang_Runtime_freeMemory },    { "gc",                 "()V",        Dalvik_java_lang_Runtime_gc },    { "availableProcessors", "()I",        Dalvik_java_lang_Runtime_availableProcessors },    { "maxMemory",          "()J",        Dalvik_java_lang_Runtime_maxMemory },    { "nativeExit",         "(IZ)V",        Dalvik_java_lang_Runtime_nativeExit },    { "nativeLoad",         "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;",        Dalvik_java_lang_Runtime_nativeLoad },    { "runFinalization",    "(Z)V",        Dalvik_java_lang_Runtime_runFinalization },    { "totalMemory",          "()J",        Dalvik_java_lang_Runtime_totalMemory },    { NULL, NULL, NULL },    };

既是继续调用Dalvik_java_lang_Runtime_nativeLoad函数,

static void Dalvik_java_lang_Runtime_nativeLoad(const u4* args,    JValue* pResult){    StringObject* fileNameObj = (StringObject*) args[0];    Object* classLoader = (Object*) args[1];    char* fileName = NULL;    StringObject* result = NULL;    char* reason = NULL;    bool success;    assert(fileNameObj != NULL);    fileName = dvmCreateCstrFromString(fileNameObj);    success = dvmLoadNativeCode(fileName, classLoader, &reason);    if (!success) {        const char* msg = (reason != NULL) ? reason : "unknown failure";        result = dvmCreateStringFromCstr(msg);        dvmReleaseTrackedAlloc((Object*) result, NULL);    }    free(reason);    free(fileName);    RETURN_PTR(result);}

继续调用dvmLoadNativeCode(),

bool dvmLoadNativeCode(const char* pathName, Object* classLoader,        char** detail){    SharedLib* pEntry;    void* handle;    bool verbose;    /* reduce noise by not chattering about system libraries */    //如果不是以/system或者/vendor开头    verbose = !!strncmp(pathName, "/system", sizeof("/system")-1);    verbose = verbose && !!strncmp(pathName, "/vendor", sizeof("/vendor")-1);    if (verbose)        LOGD("Trying to load lib %s %p\n", pathName, classLoader);    *detail = NULL;    /*     * See if we've already loaded it.  If we have, and the class loader     * matches, return successfully without doing anything.     */    pEntry = findSharedLibEntry(pathName);    if (pEntry != NULL) {        if (pEntry->classLoader != classLoader) {            LOGW("Shared lib '%s' already opened by CL %p; can't open in %p\n",                pathName, pEntry->classLoader, classLoader);            return false;        }        if (verbose) {            LOGD("Shared lib '%s' already loaded in same CL %p\n",                pathName, classLoader);        }        if (!checkOnLoadResult(pEntry))            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.     *     * The current version of the dynamic linker prints detailed information     * about dlopen() failures.  Some things to check if the message is     * cryptic:     *   - make sure the library exists on the device     *   - verify that the right path is being opened (the debug log message     *     above can help with that)     *   - check to see if the library is valid (e.g. not zero bytes long)     *   - check config/prelink-linux-arm.map to ensure that the library     *     is listed and is not being overrun by the previous entry (if     *     loading suddenly stops working on a prelinked library, this is     *     a good one to check)     *   - write a trivial app that calls sleep() then dlopen(), attach     *     to it with "strace -p <pid>" while it sleeps, and watch for     *     attempts to open nonexistent dependent shared libs     *     * This can execute slowly for a large library on a busy system, so we     * want to switch from RUNNING to VMWAIT while it executes.  This allows     * the GC to ignore us.     */    Thread* self = dvmThreadSelf();    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);    //打开库    handle = dlopen(pathName, RTLD_LAZY);    dvmChangeStatus(self, oldStatus);    if (handle == NULL) {        *detail = strdup(dlerror());        return false;    }    /* create a new entry */    SharedLib* pNewEntry;    pNewEntry = (SharedLib*) calloc(1, sizeof(SharedLib));    pNewEntry->pathName = strdup(pathName);    pNewEntry->handle = handle;    pNewEntry->classLoader = classLoader;    dvmInitMutex(&pNewEntry->onLoadLock);    pthread_cond_init(&pNewEntry->onLoadCond, NULL);    pNewEntry->onLoadThreadId = self->threadId;    /* try to add it to the list */    SharedLib* pActualEntry = addSharedLibEntry(pNewEntry);    if (pNewEntry != pActualEntry) {        LOGI("WOW: we lost a race to add a shared lib (%s CL=%p)\n",            pathName, classLoader);        freeSharedLibEntry(pNewEntry);        return checkOnLoadResult(pActualEntry);    } else {        if (verbose)            LOGD("Added shared lib %s %p\n", pathName, classLoader);        bool result = true;        void* vonLoad;        int version;    //在库中找到JNI_OnLoad符号,即函数        vonLoad = dlsym(handle, "JNI_OnLoad");        if (vonLoad == NULL) {            LOGD("No JNI_OnLoad found in %s %p, skipping init\n",                pathName, classLoader);        } 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.)             */            OnLoadFunc func = vonLoad;            Object* prevOverride = self->classLoaderOverride;            self->classLoaderOverride = classLoader;            oldStatus = dvmChangeStatus(self, THREAD_NATIVE);            LOGV("+++ calling JNI_OnLoad(%s)\n", pathName);            //执行JNI_OnLoad函数            version = (*func)(gDvm.vmList, NULL);            dvmChangeStatus(self, oldStatus);            self->classLoaderOverride = prevOverride;            if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 &&                version != JNI_VERSION_1_6)            {                LOGW("JNI_OnLoad returned bad version (%d) in %s %p\n",                    version, pathName, classLoader);                /*                 * 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.                 */                result = false;            } else {                LOGV("+++ finished JNI_OnLoad %s\n", pathName);            }        }        if (result)            pNewEntry->onLoadResult = kOnLoadOkay;        else            pNewEntry->onLoadResult = kOnLoadFailed;        pNewEntry->onLoadThreadId = 0;        /*         * Broadcast a wakeup to anybody sleeping on the condition variable.         */        dvmLockMutex(&pNewEntry->onLoadLock);        pthread_cond_broadcast(&pNewEntry->onLoadCond);        dvmUnlockMutex(&pNewEntry->onLoadLock);        return result;    }}

那么从上面的代码我们能看出,System.loadLibrary("media_jni")最终会去调用库libmedia_jni.so中的JNI_OnLoad()函数。

libmedia_jni.so

如何找这个库呢?当然是从makefile中入手,在makefile中搜索media_jni,
即,

LOCAL_MODULE:= libmedia_jniinclude $(BUILD_SHARED_LIBRARY)

前面在将介绍build/envsetup.sh中讲到,这个脚本提供了很多便捷的搜索函数,例如cgrep等,我增加了一个在makefile中搜索的函数,找相关模块非常方便。在envsetup.sh中模仿jgrep,

function mkgrep(){    find . -type f -name "*\.mk" -print0 | xargs -0 grep --color -n "$@"}

因为media的相关代码都在frameworks\base\media下,所以很容易搜索到,

$ mkgrep media_jni./build/core/user_tags.mk:242:  libmedia_jni \./build/CleanSpec.mk:48:$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libmedia_jni_intermediates)./frameworks/base/media/jni/Android.mk:45:LOCAL_MODULE:= libmedia_jni

在LOCAL_SRC_FILES中搜索,找到JNI_OnLoad就在frameworks\base\media\jni\android_media_MediaPlayer.cpp中,

LOCAL_SRC_FILES:= \    android_media_MediaPlayer.cpp \    android_media_MediaRecorder.cpp \    android_media_MediaScanner.cpp \    android_media_MediaMetadataRetriever.cpp \    android_media_ResampleInputStream.cpp \    android_media_MediaProfiles.cpp \    android_media_AmrInputStream.cpp

关于JNI_OnLoad()函数中,JNI函数的动态注册这里不写了,书中很详细。

0 0
原创粉丝点击