转载请注明出处:http://blog.csdn.net/droyon/article/details/8661672
在android中,存在很多Native代码调用,这些调用如何实现的,当初很好奇,终于在深入理解android中找到了答案,现在将自己看书的心得罗列出来,为自己复习之需。
1、MediaScanner.java,在这个方法中通过System.loadLibrary方法加载jni库。这样就可以调用native方法了。
static { System.loadLibrary("media_jni"); native_init(); }其中native_init方法就是个native方法。
private static native final void native_init();native方法的实现包含在我们加载的库里面,jni库相当于一个桥梁,连接起了java世界和native世界。在这里jni通过libmedia_jni.so库文件(linux系统会在库名字的前面加上lib),连接起了MediaScanner和libmedia.so两个世界。
具体看一下实现:关于实现在文件frameworks/base/media/jni/android_media_MediaScanner.cpp中。
static void android_media_MediaScanner_native_init(JNIEnv *env) { LOGV("native_init"); jclass clazz = env->FindClass(kClassMediaScanner); if (clazz == NULL) { return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); if (fields.context == NULL) { return; } }native_init方法的主要作用就是初始化,但是如何认定这个方法就是我们native_init方法的实现,有什么规则?。
在jni中,使用JNINativeMethod结构体来保存java native方法和jni函数之间的一一对应关系。
static JNINativeMethod gMethods[] = { { "processDirectory", "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V", (void *)android_media_MediaScanner_processDirectory }, { "processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", (void *)android_media_MediaScanner_processFile }, { "setLocale", "(Ljava/lang/String;)V", (void *)android_media_MediaScanner_setLocale }, { "extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt }, { "native_init", "()V", (void *)android_media_MediaScanner_native_init }, ...... }
这个结构体中,中间部分是函数签名,保存了java在调用native函数时的传入的参数以及返回值。
这是一种映射,将native函数映射到函数指针,但是通过这样映射,还不能保证当我们执行native_init方法时会执行到函数指针android_media_MediaScanner_native_init,因为我们还没有注册,注册实现:int register_android_media_MediaScanner(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, kClassMediaScanner, gMethods, NELEM(gMethods)); }这个注册实现会调用AndroidRunntime下的一个registerNativeMethods方法,关于这个方法的实现:AndroidRunntime.cpp中
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) { return jniRegisterNativeMethods(env, className, gMethods, numMethods); }jniRegisterNativeMethods方法的实现在JNIHelp.c中实现。
既然调用register_android_media_MediaScanner这个方法可以实现我们native_init方法的注册,那么register_android_media_MediaScanner这个方法在那里调用的那?
解答:我们在System.loadLibrary加载jni库的时候,就会在这个jni类库的源文件中查找JNI_OnLoad方法。在这个方法中实现了这个方法的调用。
jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("ERROR: GetEnv failed\n"); goto bail; } assert(env != NULL); if (register_android_media_MediaPlayer(env) < 0) { LOGE("ERROR: MediaPlayer native registration failed\n"); goto bail; } if (register_android_media_MediaRecorder(env) < 0) { LOGE("ERROR: MediaRecorder native registration failed\n"); goto bail; } if (register_android_media_MediaScanner(env) < 0) { LOGE("ERROR: MediaScanner native registration failed\n"); goto bail; } ....... }这样就注册了gMethods结构体数组到jni中,那么我们的nativie_init方法到android_media_MediaScanner_native_init之间的关联也就建立起来了。
jni可以实现让我们在java层调用c层实现的函数,同时也可以让我们在c层中调用java层的方法。实现如下:
在c层:
static const char* const kClassMediaScannerClient = "android/media/MediaScannerClient";定义了kClassMediaScannerClient字符串,然后通过GetFieldID方法或者GetMethodID方法得到属性或者方法的id值。
jclass mediaScannerClientInterface = env->FindClass(kClassMediaScannerClient);
mScanFileMethodID = env->GetMethodID( mediaScannerClientInterface, "scanFile", "(Ljava/lang/String;JJZZ)V");mScanFildMethodID就是上层java类MedisScannerClient中scanFile方法的jni层方法调用id,GetMethodID方法的第三个方法是签名,括号里面是传入的参数,外面是返回值。
public interface MediaScannerClient { public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory, boolean noMedia); ...... }我们拿到了这个mScanFieldMethodID,我们可以通过CallVoidMethod来调用。
mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize, isDirectory, noMedia);
对于属性GetFieldID也一样。
------------------------------------------------------------------------------------------------------
在android代码framework/base/core/jni下提供了大部分framework层native方法的实现,这些jni实现代码,会编译生成libandroid_runtime库,这个库中包含了一下文件:
LOCAL_SRC_FILES:= \ ActivityManager.cpp \ AndroidRuntime.cpp \ Time.cpp \ com_android_internal_content_NativeLibraryHelper.cpp \ com_google_android_gles_jni_EGLImpl.cpp \ com_google_android_gles_jni_GLImpl.cpp.arm \ android_app_NativeActivity.cpp \ android_opengl_GLES10.cpp \ android_opengl_GLES10Ext.cpp \ android_opengl_GLES11.cpp \ android_opengl_GLES11Ext.cpp \ android_opengl_GLES20.cpp \ android_database_CursorWindow.cpp \ android_database_SQLiteCompiledSql.cpp \ android_database_SQLiteDebug.cpp \ android_database_SQLiteDatabase.cpp \ android_database_SQLiteProgram.cpp \ android_database_SQLiteQuery.cpp \ android_database_SQLiteStatement.cpp \ android_emoji_EmojiFactory.cpp \ android_view_Display.cpp \ android_view_Surface.cpp \ android_view_TextureView.cpp \ android_view_InputChannel.cpp \ android_view_InputQueue.cpp \ android_view_KeyEvent.cpp \ android_view_KeyCharacterMap.cpp \ android_view_HardwareRenderer.cpp \ android_view_GLES20Canvas.cpp \ android_view_MotionEvent.cpp \ android_view_PointerIcon.cpp \ android_view_VelocityTracker.cpp \ android_text_AndroidCharacter.cpp \ android_text_AndroidBidi.cpp \ android_os_Debug.cpp \ android_os_FileUtils.cpp \ android_os_MemoryFile.cpp \ android_os_MessageQueue.cpp \ android_os_ParcelFileDescriptor.cpp \ android_os_Power.cpp \ android_os_StatFs.cpp \ android_os_SystemClock.cpp \ android_os_SystemProperties.cpp \ android_os_UEventObserver.cpp \ android_net_LocalSocketImpl.cpp \ android_net_NetUtils.cpp \ android_net_TrafficStats.cpp \ android_net_wifi_Wifi.cpp \ android_nio_utils.cpp \ android_nfc_NdefMessage.cpp \ android_nfc_NdefRecord.cpp \ android_text_format_Time.cpp \ android_util_AssetManager.cpp \ android_util_Binder.cpp \ android_util_EventLog.cpp \ android_util_Log.cpp \ android_util_FloatMath.cpp \ android_util_Process.cpp \ android_util_StringBlock.cpp \ android_util_XmlBlock.cpp \ android/graphics/AutoDecodeCancel.cpp \ android/graphics/Bitmap.cpp \ android/graphics/BitmapFactory.cpp \ android/graphics/Camera.cpp \ android/graphics/Canvas.cpp \ android/graphics/ColorFilter.cpp \ android/graphics/DrawFilter.cpp \ android/graphics/CreateJavaOutputStreamAdaptor.cpp \ android/graphics/Graphics.cpp \ android/graphics/HarfbuzzSkia.cpp \ android/graphics/Interpolator.cpp \ android/graphics/LayerRasterizer.cpp \ android/graphics/MaskFilter.cpp \ android/graphics/Matrix.cpp \ android/graphics/Movie.cpp \ android/graphics/NinePatch.cpp \ android/graphics/NinePatchImpl.cpp \ android/graphics/NinePatchPeeker.cpp \ android/graphics/Paint.cpp \ android/graphics/Path.cpp \ android/graphics/PathMeasure.cpp \ android/graphics/PathEffect.cpp \ android_graphics_PixelFormat.cpp \ android/graphics/Picture.cpp \ android/graphics/PorterDuff.cpp \ android/graphics/BitmapRegionDecoder.cpp \ android/graphics/Rasterizer.cpp \ android/graphics/Region.cpp \ android/graphics/Shader.cpp \ android/graphics/SurfaceTexture.cpp \ android/graphics/TextLayout.cpp \ android/graphics/TextLayoutCache.cpp \ android/graphics/Typeface.cpp \ android/graphics/Utils.cpp \ android/graphics/Xfermode.cpp \ android/graphics/YuvToJpegEncoder.cpp \ android_media_AudioRecord.cpp \ android_media_AudioSystem.cpp \ android_media_AudioTrack.cpp \ android_media_JetPlayer.cpp \ android_media_ToneGenerator.cpp \ android_hardware_Camera.cpp \ android_hardware_SensorManager.cpp \ android_hardware_UsbDevice.cpp \ android_hardware_UsbDeviceConnection.cpp \ android_hardware_UsbRequest.cpp \ android_debug_JNITest.cpp \ android_util_FileObserver.cpp \ android/opengl/poly_clip.cpp.arm \ android/opengl/util.cpp.arm \ android_bluetooth_HeadsetBase.cpp \ android_bluetooth_common.cpp \ android_bluetooth_BluetoothAudioGateway.cpp \ android_bluetooth_BluetoothSocket.cpp \ android_bluetooth_c.c \ android_server_BluetoothService.cpp \ android_server_BluetoothEventLoop.cpp \ android_server_BluetoothA2dpService.cpp \ android_server_NetworkManagementSocketTagger.cpp \ android_server_Watchdog.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ com_android_internal_os_ZygoteInit.cpp \ android_backup_BackupDataInput.cpp \ android_backup_BackupDataOutput.cpp \ android_backup_FileBackupHelperBase.cpp \ android_backup_BackupHelperDispatcher.cpp \ android_app_backup_FullBackup.cpp \ android_content_res_ObbScanner.cpp \ android_content_res_Configuration.cpp \ android_animation_PropertyValuesHolder.cpp以及一些共享库
LOCAL_SHARED_LIBRARIES := \ libexpat \ libnativehelper \ libcutils \ libutils \ libbinder \ libnetutils \ libui \ libgui \ libcamera_client \ libskia \ libsqlite \ libdvm \ libEGL \ libGLESv1_CM \ libGLESv2 \ libETC1 \ libhardware \ libhardware_legacy \ libsonivox \ libcrypto \ libssl \ libicuuc \ libicui18n \ libmedia \ libwpa_client \ libjpeg \ libnfc_ndef \ libusbhost \ libharfbuzz \ libz \我们的framework层代码在调用native方法之前,是一定要通过System.loadLibrary(jni库名字)方法加载jni库的,不然后报错。这个jni库包含了
android_bluetooth_BluetoothSocket.cpp \ android_bluetooth_c.c \ android_server_BluetoothService.cpp \蓝牙的几个native方法实现库,这么说,蓝牙要想正常工作,这个库是一定要在开机加载的,那么这个库在那里加载那?在蓝牙模块之前找不到,在系统中grep,终于找打了两个地方:
1、WithFramework.java
/** * Binds native framework methods and then invokes a main class with the * remaining arguments. */ class WithFramework { /** * Invokes main(String[]) method on class in args[0] with args[1..n]. */ public static void main(String[] args) throws Exception { if (args.length == 0) { printUsage(); return; } Class<?> mainClass = Class.forName(args[0]); System.loadLibrary("android_runtime"); if (registerNatives() < 0) { throw new RuntimeException("Error registering natives."); } String[] newArgs = new String[args.length - 1]; System.arraycopy(args, 1, newArgs, 0, newArgs.length); Method mainMethod = mainClass.getMethod("main", String[].class); mainMethod.invoke(null, new Object[] { newArgs }); } 。。。。。。 }2、LoadClass.java
/** * Loads a class, runs the garbage collector, and prints showmap output. * * <p>Usage: dalvikvm LoadClass [class name] */ class LoadClass { public static void main(String[] args) { System.loadLibrary("android_runtime"); if (registerNatives() < 0) { throw new RuntimeException("Error registering natives."); } Debug.startAllocCounting(); if (args.length > 0) { try { long start = System.currentTimeMillis(); Class.forName(args[0]); long elapsed = System.currentTimeMillis() - start; Log.i("LoadClass", "Loaded " + args[0] + " in " + elapsed + "ms."); } catch (ClassNotFoundException e) { Log.w("LoadClass", e); return; } }有可能是第一个方法,但不知道WithFramework.java怎么开机启动的?
留待以后研究。
javah可以得到java文件中native方法的jni c class框架。只需在c框架中实现函数功能即可。
javah -d ~/jnic -jni com.android.example.Test
javap 可以查看java函数的输入和返回参数。
javap -s com.android.example.Test
jni持久化对象的技巧
jni中声明一个结构体或者一个类,jni把这个对象当成一个int值传递给java端的object,在jni下次需要使用时,在引用到java端的int对象,把该int对象强制转换为jni存储前的对象。