Android 输入法的实现(全拼、简拼、联想词)

一、概述

JNI(Java Native Interface):Java本地开发接口。它是一个协议,通过这个协议Java和C/C++代码可以相互调用。
为什么要用JNI?
Java不是本地语言需要翻译,效率上C/C++执行起来会更高、安全角度上来说Java的反编译要比C语言容易、代码移植等等。
本文通过JNI实现了输入法的核心搜索功能(全拼、简拼、联想词)。

二、效果展示

输入法搜索.gif

三、代码实现

C++层代码结构.png

C++层代码主要是开源的谷歌输入法头文件,jni包下是对C++的封装。
common类是对自定义数据类型和方法的封装。
头文件:
1.JNI中Log的打印,用在Android中调试代码
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.结构体的创建和声明。注意对GolbalReference全局引用要调用DeleteGlobalRef将其释放,全局引用会阻止它所引用的对象被GC回收从而造成内存泄漏。LocalReference 本地引用在方法执行完毕后会被自动删除。
2.JNI的动态注册。静态注册在用到时才去加载效率不高,动态注册的方式是固定的。
3.jstring转字符和宽字符的封装,防止多次的判空和声明。
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规则对核心搜索库的封装:
头文件:

#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 }
};

/*
动态注册
*/

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代码的实现很简单,通过调用本地搜索库来实现搜索功能

本地安卓项目对JNI调用的封装:

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;

/**
 * 谷歌输入法——词库功能:全拼、简拼、联想词
 */

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;
        /**
         * 解码器已经解码的长度,比如输入"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 :所有的功能使用前必须打开引擎
     * 通过系统字典和用户字典打开解码器引擎
     */
    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!");
        }
    }

    /**
     * 通过系统和用户字典文件名打开解码器引擎
     *
     * @param sysDict 系统字典的文件名
     * @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 返回引擎打开状态
     */
    public static boolean isInited() {
        return mOpenSucceeded;
    }

    /**
     * 关闭解码器引擎
     */
    public static void closeDecoder() {
        if (mOpenSucceeded) {
            nativeCloseDecoder();
            mOpenSucceeded = false;
        }
    }

    /**
     * @param keyWord 要搜索的关键字
     * @return 关键字、常用词 搜索候选数
     */
    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;
    }

    /**
     * 联想字候选数
     *
     * @param keyWord 联想关键字
     * @return 候选联想字个数
     */
    public static int getPredictsNum(String keyWord) {
        if (mOpenSucceeded) {
            return nativeGetPredictsNum(keyWord);
        }
        return 0;
    }


    /**
     * 联想字搜索结果集合
     *
     * @param keyWord 联想关键字
     * @return 候选联想字
     * eg:如输入"大",会返回 "家","学","概"等
     */
    public static String[] getAllPredicts(String keyWord) {
        if (mOpenSucceeded) {
            String[] tasks = nativeGetAllPredicts(keyWord);
            return tasks;
        }
        return null;
    }

    /**
     * 将缓存数据刷新到持久内存
     */
    public static void flushCache() {
        if (mOpenSucceeded) {
            nativeflushCache();
        }
    }

    /**
     * 删除指定位置的字符后搜索候选数
     *
     * @param pos 指定位置
     * @return 删除指定位置的字符后重新搜索返回候选数
     */
    public static int delSearch(int pos) {
        if (mOpenSucceeded) {
            return nativeDelsearch(pos);
        }
        return -1;
    }


    /**
     * 启用首字母为声母的查询模式<br>
     * 默认:关闭
     */
    public static void enableShmAsSzm(boolean enable) {
        if (mOpenSucceeded) {
            nativeEnableShmAsSzm(enable);
        }
    }


    /**
     * 启用首字母为韵母的查询模式<br>
     * 默认:关闭
     */
    public static void enableYmAsSzm(boolean enable) {
        if (mOpenSucceeded) {
            nativeEnableYmAsSzm(enable);
        }
    }


    /**
     * 取消最后一个选择,或恢复选择前的最后一个操作
     *
     * @return 返回更新后的候选词总数
     */
    public static int cancelLastChoice() {
        if (mOpenSucceeded) {
            return nativeCancelLastChoice();
        }
        return 0;
    }


    /**
     * 获取常用词中要选择并使其固定的候选人的ID
     *
     * @return 返回更新后的候选词总数
     * @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;
    }


    /**
     * 重置搜索结果,发起新的搜索时需要调用
     */
    public static void resetSearch() {
        if (mOpenSucceeded) {
            nativeResetSearch();
        }
    }

    /**
     * @return 返回中文字符的固定拼写ID数
     */
    public static int getFixedLen() {
        if (mOpenSucceeded) {
            return nativeGetFixedLen();
        }
        return 0;
    }

}

下面就是Main的调用方法。通过ndk-build将生成的so库引入从而实现搜索功能

谷歌拼音库.png
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);
    }
}

总结

JNI开发需要多注意:
原始类型和引用类型的对应关系。
GolbalReference和LocalReference的区别以及使用场景。
主线程和子线程中的类加载。
欢迎大家的讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容