引擎初始化首先是在Java層調用native的初始化方法,Java層調用如下:
private void initPinyinEngine() {
byte usr_dict[];
usr_dict = new byte[MAX_PATH_FILE_LENGTH];
// Here is how we open a built-in dictionary for access through
// a file descriptor...
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.dict_pinyin);
if (Environment.getInstance().needDebug()) {
Log.i("foo", "Dict: start=" + afd.getStartOffset()
+ ", length=" + afd.getLength() + ", fd="
+ afd.getParcelFileDescriptor());
}
if (getUsrDictFileName(usr_dict)) {
inited = nativeImOpenDecoderFd(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), usr_dict);
}
try {
afd.close();
} catch (IOException e) {
}
}
根據構建後的二進制文件獲取 一個文件描述符,將該文件描述符傳入native進行初始化,其實就是load之前build中產生的一些數組、樹結構以及一些值,native經過一些列調用,最終是調用用了matrixsearch類的init方法:
bool MatrixSearch::init_fd(int sys_fd, long start_offset, long length,
const char *fn_usr_dict) {
if (NULL == fn_usr_dict)
return false;
if (!alloc_resource())
return false;
LOGD("init_fd sys_fd == %d , start_offset == %l, and length == %l", sys_fd, start_offset, length);
if (!dict_trie_->load_dict_fd(sys_fd, start_offset, length, 1, kSysDictIdEnd))
return false;
LOGD("fn_usr_dict == %c", *fn_usr_dict);
if (!user_dict_->load_dict(fn_usr_dict, kUserDictIdStart, kUserDictIdEnd)) {
delete user_dict_;
user_dict_ = NULL;
} else {
user_dict_->set_total_lemma_count_of_others(NGram::kSysDictTotalFreq);
}
LOGD("size of dict_trie == %d", sizeof(dict_trie_));
reset_search0();
inited_ = true;
return true;
}
調用了兩個load_dict,一個是dict_trie的,用來加載sys_fd,這個sys_fd就是build後生成的二進制文件dict_pinyin.dat文件,這裏使用其文件描述符來進行加載操作,另一個是調用user_dict的load_dict方法,此方法用來加載用戶詞典,fn_usr_dict變量爲:/data/user/0/com.android.inputmethod.latin/files/user_dict.dat文件,這個dat文件是用戶在使用輸入法過程中產生的用戶字典,初次安裝應用的話用戶字典初始狀態爲空,因此初始化的過程主要邏輯在加載系統詞典:
bool DictTrie::load_dict_fd(int sys_fd, long start_offset,
long length, LemmaIdType start_id,
LemmaIdType end_id) {
if (start_offset < 0 || length <= 0 || end_id <= start_id)
return false;
FILE *fp = fdopen(sys_fd, "rb");
if (NULL == fp)
return false;
if (-1 == fseek(fp, start_offset, SEEK_SET)) {
fclose(fp);
return false;
}
free_resource(true);
dict_list_ = new DictList();
if (NULL == dict_list_) {
fclose(fp);
return false;
}
SpellingTrie &spl_trie = SpellingTrie::get_instance();
NGram &ngram = NGram::get_instance();
if (!spl_trie.load_spl_trie(fp) || !dict_list_->load_list(fp) ||
!load_dict(fp) || !ngram.load_ngram(fp) ||
ftell(fp) < start_offset + length ||
total_lma_num_ > end_id - start_id + 1) {
free_resource(true);
fclose(fp);
return false;
}
fclose(fp);
return true;
}
主要加載邏輯都在第四個if語句中:
1、spl_trie.load_spl_trie()從stream中讀取相關數據結構並組織拼音樹結構。
2、dict_list_->load_list(fp)從fp中讀取單漢字列表scis以及buf_413個合法音節數組等,load_list的過程和save_list的流程相同,按照save的順序去讀取對應的數組。
3、load_dict(fp)從fp中恢復lma_node_num_le0_、lma_node_num_ge1_、lma_idx_buf_len_、top_lmas_num_、lma_idx_buf_、nodes_ge1_和root_
4、ngram.load_ngram(fp)從fp中加載build的時候save的數組和數值:idx_num_、freq_codes_、lma_freq_idx_,關於ngram信息構建流程可以參考前面的Google原生輸入法LatinIME詞庫構建流程分析(三)--N-gram信息構建
綜上,load_dict的流程其實就是把build過程中的一些數組和數值從二進制文件流中按照保存次序一次讀取出來,在search或者predict的過程中,綜合這些數據結構實現輸入文字的查找、預測過程。