深入理解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函数的动态注册这里不写了,书中很详细。
- 深入理解android卷1—java调用native代码
- 深入理解android:卷1
- Zygote (深入理解android 卷1)
- 深入理解Android:卷II
- 深入理解Android 卷 作者
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 深入理解 Android 卷I
- 从jQuery到AngularJS,再到React,前端必须走在最前端
- 第十四周 项目2 二叉树排序树中查找的路径
- libSVM学习笔记
- CIFilter 创建二维码
- 支付宝支付开发教程
- 深入理解android卷1—java调用native代码
- WMI 编程之接收事件通知
- 第16周项目3归并排序算法的改进
- 第十三周项目3Floyd算法验证
- 3115 高精度练习之减法
- stm32f207 栈溢出
- 性能测试学习
- iOS 【cartool的使用】
- iOS开发ARC内存管理技术要点