美文网首页Android-NDK/JNIAndroid应用开发那些事Android输入法
Android 输入法的实现(全拼、简拼、联想词)

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

作者: 果园_ | 来源:发表于2019-05-22 01:25 被阅读627次

    一、概述

    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的区别以及使用场景。
    主线程和子线程中的类加载。
    欢迎大家的讨论。

    相关文章

      网友评论

        本文标题:Android 输入法的实现(全拼、简拼、联想词)

        本文链接:https://www.haomeiwen.com/subject/ofrugqtx.html