Android Jni分析1

sancaiodm Android应用 2023-07-11 890 0

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

image.png

       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交互。


具体代码如下:

image.png

                        一起来看一下  native_send_object_info方法。

image.png

     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


评论