Android Jni分析1
JNI概念
JNI是Java Native Interface的缩写,java本地调用,JNI是一种技术,可以做到以下两点:
1.java程序中函数可以调用Native语言写的函数,Native一般指的是C/C++编写的函数
2.Native程序中的函数可以调用Java层的函数,也就是说,C/C++程序中可以调用java的函数
JNI是用来调用本地方法的技术,通过jni接口可以使Java和C/C++之间进行通信。采用JNI特性可以增强 Java 与本地代码交互的能力,使Java和其他类型的语言如C++/C能够互相调用。
1.2 学习JNI的实例:MtpDevice
1. java层对应模块是MtpDevice,然而MtpDevice类中的一些函数需要Native层来实现。但java程序是无法直接调用c/c++(程序或功能),这就需要在两者搭建一个通信桥梁。
2.JNI层对应的是libmedia_jni.so,media_jni是JNI库的名字,其中,下划线前的media是Native层库的名字,这里就是libmedia库,下划线jni表示他是一个JNI库。注意,JNI库名字可以随便取,但是Android平台上基本都采用Jni的库文件命名规则是: “lib模块名_jni.so”
3.Native层对应的是libmedia.so,这个库完成了实际的功能。
4.MtpDevice将通过JNI库libmedia_jni.so和Native层的libmedia.so交互。
具体代码如下:
一起来看一下 native_send_object_info方法。
Java层代码 @UnsupportedAppUsage public static final boolean BYPASS_METADATA_FILTER = false; static { System.loadLibrary("media_jni");//加载动态库文件,media_jni是JNI库名,在加载的时候会扩展成libmedia_jni.so native_init();////调用native_init函数 } private static native final void native_init(); //声明native函数,native是java关键字,代示此函数由Jni层实现逻辑 Jni层代码 ///frameworks/base/media/jni/android_mtp_MtpDevice.cpp //以下是部分调用java函数的实现,java函数内的实现逻辑就是调用native函数,实际业务的实现是native函数 public boolean importFile(int objectHandle, @NonNull ParcelFileDescriptor descriptor) { return native_import_file(objectHandle, descriptor.getFd());//调用native函数 } /** * Uploads an object metadata for a new entry. The {@link MtpObjectInfo} can be * created with the {@link MtpObjectInfo.Builder} class. * * The returned {@link MtpObjectInfo} has the new object handle field filled in. * * @param info metadata of the entry * @return object info of the created entry, or null if sending object info fails */ public @Nullable MtpObjectInfo sendObjectInfo(@NonNull MtpObjectInfo info) { return native_send_object_info(info);//调用native函数 } 以下native函数的声明: private native boolean native_open(String deviceName, int fd); private native void native_close(); private native MtpDeviceInfo native_get_device_info(); private native byte[] native_get_thumbnail(int objectHandle); private native boolean native_delete_object(int objectHandle); private native int native_get_parent(int objectHandle); private native int native_get_storage_id(int objectHandle); private native boolean native_import_file(int objectHandle, String destPath); private native boolean native_import_file(int objectHandle, int fd); private native boolean native_send_object(int objectHandle, long size, int fd); private native MtpObjectInfo native_send_object_info(MtpObjectInfo info); private native int native_submit_event_request() throws IOException; private native MtpEvent native_reap_event_request(int handle) throws IOException; private native void native_discard_event_request(int handle); private native long native_get_object_size_long(int handle, int format) throws IOException; static const JNINativeMethod gMethods[] = { .....省略部分 {"native_import_file", "(II)Z",(void *)android_mtp_MtpDevice_import_file_to_fd}, {"native_send_object", "(IJI)Z",(void *)android_mtp_MtpDevice_send_object}, {"native_send_object_info", "(Landroid/mtp/MtpObjectInfo;)Landroid/mtp/MtpObjectInfo;", (void *)android_mtp_MtpDevice_send_object_info},//在jni层相对应关联的函数名 {"native_submit_event_request", "()I", (void *)android_mtp_MtpDevice_submit_event_request}, {"native_reap_event_request", "(I)Landroid/mtp/MtpEvent;", (void *)android_mtp_MtpDevice_reap_event_request}, {"native_discard_event_request", "(I)V", (void *)android_mtp_MtpDevice_discard_event_request}, .....省略部分 }; //对应关联的函数具体实现 static jobject android_mtp_MtpDevice_send_object_info(JNIEnv *env, jobject thiz, jobject info) { MtpDevice* device = get_device_from_object(env, thiz); if (!device) { return NULL; } // Updating existing objects is not supported. if (env->GetIntField(info, field_objectInfo_handle) != -1) { return NULL; } //...省略代码部分 } int object_handle = device->sendObjectInfo(object_info);//调用了device对象的sendObjectInfo方法,这个device对象是native层的, //我们跟进native再看看实现 if (object_handle == -1) { delete object_info; return NULL; } //...省略代码部分 } native层代码 //frameworks/av/media/mtp/MtpDevice.cpp bool MtpDevice::sendObject(MtpObjectHandle handle, uint32_t size, int srcFD) { std::lock_guard<std::mutex> lg(mMutex); if (mLastSendObjectInfoTransactionID + 1 != mTransactionID || mLastSendObjectInfoObjectHandle != handle) { ALOGE("A sendObject request must follow the sendObjectInfo request."); return false; } mRequest.reset(); if (sendRequest(MTP_OPERATION_SEND_OBJECT)) { mData.setOperationCode(mRequest.getOperationCode()); mData.setTransactionID(mRequest.getTransactionID()); const int64_t writeResult = mData.write(mRequestOut, mPacketDivisionMode, srcFD, size); const MtpResponseCode ret = readResponse(); return ret == MTP_RESPONSE_OK && writeResult > 0; } return false; }
so结尾的文件叫:动态库文件 ,或是叫XXso动态库文件 ,
什么是动态库 静态库
Android下的动态库和静态库就是Linux的静态库和动态库;Linux平台静态库以.a结尾,动态库以.so结尾。
动态库
Linux下以.so即为,动态库不会在编译时将整个函数库都整合到目标代码中,而是在运行时执行到哪个函数才会从动态库中加载哪个函数,
好处在于体量小,升级方便。
JNI与NDK有何差异:
NDK: native Development Kit, Android的一个工具开发包。NDK能帮助开发者快速开发C、 C++的动态库,将动态库编译成.so文件供Java调用,
并支持将.so和应用一起打包成 apk.
使用JNI编译时,需要在将部分C/C++功能代码编译成动态库.so,类似java库.jar文件一样,这里就需要使用NDK工具来打包制作.SO文件
加载JNI库
java如果要调用Native函数就必须在JNI层通过加载一个动态库实现,原则上,动态库的加载时间是在调用Native函数之前,
但实际中通行的做法是在类的static语句中加载,调用System.loadLibrary方法,System.loadLibrary方法的参数是动态库的名字,
media_jni,系统会自动根据不同的平台拓展成真实的动态库文件名。
总结:
JNI技术使用需要完成下面两项工作就可以使用JNI:
1.加载对应的JNI库
2.声明由关键字navtive修饰的函数
系列文章: Android Jni分析2
评论