Java_call_C(双向传递基本类型)

摘自:NBG(ndk.beginners.guide)第3章


本节将通过一个小实例,实现java与C,通过JNI来相互传递基本类型(int , String)。例子界面如下:


在界面输入“键”,‘‘值’’,再选择“类型”,“设置键值”即可将java端数据存入本地数组中。通过输入“键”,选择“类型”,再“获取值”,即可从java端将本地数据取回。

原书所述程序结构图如下:


看C与java交界处,可得知将要交互处理的类型为“int”,“String”。java端的本地方法声明在Store.java中,JNI接口实现模块为com_packtpub_Store。


下面以图上源码:

JAVA端:

1.界面StoreActivity.java

package com.packtpub;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;

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

/*
 * 界面,提供输入,取出途径
 */
public class StoreActivity extends Activity {
	private EditText mUIKeyEdit, mUIValueEdit;
	private Spinner mUITypeSpinner;
	private Button mUIGetButton, mUISetButton;
	private Store mStore;

	private final static String TAG = "NDK";

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		initComponent();
		mStore = new Store();
	}

	private void initComponent() {
		mUIKeyEdit = (EditText) findViewById(R.id.etKey);
		mUIValueEdit = (EditText) findViewById(R.id.etValue);
		mUITypeSpinner = (Spinner) findViewById(R.id.spinnerType);
		mUIGetButton = (Button) findViewById(R.id.btnGet);
		mUISetButton = (Button) findViewById(R.id.btnSave);

		mUIGetButton.setOnClickListener(getListener);
		mUISetButton.setOnClickListener(setListener);

		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_spinner_item);
		adapter.add("Integer");
		adapter.add("String");

		mUITypeSpinner.setAdapter(adapter);
	}

	private OnClickListener getListener = new OnClickListener() {
		public void onClick(View v) {
			String lKey = mUIKeyEdit.getText().toString();
			StoreType lType = StoreType.values()[mUITypeSpinner
					.getSelectedItemPosition()];
			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;			
			}
		}
	};

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

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

	private void displayError(String pError) {
		Toast.makeText(getApplicationContext(), pError, Toast.LENGTH_LONG)
				.show();
	}
}

2.布局文件: main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:weightSum="1">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Save or retrieve a value from the store:" />

    <LinearLayout
        android:layout_height="wrap_content"
        android:id="@+id/LinearLayout01"
        android:layout_width="match_parent">
        <TextView
            android:text="Key: "
            android:id="@+id/TextView01"
            android:layout_height="wrap_content"
            android:layout_width="50dp"></TextView>
        <EditText
            android:id="@+id/etKey"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"></EditText>
    </LinearLayout>


    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:text="Value: "
            android:id="@+id/textView1"
            android:layout_width="50dp"
            android:layout_height="wrap_content"></TextView>
        <EditText
            android:id="@+id/etValue"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:layout_weight="1">
            <requestFocus></requestFocus>
        </EditText>
    </LinearLayout>


    <LinearLayout
        android:layout_height="wrap_content"
        android:id="@+id/LinearLayout02"
        android:layout_width="match_parent">
        <TextView
            android:text="Type: "
            android:id="@+id/TextView02"
            android:layout_height="wrap_content"
            android:layout_width="50dp"></TextView>
        <Spinner
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:id="@+id/spinnerType"
            android:layout_width="wrap_content"></Spinner>
    </LinearLayout>


    <RelativeLayout
        android:id="@+id/linearLayout2"
        android:layout_height="wrap_content"
        android:layout_width="match_parent">
        
        <Button
            android:text="Set Value"
            android:id="@+id/btnSave"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            
            android:layout_alignParentLeft="true"
            android:layout_marginLeft="120dp"
            android:layout_marginRight="20dp"></Button>
            
        <Button
            android:text="Get Value"
            android:id="@+id/btnGet"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"            
            android:layout_toRightOf="@+id/btnSave"></Button>
    </RelativeLayout>
</LinearLayout>


3. 本地方法声明文件: Store.java

package com.packtpub;

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

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


4.用户可选类: StoreType.java

package com.packtpub;

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

/* 
 * 目前仅是基本类型
 */
public enum StoreType {
	Integer, String
}


再C端:

5.本地存储工具类头文件 Store.h

#ifndef _STORE_H_
#define _STORE_H_

#include "jni.h"
#include <stdint.h>

#define STORE_MAX_CAPPCITY	3

//支持类型枚举
typedef enum {
	StoreType_Integer, StoreType_String
} StoreType;

//值存储
typedef union {
	int32_t mInteger;
	char*  mString;
} StoreValue;

//储存的实体结构
typedef struct {
	char* mKey;
	StoreType mType;
	StoreValue mValue;
} StoreEntry;

//实体存储集合
typedef struct {
	StoreEntry mEntries[STORE_MAX_CAPPCITY];
	int32_t mLength;
} Store;


int32_t isEntryValid(JNIEnv *pEnv, StoreEntry *pEntry,
							StoreType pType);

StoreEntry* allocateEntry(JNIEnv *pEnv, Store *pstore,
							jstring pKey);

StoreEntry* findEntry(JNIEnv *pEnv, Store *store, jstring pKey,
							int32_t* pError);

void releaseEntryValue(JNIEnv *pEvn, StoreEntry *pEntry);

#endif

6 .本地存储工具类头文件  Store.c

#include "Store.h"
#include <string.h>

/*
 * 本地实现的诸工具方法
 */

//判断传入实例是否合法
int32_t isEntryValid(JNIEnv *pEnv, StoreEntry *pEntry,
							StoreType pType)
{
	if((pEntry != NULL) && (pEntry->mType == pType)){
		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)
		{
			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 releaseEntryValue(JNIEnv *pEnv, StoreEntry *pEntry)
{
	int i;
	switch(pEntry->mType)
	{
	case StoreType_String:
		free(pEntry->mValue.mString);
		break;
	}
}

//查找是否存在此实体,存在则返加,否则返回空
StoreEntry* findEntry(JNIEnv *pEnv, Store *pStore, jstring pKey,
							int32_t* pError)
{
	StoreEntry *lEntry = pStore->mEntries;
	StoreEntry *lEntryEnd = lEntry + pStore->mLength;

	const char *lKeyTmp = (*pEnv)->GetStringUTFChars(pEnv, pKey, NULL);

	if(lKeyTmp == NULL)
	{
		if(pError != NULL)
		{
			*pError = 1;
		}
		return NULL;//add by me the 'NULL'
	}

	while((lEntry < lEntryEnd)
			&& (strcmp(lEntry->mKey, lKeyTmp) != 0) )
	{
		++lEntry;
	}

	(*pEnv)->ReleaseStringUTFChars(pEnv, pKey, lKeyTmp);

	return (lEntry == lEntryEnd) ? NULL : lEntry;
}

7.JNI生成头文件:  com_packtpub_Store.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_packtpub_Store */

#ifndef _Included_com_packtpub_Store
#define _Included_com_packtpub_Store
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_packtpub_Store
 * Method:    getInteger
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_packtpub_Store_getInteger
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_packtpub_Store
 * Method:    setInteger
 * Signature: (Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_packtpub_Store_setInteger
  (JNIEnv *, jobject, jstring, jint);

/*
 * Class:     com_packtpub_Store
 * Method:    getString
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_packtpub_Store_getString
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_packtpub_Store
 * Method:    setString
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_packtpub_Store_setString
  (JNIEnv *, jobject, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

8.本地方法的实现文件: com_packtpub_Store.c

#include "com_packtpub_Store.h"
#include "Store.h"
#include <stdint.h>
#include <string.h>

static Store gStore = {{} , 0};

/*
 * 提供java端本地方法的实现。
 */


/*
 * Class:     com_packtpub_Store
 * Method:    getInteger
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_packtpub_Store_getInteger
	(JNIEnv *pEnv, jobject pThis, jstring pKey)
{
	StoreEntry *lEntry = findEntry(pEnv, &gStore, pKey, NULL);
	if(isEntryValid(pEnv, lEntry, StoreType_Integer)){
		return lEntry->mValue.mInteger;
	}
	else
	{
		return 0;
	}
}

/*
 * Class:     com_packtpub_Store
 * Method:    setInteger
 * Signature: (Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_packtpub_Store_setInteger
	(JNIEnv *pEnv, jobject pThis, jstring pKey, jint pInteger)
{
	StoreEntry *lEntry = allocateEntry(pEnv, &gStore, pKey);
	if(lEntry != NULL)
	{
		lEntry->mType = StoreType_Integer;
		lEntry->mValue.mInteger = pInteger;
	}
}

/*
 * Class:     com_packtpub_Store
 * Method:    getString
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_packtpub_Store_getString
  (JNIEnv *pEnv, jobject pThis, jstring pKey)
{
	StoreEntry *lEntry = findEntry(pEnv, &gStore, pKey, NULL);
	if(isEntryValid(pEnv, lEntry, StoreType_String))
	{
		return (*pEnv)->NewStringUTF(pEnv, lEntry->mValue.mString);
	}
	else
	{
		return NULL;
	}
}

/*
 * Class:     com_packtpub_Store
 * Method:    setString
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_packtpub_Store_setString
  (JNIEnv *pEnv, jobject pThis, jstring pKey, jstring pValue)
{
	const char *lStringTemp = (*pEnv)->GetStringUTFChars(pEnv, pValue, NULL);

	if(lStringTemp == NULL)
	{
		return ;
	}

	StoreEntry *lEntry = allocateEntry(pEnv, &gStore, pKey);
	if(lEntry != NULL)
	{
		lEntry->mType = StoreType_String;
		jsize lStringLength = (*pEnv)->GetStringUTFLength(pEnv, pValue);
		lEntry->mValue.mString =
				(char*)malloc(sizeof(char) * (lStringLength + 1));
		strcpy(lEntry->mValue.mString, lStringTemp);
	}
	(*pEnv)->ReleaseStringUTFChars(pEnv, pValue, lStringTemp);
}


9. JNI部分编译脚本 android.mk

LOCAL_PATH := $(call my-dir)
		
include $(CLEAR_VARS)

LOCAL_CFLAGS		:= -DHAVE_INTTYPES_H
LOCAL_MODULE  		:= store
LOCAL_SRC_FILES	:= com_packtpub_Store.c Store.c

include $(BUILD_SHARED_LIBRARY)


要点小结:

  1. 一个jstring不能直接在本地代码中进行操作。java与C中的字符串是不同的事物。在java中,String是一个带有成员方法的真实的对象,而在C中,字符串是原始的字符数组;   要把一个Java String转换为C String,可以使用GetStringUTFChars( ) 来进行。但同时,也必须使用ReleaseStringUTFChars( ) 来释放临时缓存(前者产生的),(详见8中示例)。
  2. 要将一个C string转化为Java String,可以使用NewStringUTF( ),(详见8中示例)。
  3. 从上例可以看出:在本地调用过程中,基本类型整型也经历了多次变化:首在在java中是int,在传入\传出java时是jint,最后在本地端是int/int_32。显然,可以在本地代码中使用JNI的表现方式jint,因为这两种类型是等价的。(int_32属于C99标准库,typedef int,为的是更好的移植性。在stdint.h中还有更多的数字类型,要在JNI中使用它们,需要在android.mk中申明 -DHAVE_INTTYPES_H宏)。
  4. 下表列出其它常见基本类型在C,Java,JNI中的类型对照:
  5. 更多C与Java字符串的转换,看P95;详细JNI规格说明://java.sun.com/docs/books/jni/html/jniTOC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值