一、概述
JNI(Java Native Interface):Java本地開發(fā)接口。它是一個協(xié)議,通過這個協(xié)議Java和C/C++代碼可以相互調(diào)用。
為什么要用JNI?
Java不是本地語言需要翻譯,效率上C/C++執(zhí)行起來會更高、安全角度上來說Java的反編譯要比C語言容易、代碼移植等等。
本文通過JNI實現(xiàn)了輸入法的核心搜索功能(全拼、簡拼、聯(lián)想詞)。
二、效果展示

三、代碼實現(xiàn)

C++層代碼主要是開源的谷歌輸入法頭文件,jni包下是對C++的封裝。
common類是對自定義數(shù)據(jù)類型和方法的封裝。
頭文件:
1.JNI中Log的打印,用在Android中調(diào)試代碼
2.方法體的聲明
#pragma once
#include <jni.h>
#include <wchar.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <android/log.h>
#include "googlepinyin/pinyinime.h"
#define TAG "PinyinIme"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, TAG, __VA_ARGS__)
bool jstring2cqWCHAR(JNIEnv * env, jstring str, wchar_t* out, size_t maxLength);
bool jstring2cqCHAR(JNIEnv * env, jstring str, char* out, size_t maxLength);
size_t cq_wcslen(wchar_t* str);
jobject CommonJNI_createSpellingString(JNIEnv * env, jstring y,jint x );
void CppCommonJNI_init(JNIEnv * env);
void CppCommonJNI_cleanup(JNIEnv * env);
jclass CppCommonJNI_getStringCls();
C++:
1.結(jié)構(gòu)體的創(chuàng)建和聲明。注意對GolbalReference全局引用要調(diào)用DeleteGlobalRef將其釋放,全局引用會阻止它所引用的對象被GC回收從而造成內(nèi)存泄漏。LocalReference 本地引用在方法執(zhí)行完畢后會被自動刪除。
2.JNI的動態(tài)注冊。靜態(tài)注冊在用到時才去加載效率不高,動態(tài)注冊的方式是固定的。
3.jstring轉(zhuǎn)字符和寬字符的封裝,防止多次的判空和聲明。
4.cq_wcslen自定義測量寬字符長度,為了防止在不同編譯器下因字符長度的不同所導致的錯誤
#include "com_pinyin_common.h"
#include "com_pinyin_PinyinIme.h"
/*
* 如果成功返回JNI版本, 失敗返回-1
*/
typedef struct CppCommonJNI
{
jclass strCls;
jclass routeCls;
jmethodID strMid;
jclass stringCls;
} CppCommonJNI;
static CppCommonJNI g_CppCommonJNI;
void CppCommonJNI_init(JNIEnv * env){
CppCommonJNI * o = &g_CppCommonJNI;
memset(o, 0, sizeof(CppCommonJNI));
o->strCls = (jclass)env->NewGlobalRef(env->FindClass("com/pinyin/PinyinIme$SpellingString"));
o->strMid = env->GetMethodID(o->strCls, "<init>", "(Ljava/lang/String;I)V");
o->routeCls = (jclass)env->NewGlobalRef(env->FindClass("java/lang/String"));
}
void CppCommonJNI_cleanup(JNIEnv * env){
CppCommonJNI * o = &g_CppCommonJNI;
if (o->routeCls != NULL)
{
env->DeleteGlobalRef(o->strCls);
env->DeleteGlobalRef(o->routeCls);
memset(o, 0, sizeof(CppCommonJNI));
}
}
jobject CommonJNI_createSpellingString(JNIEnv * env,jstring str, jint num)
{
CppCommonJNI * o = &g_CppCommonJNI;
return env->NewObject((jclass)o->strCls, o->strMid, str, num);
}
jclass CppCommonJNI_getStringCls()
{
CppCommonJNI * o = &g_CppCommonJNI;
if (o->routeCls != NULL)
{
return o->routeCls;
}
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
bool resultRegister = true;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
resultRegister = JNaviCore_Pinyin_register(env);
if (!resultRegister)
{
return -1;
}
return JNI_VERSION_1_6;
}
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
JNaviCore_Pinyin_unregister(env);
CppCommonJNI_cleanup(env);
}
bool jstring2cqWCHAR(JNIEnv * env, jstring str, wchar_t * out, size_t maxLength)
{
jsize len;
if (str == NULL)
{
return false;
}
len = env->GetStringLength(str);
if (len >= (jsize)maxLength)
{
return false;
}
env->GetStringRegion(str, 0, len, (jchar*)out);
out[len] = 0;
return true;
}
bool jstring2cqCHAR(JNIEnv * env, jstring str, char * out, size_t maxLength)
{
jsize len;
if (str == NULL)
{
return false;
}
len = env->GetStringUTFLength(str);
if (len >= (jsize)maxLength)
{
return false;
}
env->GetStringUTFRegion(str, 0, env->GetStringLength(str),(char*)out);
out[len] = 0;
return true;
}
size_t cq_wcslen(wchar_t* str)
{
size_t len = 0;
while (*str++)
len++;
return len;
}
PinyinIme是通過JNI規(guī)則對核心搜索庫的封裝:
頭文件:
#pragma once
bool JNaviCore_Pinyin_register(JNIEnv * env);
void JNaviCore_Pinyin_unregister(JNIEnv * env);
C++文件:
#include"com_pinyin_common.h"
#include "com_pinyin_PinyinIme.h"
#define REGISTER_CLASS "com/pinyin/PinyinIme"
#define element_of(o) (sizeof(o) / sizeof(o[0]))
#define UNUSED_VAR(o) ((o) = (o))
using namespace ime_pinyin;
jclass stringClass;
typedef struct CppCommonJNI
{
jobject pointCls;
jmethodID ndsRectCtrMid;
} CppCommonJNI;
static CppCommonJNI g_CppCommonJNI;
/*
* Class: com_pinyin_PinyinIme
* Method: nativeOpenDecoderFromAssets
* Signature: (IJJLjava/lang/String;)Z
*/
static jboolean nativeOpenDecoderFromAssets(JNIEnv* env, jclass thiz, jint file, jlong startOffset, jlong length, jstring dictionaryName)
{
char cdictionaryName[128];
if (!jstring2cqCHAR(env, dictionaryName, cdictionaryName, element_of(cdictionaryName)))
{
return JNI_FALSE;
}
return im_open_decoder_fd(file, startOffset, length, cdictionaryName);
}
/*
* Class: com_pinyin_PinyinIme
* Method: nativeOpenDecoder
* Signature: (Ljava/lang/String;Ljava/lang/String;)Z
*/
static jboolean nativeOpenDecoder(JNIEnv* env, jclass thiz, jstring fnSysDict, jstring fnUsrDict)
{
char csysDict[128];
char cuserDict[128];
if (!jstring2cqCHAR(env, fnSysDict, csysDict, element_of(csysDict)))
{
return JNI_FALSE;
}
if (!jstring2cqCHAR(env, fnUsrDict, cuserDict, element_of(cuserDict)))
{
return JNI_FALSE;
}
return im_open_decoder(csysDict, cuserDict) ? JNI_TRUE : JNI_FALSE;
}
/*
* Class: com_pinyin_PinyinIme
* Method: nativeCloseDecoder
* Signature: ()Z
*/
static void nativeCloseDecoder(JNIEnv* env, jclass thiz)
{
im_close_decoder();
}
/*
* Class: com_pinyin_PinyinIme
* Method: nativeSearchAllNum
* Signature: (Ljava/lang/String;)I
*/
static jint nativeSearchAllNum(JNIEnv* env, jclass thiz, jstring keyWord)
{
char ckeyWord[128];
if (!jstring2cqCHAR(env, keyWord, ckeyWord, element_of(ckeyWord)))
{
return 0;
}
return im_search(ckeyWord, strlen(ckeyWord));
}
/*
* Class: com_pinyin_PinyinIme
* Method: nativeSearchAll
* Signature: (Ljava/lang/String;)[Ljava/lang/String;
*/
static jobjectArray nativeSearchAll(JNIEnv* env, jclass thiz, jstring keyWord)
{
char ckeyWord[128];
if (!jstring2cqCHAR(env, keyWord, ckeyWord, element_of(ckeyWord)))
{
return NULL;
}
size_t numberOfResults = im_search(ckeyWord, strlen(ckeyWord));
jobjectArray searchAggregate = env->NewObjectArray((jsize)numberOfResults, stringClass, NULL);
char16 candBuffer[32];
for (size_t i = 0; i < numberOfResults; i++){
wchar_t* candidataStr = (wchar_t*)im_get_candidate(i, (char16*)candBuffer, element_of(candBuffer));
jobject job = env->NewString((const jchar*)candidataStr, cq_wcslen(candidataStr));
env->SetObjectArrayElement(searchAggregate, i, job);
(env)->DeleteLocalRef(job);
}
return searchAggregate;
}
/*
* Class: com_pinyin_PinyinIme
* Method: nativeGetPredictsNum
* Signature: (Ljava/lang/String;)I
*/
static jint nativeGetPredictsNum(JNIEnv* env, jclass thiz, jstring keyWord)
{
char16(*predicts)[ime_pinyin::kMaxPredictSize + 1];
wchar_t cstr[2048];
size_t predictsSize;
if (!jstring2cqWCHAR(env, keyWord, cstr, element_of(cstr))){
return 0;
}
predictsSize = ime_pinyin::im_get_predicts((char16*)cstr, predicts);
return predictsSize;
}
/*
* Class: com_pinyin_PinyinIme
* Method: nativeGetPredictsAggregate
* Signature: (Ljava/lang/String;)[Ljava/lang/String;
*/
static jobjectArray nativeGetAllPredicts(JNIEnv* env, jclass thiz, jstring keyWord)
{
char16(*predicts)[ime_pinyin::kMaxPredictSize + 1];
wchar_t cstr[2048];
size_t predictsSize;
if (!jstring2cqWCHAR(env, keyWord, cstr, element_of(cstr))){
return NULL;
}
predictsSize = ime_pinyin::im_get_predicts((char16*)cstr, predicts);
jobjectArray predictsAggregate = env->NewObjectArray(predictsSize, stringClass, NULL);
for (size_t i = 0; i < predictsSize; i++)
{
wchar_t* predict = (wchar_t*)predicts[i];
jobject job = env->NewString((const jchar*)predict, (jsize)cq_wcslen(predict));
env->SetObjectArrayElement(predictsAggregate, i, job);
env->DeleteLocalRef(job);
}
return predictsAggregate;
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeflushCache
* Signature: ()V
*/
static void nativeflushCache(JNIEnv* env, jclass thiz)
{
im_flush_cache();
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeEnableShmAsSzm
* Signature: (Z)V
*/
static void nativeEnableShmAsSzm(JNIEnv* env, jclass thiz, jboolean enable)
{
im_enable_shm_as_szm(enable);
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeEnableYmAsSzm
* Signature: (Z)V
*/
static void nativeEnableYmAsSzm(JNIEnv* env, jclass thiz, jboolean enable)
{
im_enable_ym_as_szm(enable);
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeCancelLastChoice
* Signature: ()I
*/
static jint nativeCancelLastChoice(JNIEnv* env, jclass thiz)
{
return (jint)im_cancel_last_choice();
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeChoose
* Signature: (I)I
*/
static jint nativeChoose(JNIEnv* env, jclass thiz, jint candId)
{
return (jint)im_choose(candId);
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeGetSpellingString
* Signature: ()Lco/pinyin/PinyinIme$SpellingString
*/
static jobject nativeGetSpellingString(JNIEnv* env, jclass thiz)
{
size_t candidate;
size_t *decodedLen = &candidate;
const char* searchKey = im_get_sps_str(decodedLen);
return CommonJNI_createSpellingString(env, env->NewString((const jchar*)searchKey, strlen(searchKey)),(jint)candidate);
}
/*
* Class: com_pinyin_PinyinIme
* Method: nativeGetCandidate
* Signature: (I)Ljava/lang/String;
*/
static jstring nativeGetCandidate(JNIEnv* env, jclass thiz, jint index)
{
size_t i = index;
wchar_t candBuffer[32] = { 0 };
wchar_t* candidataStr = (wchar_t*)im_get_candidate(i, (char16*)candBuffer, sizeof(candBuffer));
return env->NewStringUTF((char *)candidataStr);
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeResetSearch
* Signature: ()V
*/
static void nativeResetSearch(JNIEnv* env, jclass thiz)
{
im_reset_search();
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeGetFixedLen
* Signature: ()I
*/
static jint nativeGetFixedLen(JNIEnv* env, jclass thiz)
{
return (jint)im_get_fixed_len();
};
/*
* Class: com_pinyin_PinyinIme
* Method: nativeDelsearch
* Signature: (I)I
*/
static jint nativeDelsearch(JNIEnv* env, jclass thiz, jint pos)
{
return (jint)im_delsearch(4, false, true);
}
static JNINativeMethod g_methods[] =
{
{ "nativeOpenDecoder", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)nativeOpenDecoder },
{ "nativeOpenDecoderFromAssets", "(IJJLjava/lang/String;)Z", (void*)nativeOpenDecoderFromAssets },
{ "nativeCloseDecoder", "()V", (void*)nativeCloseDecoder },
{ "nativeSearchAllNum", "(Ljava/lang/String;)I", (void*)nativeSearchAllNum },
{ "nativeSearchAll", "(Ljava/lang/String;)[Ljava/lang/String;", (void*)nativeSearchAll },
{ "nativeGetPredictsNum", "(Ljava/lang/String;)I", (void*)nativeGetPredictsNum },
{ "nativeGetAllPredicts", "(Ljava/lang/String;)[Ljava/lang/String;", (void*)nativeGetAllPredicts },
{ "nativeflushCache", "()V", (void*)nativeflushCache },
{ "nativeEnableShmAsSzm", "(Z)V", (void*)nativeEnableShmAsSzm },
{ "nativeEnableYmAsSzm", "(Z)V", (void*)nativeEnableYmAsSzm },
{ "nativeCancelLastChoice", "()I", (void*)nativeCancelLastChoice },
{ "nativeChoose", "(I)I", (void*)nativeChoose },
{ "nativeGetSpellingString", "()Lcom/pinyin/PinyinIme$SpellingString;", (void*)nativeGetSpellingString },
{ "nativeGetCandidate", "(I)Ljava/lang/String;", (void*)nativeGetCandidate },
{ "nativeResetSearch", "()V", (void*)nativeResetSearch },
{ "nativeGetFixedLen", "()I", (void*)nativeGetFixedLen },
{ "nativeDelsearch", "(I)I", (void*)nativeDelsearch }
};
/*
動態(tài)注冊
*/
bool JNaviCore_Pinyin_register(JNIEnv * env)
{
jclass findClass = env->FindClass(REGISTER_CLASS);
if (env->RegisterNatives(findClass, g_methods, element_of(g_methods)) < 0)
{
return false;
}
CppCommonJNI_init(env);
stringClass = CppCommonJNI_getStringCls();
env->DeleteLocalRef(findClass);
return true;
}
void JNaviCore_Pinyin_unregister(JNIEnv * env)
{
CppCommonJNI_cleanup(env);
}
android代碼的實現(xiàn)很簡單,通過調(diào)用本地搜索庫來實現(xiàn)搜索功能
本地安卓項目對JNI調(diào)用的封裝:
package com.pinyin;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.util.Log;
import java.io.IOException;
/**
* 谷歌輸入法——詞庫功能:全拼、簡拼、聯(lián)想詞
*/
public class PinyinIme {
private static AssetFileDescriptor fileDescriptor;
private static AssetManager assetManager;
private static int file;
private static long startOffset;
private static long length;
/**
* 輸入的字符串信息
*/
public static class SpellingString {
/**
* 輸入字符串,比如"wangfujingmeishi"
*/
public String spellingStr;
/**
* 解碼器已經(jīng)解碼的長度,比如輸入"wangfujingmeishi",只解碼wangfujin時,decodedLen此時為9
*/
public int decodedLen;
public SpellingString(String spelling, int decodedLen) {
this.decodedLen = decodedLen;
this.spellingStr = spelling;
}
}
private static native SpellingString nativeGetSpellingString();
private static native boolean nativeOpenDecoder(String sysDict, String usrDict);
private static native boolean nativeOpenDecoderFromAssets(int file, long startOffset, long length, String usrDict);
private static native void nativeCloseDecoder();
private static native int nativeDelsearch(int pos);
private static native String[] nativeSearchAll(String keyWord);
private static native int nativeGetPredictsNum(String keyWord);
private static native String[] nativeGetAllPredicts(String keyWord);
private static native void nativeflushCache();
private static native void nativeEnableShmAsSzm(boolean enable);
private static native void nativeEnableYmAsSzm(boolean enable);
private static native int nativeCancelLastChoice();
private static native int nativeChoose(int candId);
private static native String nativeGetCandidate(int candId);
private static native void nativeResetSearch();
private static native int nativeGetFixedLen();
private static native int nativeSearchAllNum(String keyWord);
private static final String TAG = "[PinyinIme]";
private static boolean mOpenSucceeded = false;
/**
* @note :所有的功能使用前必須打開引擎
* 通過系統(tǒng)字典和用戶字典打開解碼器引擎
*/
public static void openDecoderFromAssets(String usrDict, Context context) {
try {
assetManager = context.getAssets();
fileDescriptor = assetManager.openFd("gpinyindict/dict_pinyin32.dat.png");
startOffset = fileDescriptor.getStartOffset();
length = fileDescriptor.getLength();
file = fileDescriptor.getParcelFileDescriptor().detachFd();
fileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
if (!mOpenSucceeded) {
mOpenSucceeded = nativeOpenDecoderFromAssets(file, startOffset, length, usrDict);
if (!mOpenSucceeded) {
Log.e(TAG, "[openDecoderFromAssets] failed to open decoder, assetFile is " + file + ", userDict is " + usrDict);
}
} else {
Log.e(TAG, "[openDecoderFromAssets] has opned!");
}
}
/**
* 通過系統(tǒng)和用戶字典文件名打開解碼器引擎
*
* @param sysDict 系統(tǒng)字典的文件名
* @param usrDict 用戶字典的文件名
*/
public static void openDecoder(String sysDict, String usrDict) {
if (!mOpenSucceeded) {
mOpenSucceeded = nativeOpenDecoder(sysDict, usrDict);
if (!mOpenSucceeded) {
Log.e(TAG, "[openDecoder] failed to open decoder, sysDict is " + sysDict + ", userDict is " + usrDict);
}
} else {
Log.e(TAG, "[openDecoder] has opned!");
}
}
/**
* @return 返回引擎打開狀態(tài)
*/
public static boolean isInited() {
return mOpenSucceeded;
}
/**
* 關(guān)閉解碼器引擎
*/
public static void closeDecoder() {
if (mOpenSucceeded) {
nativeCloseDecoder();
mOpenSucceeded = false;
}
}
/**
* @param keyWord 要搜索的關(guān)鍵字
* @return 關(guān)鍵字、常用詞 搜索候選數(shù)
*/
public static int searchAllNum(String keyWord) {
if (mOpenSucceeded) {
return nativeSearchAllNum(keyWord);
}
return 0;
}
/**
* ed:如輸入 z,會返回 "在","中","這"等
*
* @param pinyin 搜索拼音
* @return 拼音對應的所有漢字詞組
*/
public static String[] searchAll(String pinyin) {
if (mOpenSucceeded) {
return nativeSearchAll(pinyin);
}
return null;
}
/**
* 聯(lián)想字候選數(shù)
*
* @param keyWord 聯(lián)想關(guān)鍵字
* @return 候選聯(lián)想字個數(shù)
*/
public static int getPredictsNum(String keyWord) {
if (mOpenSucceeded) {
return nativeGetPredictsNum(keyWord);
}
return 0;
}
/**
* 聯(lián)想字搜索結(jié)果集合
*
* @param keyWord 聯(lián)想關(guān)鍵字
* @return 候選聯(lián)想字
* eg:如輸入"大",會返回 "家","學","概"等
*/
public static String[] getAllPredicts(String keyWord) {
if (mOpenSucceeded) {
String[] tasks = nativeGetAllPredicts(keyWord);
return tasks;
}
return null;
}
/**
* 將緩存數(shù)據(jù)刷新到持久內(nèi)存
*/
public static void flushCache() {
if (mOpenSucceeded) {
nativeflushCache();
}
}
/**
* 刪除指定位置的字符后搜索候選數(shù)
*
* @param pos 指定位置
* @return 刪除指定位置的字符后重新搜索返回候選數(shù)
*/
public static int delSearch(int pos) {
if (mOpenSucceeded) {
return nativeDelsearch(pos);
}
return -1;
}
/**
* 啟用首字母為聲母的查詢模式<br>
* 默認:關(guān)閉
*/
public static void enableShmAsSzm(boolean enable) {
if (mOpenSucceeded) {
nativeEnableShmAsSzm(enable);
}
}
/**
* 啟用首字母為韻母的查詢模式<br>
* 默認:關(guān)閉
*/
public static void enableYmAsSzm(boolean enable) {
if (mOpenSucceeded) {
nativeEnableYmAsSzm(enable);
}
}
/**
* 取消最后一個選擇,或恢復選擇前的最后一個操作
*
* @return 返回更新后的候選詞總數(shù)
*/
public static int cancelLastChoice() {
if (mOpenSucceeded) {
return nativeCancelLastChoice();
}
return 0;
}
/**
* 獲取常用詞中要選擇并使其固定的候選人的ID
*
* @return 返回更新后的候選詞總數(shù)
* @candId 常用詞中要選擇并使其固定的候選人的ID
*/
public static int choose(int candId) {
if (mOpenSucceeded) {
return nativeChoose(candId);
}
return 0;
}
/**
* 獲取解碼器保留的拼寫字符串:正在搜索的詞匯和拼寫中有多少個字符
*
* @return 返回解碼器保留的拼寫字符串
*/
public static SpellingString getSpellingString() {
if (mOpenSucceeded) {
return nativeGetSpellingString();
}
return null;
}
/**
* 獲取候選(或選項)字符串
*
* @param candId 候選字的ID,一般從0開始
* @return 返回候選(或選項)字符串
*/
public static String getCandidate(int candId) {
if (mOpenSucceeded) {
return nativeGetCandidate(candId);
}
return null;
}
/**
* 重置搜索結(jié)果,發(fā)起新的搜索時需要調(diào)用
*/
public static void resetSearch() {
if (mOpenSucceeded) {
nativeResetSearch();
}
}
/**
* @return 返回中文字符的固定拼寫ID數(shù)
*/
public static int getFixedLen() {
if (mOpenSucceeded) {
return nativeGetFixedLen();
}
return 0;
}
}
下面就是Main的調(diào)用方法。通過ndk-build將生成的so庫引入從而實現(xiàn)搜索功能

package com.pinyin;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.pinyin.jni4.R;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("pinyin");
}
private long startOffset;
private long length;
private int sysFd;
private AssetFileDescriptor fileDescriptor;
private TextView mtvShow;
private EditText medPinyin;
private EditText medAssociation;
private AssetManager assetManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
assetManager = getAssets();
mtvShow = findViewById(R.id.tv_show);
medPinyin = findViewById(R.id.ed_pinyin);
medAssociation = findViewById(R.id.ed_lianxiang);
configure();
}
private void configure() {
try {
fileDescriptor = assetManager.openFd("gpinyindict/dict_pinyin32.dat.png");
startOffset = fileDescriptor.getStartOffset();
length = fileDescriptor.getLength();
sysFd = fileDescriptor.getParcelFileDescriptor().detachFd();
fileDescriptor.close();
} catch (IOException e) {
Log.e("sss", e.toString());
}
}
public void searcha(View view) {
PinyinIme.openDecoderFromAssets("gpinyindict/user_pinyin.dat.png", this);
PinyinIme.enableShmAsSzm(true);
PinyinIme.resetSearch();
String ss = "";
String[] strings = PinyinIme.searchAll(medPinyin.getText().toString());
if (strings == null) {
Toast.makeText(this, "1111", Toast.LENGTH_SHORT).show();
return;
}
for (int i = 0; i < strings.length; i++) {
ss += (strings[i] + " ,");
}
mtvShow.setText(ss);
}
public void searchb(View view) {
PinyinIme.openDecoderFromAssets("gpinyindict/user_pinyin.dat.png", this);
String ss = "";
String[] predictsAggregate = PinyinIme.getAllPredicts(medAssociation.getText().toString());
for (int i = 0; i < predictsAggregate.length; i++) {
ss += (predictsAggregate[i] + " ,");
}
mtvShow.setText(ss);
}
}
總結(jié)
JNI開發(fā)需要多注意:
原始類型和引用類型的對應關(guān)系。
GolbalReference和LocalReference的區(qū)別以及使用場景。
主線程和子線程中的類加載。
歡迎大家的討論。