Java_call_C(从本地端抛出异常)

本文将实现从C/C++代码中抛出异常,并在JAVA端处理。

三个异常:值类型错误异常,键值不存在异常,存储满异常。


1.在java端新建异常包,并编写异常类:com.packtpub.exception,中添加如下三个相应的异常类文件

package com.packtpub.exception;

public class InvalidTypeException extends Exception{
	public InvalidTypeException(String pDetailMessage){
		super(pDetailMessage);
	}
}


package com.packtpub.exception;

public class NotExistingKeyException  extends Exception{
	public NotExistingKeyException(String pDetailMessage){
		super(pDetailMessage);
	}
}

package com.packtpub.exception;

public class StoreFullException extends RuntimeException{
	public StoreFullException(String pDetailMessage){
		super(pDetailMessage);
	}
}


2.在java端的本地方法声明处,添加异常:Store.java

package com.packtpub;

import com.packtpub.exception.InvalidTypeException;
import com.packtpub.exception.NotExistingKeyException;

/**
 * @author Administrator
 * @version 1.0
 */

/*
 * 本类用于加载本地库,定义访问本地代码的接口
 */
public class Store {
	static {
		System.loadLibrary("store");
	}
	
	public native int getInteger(String pKey)
			throws NotExistingKeyException, InvalidTypeException;	
		public native void setInteger(String pKey, int pInt);
		
		public native String getString(String pKey)
			throws NotExistingKeyException, InvalidTypeException;	
		public native void setString(String pKey, String pString);
		
		
		public native Color getColor(String pKey)
			throws NotExistingKeyException, InvalidTypeException;	
		public native void setColor(String pKey, Color pColor);
}

要从本地代码中抛出异常。因为异常不属于C语言,所以JNI异常不能在C方法原型处理声明(同样也不能在C++原型方法处声明,因为C++与JAVA有着不同的异常模型)。因此,这里不需要重新生成JNI头文件。


3.捕获异常: StoreActivity.java

	private OnClickListener getListener = new OnClickListener() {
		public void onClick(View v) {
			String lKey = mUIKeyEdit.getText().toString();
			StoreType lType = StoreType.values()[mUITypeSpinner
					.getSelectedItemPosition()];
			try {
				switch (lType) {
				case Integer:
					Log.d(TAG, "case Integer");
					mUIValueEdit.setText(Integer.toString(mStore
							.getInteger(lKey)));
					break;

				case String:
					Log.d(TAG, "case String");
					mUIValueEdit.setText(mStore.getString(lKey));
					break;

				case Color:
					Log.d(TAG, "case Color" + mStore.getColor(lKey));
					mUIValueEdit.setTextColor(mStore.getColor(lKey).getColor());
					mUIValueEdit.setText("Color value : "
							+ mStore.getColor(lKey));
					break;
				}
			} catch (NotExistingKeyException e) {
				displayError("Key does not exist in store..");
			} catch (InvalidTypeException e) {
				displayError("Incorrect  type.");
			}
		}
	};

	private OnClickListener setListener = new OnClickListener() {
		public void onClick(View v) {
			String lKey = mUIKeyEdit.getText().toString();
			StoreType lType = StoreType.values()[mUITypeSpinner
					.getSelectedItemPosition()];

			try {
				switch (lType) {
				case Integer:
					mStore.setInteger(lKey,
							Integer.valueOf(mUIValueEdit.getText().toString()));
					break;

				case String:
					mStore.setString(lKey, mUIValueEdit.getText().toString());
					break;

				case Color:
					mStore.setColor(lKey, new Color(mUIValueEdit.getText()
							.toString()));
					break;
				}
			} catch (StoreFullException eStoreFullException) {
				displayError("Store is full.");
			}
		}
	};

4.在jni/Store.h中创建新的辅助方法以帮助抛出异常:

void throwInvalidTypeException(JNIEnv *pEnv);
void throwNotExistingException(JNIEnv *pEnv);
void throwStoreFullException(JNIEnv *pEnv);

5.在适当的地方抛出异常: jni/Store.c

//判断传入实例是否合法
int32_t isEntryValid(JNIEnv *pEnv, StoreEntry *pEntry,
							StoreType pType)
{
	if(pEntry == NULL)
		throwNotExistingKeyException(pEnv);//////
	else if(pEntry->mType != pType)
		throwInvalidTypeException(pEnv);//////
	else
		return 1;

	return 0;
}

//根据传入键名,在pStore里为其分配一个键名空间(分配一个实体,以在调用处写值)
StoreEntry* allocateEntry(JNIEnv *pEnv, Store *pStore, jstring pKey)
{
	int32_t lError = 0;
	StoreEntry *lEntry = findEntry(pEnv, pStore, pKey, &lError);
	if(lEntry != NULL)
	{
		releaseEntryValue(pEnv, lEntry);
	}
	else if(!lError)
	{
		if(pStore->mLength >= STORE_MAX_CAPPCITY)
		{
			throwStoreFullException(pEnv);//////
			return NULL;
		}
		lEntry = pStore->mEntries + pStore->mLength;

		const char *lKeyTmp = (*pEnv)->GetStringUTFChars(pEnv, pKey, NULL);
		if(lKeyTmp == NULL)
		{
			return NULL;//add by me the 'NULL'
		}

		lEntry->mKey = (char*) malloc(strlen(lKeyTmp));
		strcpy(lEntry->mKey, lKeyTmp);
		(*pEnv)->ReleaseStringUTFChars(pEnv, pKey, lKeyTmp);

		++pStore->mLength;
	}
	return lEntry;
}

......

void throwNotExistingKeyException(JNIEnv* pEnv)
{
	jclass lClass = (*pEnv)->FindClass(pEnv, "com/packtpub/exception/NotExistingKeyException");
	if(lClass != NULL)
	{
		(*pEnv)->ThrowNew(pEnv, lClass, "Key does not exist.");
	}
	(*pEnv)->DeleteLocalRef(pEnv, lClass);
}


void throwInvalidTypeException(JNIEnv *pEnv)
{
	jclass lClass = (*pEnv)->FindClass(pEnv, "com/packtpub/exception/InvalidTypeException");
	if(lClass != NULL)
	{
		(*pEnv)->ThrowNew(pEnv, lClass, "Incorrect value.");
	}
	(*pEnv)->DeleteLocalRef(pEnv, lClass);
}

void throwStoreFullException(JNIEnv *pEnv)
{
	jclass lClass = (*pEnv)->FindClass(pEnv, "com/packtpub/exception/StoreFullException");
	if(lClass != NULL)
	{
		(*pEnv)->ThrowNew(pEnv, lClass, "Store is full.");
	}
	(*pEnv)->DeleteLocalRef(pEnv, lClass);
}


小结(抛出异常步骤):

1.在java端编写异常类,并在本地方法声明处添加异常声明(仅添加异常声明,不需要重新生成JNI头文件);

2.在上层Java端捕获异常,并处理;

3.在下层C、C++端抛出异常:先FindClass( )找到异常类描述,再ThrowNew( )抛出异常,最后删除类描述引用;

4.FindClass( )及其它JNI方法一般可能因多种原因调用失败(如内存不足,类未找到,等),因此,非常建议检查它们的返回值。

5.当一个异常抛出后,不要再进一步去调用JNI其它的清理方法(DeleteLocalRef(), Delete GlobalRef() 等)。本地代码应该清理它的资源并把控制权返给java,当然也有可能继续由纯本地代码处理,如果没有使用java代码的话。当本地方法返回后,虚拟机把异常传播给JAVA

6.JNI与C++

C不是面向对象语言,而C++是。

在C中,JNIEnv实质上是一个包含了函数指针的结构体。当然,当一个JNIEnv传给你后,所有的函数指针已被初始化了,因此你可以向一个对象那样使用它。然而,对于pThis这个参数,它隐式地表明处于一个面向对象语言中。在C中,JNIEnv需要被“反引用(找到所指对象)”从而来调用一个方法:

	jclass lClass = (*pEnv)->FindClass(pEnv, "com/packtpub/exception/StoreFullException");
	//而C++看起来更自然和简单,JNIEnv是不需要“反引用”的,它看起来更像一个成员方法:
	jclass lClass = pEnv->FindClass("com/packtpub/exception/StoreFullException");

你可能感兴趣的:(java,c,String,jni,null,Integer)