JNI实例2---扫描SD卡中mp3文件,native层调用Java自定义的类

       此博客是在研究完《Android内核剖析》中2.2章节JNI调用机制后,才完成的。在此非常感谢该书的作者。此书的内容较多,讲述的知识点也比较深入,值得各位Android coder们学习。
在Android应用开发时,有时候为了提升程序的效率,需要使用到JNI编程,调用native C代码,协作完成应用的某些功能。
                                           理论讲解
JNI接口主要包含两种情况,第一种为从Java中调用C,第二种为从C中调用Java。
一. Java访问C
      Java类中可以定义某些native函数。当Java编译器遇到native函数时,不会关心该函数的具体实现。在程序运行时,调用native方法前,必须将C所生成的lib库装载进来。
当调用native时,编译器会向native引擎传递调用者的包名,函数名和参数类型。在产生的C函数中,会包含至少两个参数:JNIEnv指针,指向JVM的对象,可以访问JVM内部的各种对象。第二个参数为jobject,指调用者对象。
二. C访问Java
      如果C中需要使用Java的某个变量而进行相应的处理,或者C中也想调用Java中的某个函数完成某些功能,那么C就得访问Java。
      Java中是没有指针的,C访问Java时只能使用特定的接口,需要将要访问的类名,函数名称和参数传递给Java引擎。其步骤如下:
      1.获取Java对象的类
     cls = env->GetObjectClass(jobject);
     env为native函数中的第一个参数,jobject为第二个参数。
     2. 获取Java函数的id值
      jmethodID mid = env->GetMethodId(cls,"method_name","(Ljava/lang/String;)V");
      该方法第二个参数为Java中的函数名称,第三个参数:Ljava/lang/String表示String类型的参数。
     “[Ljava/lang/String;”表示String数组。返回值V代表void.
      需要注意的是GetMethodID方法的格式。
 jmethodID GetMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
     JNIEnv这个参数C++中不需要。clazz就是前面得到的jclass,name则是方法名称,sig是方法签名。
    3. 找到函数后,开始调用函数  env->CallXXXMethod(jobject,mid,ret);
     其中XXX代表函数返回值类型,具体包括Void,Object,Boolean,Byte,Char等…
通过以上三步,实现了C中调用Java函数的目标。
***************************************************************************************************************************
      另外一个问题:C访问Java类中的变量时如何实现的?其实跟以上的步骤相似,也为三个步骤:
     1.获取Java对象
     2.获取变量的id值 .jmethodID id = env->GetFiledId(cls,"filed_name”,”I”);第三个参数为变量的类型
     3. 获取变量的值
     Value = env->GetXXXFiled(env,jobjcet,fid);
     这个函数以返回值的方式获取变量值。    
       为了更好的将上述知识点表述清楚,本博客实现一个简单的demo,力争让大家理解清楚。
                                                     应用实例

     自定义一个类,该对象用于描述sd卡中mp3音频文件的属性。可以参看我前一篇博客内容(JNI实例1---扫描SD卡中mp3文件)native函数用于遍历MP3文件,之后调用Java的Track_Info类方法,native函数分别调用setDisplayName(),setSize(),setFilePath()等函数。

public class Track_Info implements Serializable{
	
	private String displayName;
	private long size;
	private String ext;//后缀名称,eg.mp3
	private String filePath;
	private String parentPath;
	

	public String getDisplayName() {
		return displayName;
	}
	
	public void setDisplayName(String displayName) {
		this.displayName = displayName;
	}
	
	public long getSize() {
		return size;
	}
	
	public void setSize(long size) {
//		Log.e("Track_Info.java", "setSize() called in JNI, size = " + size);
		this.size = size;
	}
	
	public String getExt() {
		return ext;
	}
	
	public void setExt(String ext) {
		this.ext = ext;
	}
	
	public String getFilePath() {
		return filePath;
	}
	
	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}
	
	public String getParentPath() {
		return parentPath;
	}
	
	public void setParentPath(String parentPath) {
		this.parentPath = parentPath;
	}
}

     在Java层的native方法为:    

    public native void scanDir(String dirPath);
    public native String[] getPathArray(String dirPath);  
    public native Track_Info[] getTracksArray(String dirPath); 

调用该native函数时,传入sd卡目录名称(String类型),函数返回一个Track_Info对象数组。 (getTracksArray函数)

     在实现这个demo时,遇到如下几个问题:

      1.没有很好掌握GetMethodID函数的使用

      2.JNI函数签名返回值对应表,函数签名没有很好的掌握,导致花费了不少时间排查bug。

      3.使用自定义类,首先要能访问类的构造函数,当时没有调用(init),导致花费了很多时间。

	jclass  classTrackInfo = (*env)->FindClass(env,"com/coder80/scaner/Track_Info");
	// 获取Track_Info类的构造函数ID
	jmethodID midInit = (*env)->GetMethodID(env,classTrackInfo,"<init>", "()V");
	//构造Track_Info对象
	jobject objTrackInfo = (*env)->NewObject(env,classTrackInfo,midInit);


(类容较多,分为两篇博客进行介绍----JNI实例3---扫描SD卡中mp3文件,native函数中使用自定义的类



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值