摘自: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(); } }
<?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>
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
#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; }
/* 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
#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); }
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)
要点小结: