转载请注明出处: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
ps:
jni持久化对象的技巧
jni中声明一个结构体或者一个类,jni把这个对象当成一个int值传递给java端的object,在jni下次需要使用时,在引用到java端的int对象,把该int对象强制转换为jni存储前的对象。