为了解决 SampleIME + jvm 任务多开及 word 闪退的问题,对 动态输入法 TSF 的启动过程进行了分析。
顶层是 CSampleIME,在其构造函数中,startJVM() 启动 jvm(创建的 jvm 似乎只为了得到 penv0) 。这个过程中,对 penv0(jvm 环境), mainclass0(jar文件中的主类), dTyper0(主类的实例)进行初始化。
然后,在 CSampleIME::_AddTextProcessorEngine() 中:
// Create composition processor engine
if (_pCompositionProcessorEngine == nullptr)
{
_pCompositionProcessorEngine = new (std::nothrow) CCompositionProcessorEngine();
// set JVM
_pCompositionProcessorEngine->setJvm(penv0,&mainclass0,VER,&dTyper0,startinfo);
}
_pCompositionProcessorEngine 是 CSmapleIME 的一个成员指针,它又包含了 ->dTyperJar,后者是 jar 文件的调用管理接口。它还包含 ->setJvm 方法。在 ->setJvm()中,做两件事:->dTyperJar.setJvm(), ->dTyperJar.startup().
在 CSmapleIME 中启动的 jvm,其环境指针 penv0,主类 mainclass0,主类实例 dTyper0,通过 _pCompositionProcessorEngine->setJvm()传递给其成员 ->dTyperJar。
dTyperJar 是一个接口,它有 3 个重要成员:penv, mainclass, dTyper。penv 可理解为连接到宿主线程的 jvm 环境,mainclass 是 mainclass0 的本地副本,dTyper = penv->NewGlobalRef(*_dTyper0); 可理解为宿主线程上挂接的 dTyper0。
dTyperJar 还有两个重要的成员函数:setJvm() 和 startup(). 在 setJvm()中,把 penv0, mainclass0 进行本地存储,penv0->GetJavaVM(&jvm); jvm->AttachCurrentThread((void **)&penv, &args);
至此,dTyper 完成了初始化,可以执行打字功能。当需要调用 dTyper 中的函数 func_xxx() 时,
jmethodID mid1 = penv->GetMethodID(mainclass, "func_xxx", "()Ljava/lang/String;");
jstring msg1 = (jstring)penv->CallObjectMethod(dTyper, mid1);
目前看起来,penv0->GetJavaVM(&jvm); jvm->AttachCurrentThread((void **)&penv, &args); 都有可能造成 word 闪退。