osfans / trime

同文安卓輸入法平臺3.x/Android-rime/Rime Input Method Engine for Android
http://osfans.github.io/trime/
GNU General Public License v3.0
3.09k stars 372 forks source link

降低弹出键盘的耗时 #713

Closed tumuyan closed 1 year ago

tumuyan commented 2 years ago

Describe the bug 在重新安装App、App崩溃、切换输入法等操作后,再次从输入法选择器里选择同文作为默认输入法,从完成操作到弹出键盘耗时较长。

通过对log信息分析,可以发现主要耗时的位置有:

  1. 13.779 - 16.305 Rime: init() initlialize ,耗时2.6秒。查看代码发现耗时操作在JNI函数https://github.com/osfans/trime/blob/3166f6c5d0eeb4162fe080ab0e905e14d6882d7b/app/src/main/jni/librime_jni/rime.cc#L41
  2. 16.356 - 16.923 Rime: init() set_notification_handler ,耗时0.6秒。执行的代码为:
    set_notification_handler();
    if (!find_session()) {
      if (create_session() == 0) {
        Timber.wtf("Error creating rime session");
        return;
      }
    }
  3. 17.013 - 17.938Config: new() init,耗时0.9秒。查看代码发现是主题参数的载入过程。
  4. 另外 check(full_check) 的耗时不稳定,最高可达5秒,复现不明。被调用的方法在这里 https://github.com/osfans/trime/blob/82f604272592ac5fbfdad1f83a8a126b7c918986/app/src/main/java/com/osfans/trime/Rime.java#L548

Expected behavior

  1. 需要确认上述两个位置到底做了什么事情,是否可以简化(这一部分我力有不逮,需要大佬看看)

  2. 区分完全初始化和正常初始化。在 #712 中我提出了share目录内的主题被重复部署的问题(在提出issue前已经缩减了2秒)。在 https://github.com/osfans/trime/pull/709/commits/8c1c18727e6792aa813b4030f83470f16f0c5b8c 中,我把int方法改为了正常初始化时,如果文件已经部署到了build目录,不重新部署;当用户选择一个主题时,再进行部署。类似的问题应该比较多的 https://github.com/osfans/trime/blob/8c1c18727e6792aa813b4030f83470f16f0c5b8c/app/src/main/java/com/osfans/trime/setup/Config.java#L363

  3. config载入的持续优化

  4. 现在同文基本全在UI进程运行,应该尝试把JNI的部分做到单独的进程中,从而从多方面改善体验: a. 避免UI卡死 b. 充分利用设备性能(毕竟现在只运行在一个核心上 c. 减少重复调用、重复初始化 d. 参数不在弹出键盘前全部完成初始化,先初始化选中的键盘,当用户需要切换键盘时,其他键盘也就完成初始化了。 e. 目前存在这样的bug,届时应该可以一并解决:切换输入法后,如果不等待完成初始化就在文本框内唤起输入法,耗时要大于正常初始化+键盘唤起的时间,同时root_background不能正常显示。

Log

我为整个流程增加了log打印,并删除了部分信息,第一列为秒,冒号前方为所在的类,带括号的部分为所在的函数,后方跟随的信息为即将执行什么方法,可以作为参考。

另外针对初始化过程的log,我们是否可以定一个输出log的规范,从而方便快速筛选需要的信息,确认程序初始化的进度?因为同文目前初始化流程非常长,很多log是调用的依赖、jni所输出的,又属于不可控的部分。 拟定这样:

  1. 初始化过程log统一使用Timber.d()生成,参照下述内容添加(Timber已经包含了输出log的位置所属的方法:空格):筛选log的标记信息 +\t+ 所属的方法+\t+空白/finish/调用其他方法的名称/finish
  2. 拟定筛选的标记为<TrimeInit>
  3. 在App内增加1个开关:输出log到文件
13.673 Trime: onCreate...
13.675 Trime: onCreate() InputFeedbackManager
13.700 Trime: onCreate() KeyboardSwitcher
13.700 KeyboardSwitcher: newOrReset()
13.700 KeyboardSwitcher: newOrReset() getConfig
13.704 Config: new()
13.704 Config: new() prepareRime
13.704 Config: prepareRime()
13.711 Config: prepareRime() copy
13.714 Config: prepareRime() copy2
13.715 Config: prepareRime() copy default.custom.yaml
13.719 Config: prepareRime() Rime.get
13.779 Rime: init()
13.779 Rime: init() setup
13.779 Rime: init() initlialize
16.305 Rime: init() check
16.356 Rime: init() set_notification_handler
16.923 Rime: init() initSchema
17.013 Rime: init() finish
17.013 Config: prepareRime() finish
17.013 Config: new() init
17.938 Config: new() setSoundFromColor
18.016 Config: new() setClipboard&draft
18.017 Config: new() finish
18.017 KeyboardSwitcher: newOrReset() land
18.018 KeyboardSwitcher: newOrReset() getConfig
18.019 KeyboardSwitcher: newOrReset() getKeyboards
18.246 KeyboardSwitcher: newOrReset() setKeyboards
18.247 KeyboardSwitcher: newOrReset() finish
18.247 Trime: onCreate() liquidKeyboard
18.320 Trime: super.onCreate()
18.336 Trime: onCreate() create listener
18.340 Trime: onCreate() finish
18.443 Trime: onCreateInputView()
18.552 Trime: update KeyboardPadding: Trime.loadBackground, padding= 0 0 33, orientation=1
18.557 KeyboardSwitcher: newOrReset()
18.557 KeyboardSwitcher: newOrReset() getConfig
18.558 KeyboardSwitcher: newOrReset() land
18.558 KeyboardSwitcher: newOrReset() getConfig
18.559 KeyboardSwitcher: newOrReset() getKeyboards
18.663 KeyboardSwitcher: newOrReset() setKeyboards
18.663 KeyboardSwitcher: newOrReset() finish
18.663 Trime: onCreateInputView() finish
18.665 KeyboardSwitcher: newOrReset()
18.665 KeyboardSwitcher: newOrReset() getConfig
18.666 KeyboardSwitcher: newOrReset() land
18.666 KeyboardSwitcher: newOrReset() getConfig
18.667 KeyboardSwitcher: newOrReset() getKeyboards
18.746 KeyboardSwitcher: newOrReset() setKeyboards
18.746 KeyboardSwitcher: newOrReset() finish
18.770 Trime: EditorInfo: normal; packageName=mark.via; fieldName=null; actionLabel=null; inputType=524305;
18.772 Trime: onWindowShown...
LOVAaiqg commented 2 years ago

你好,看你分析的这么详细,想问下你有没有部署过拼音9键的方案,我这边发现加载多词库后,9键拼音的方案会出现卡主线程的情况,看代码原因好像是 process_key 这个native方法的耗时不稳定,有时候十几毫秒 但有时会是几秒。

tumuyan commented 2 years ago

看代码原因好像是 process_key 这个native方法的耗时不稳定,有时候十几毫秒 但有时会是几秒。

JNI的问题我解决不了 我26键打特定内容也卡——并不是有时。用工具可以看到CPU可以占满一个核 但是同一方案在PC上使用小狼毫打相同内容没有任何问题,CPU占用没有飙升

Jacobax commented 2 years ago

看代码原因好像是 process_key 这个native方法的耗时不稳定,有时候十几毫秒 但有时会是几秒。

JNI的问题我解决不了 我26键打特定内容也卡——并不是有时。用工具可以看到CPU可以占满一个核 但是同一方案在PC上使用小狼毫打相同内容没有任何问题,CPU占用没有飙升

我也试过在输入特定内容时卡顿, 就是开启简拼的时候, 关闭简拼后暂时没有发现输入卡顿, 试过朙月拼音和小鹤双拼

iovxw commented 2 years ago

process_key 卡顿可以删除 *.userdb 文件夹试试(记得先 sync 备份)

可能是由于 leveldb 升级导致的,对旧的数据库处理有性能问题

由于我这边已经没有可以触发这种情况的 .userdb 来做测试,所以不是很好修,可以的话希望谁能发一份上来(但这包含个人输入数据,比较敏感)

一个比较可行的修复方案是在版本升级后自动重建 .userdb

tumuyan commented 2 years ago

process_key 卡顿可以删除 *.userdb 文件夹试试(记得先 sync 备份)

可能是由于 leveldb 升级导致的,对旧的数据库处理有性能问题

由于我这边已经没有可以触发这种情况的 .userdb 来做测试,所以不是很好修,可以的话希望谁能发一份上来(但这包含个人输入数据,比较敏感)

一个比较可行的修复方案是在版本升级后自动重建 .userdb

我之前已经发现和userdb相关,同步后马上跪,怀疑是不是我的自造词有问题。后来发现删除后是会好一些,如果不同步,打字使用一段时间,也会明显变卡。 恐怕还是拼运复杂(特别是简拼)、候选词数量多所产生的问题

LOVAaiqg commented 2 years ago

process_key 卡顿可以删除 *.userdb 文件夹试试(记得先 sync 备份)

可能是由于 leveldb 升级导致的,对旧的数据库处理有性能问题

由于我这边已经没有可以触发这种情况的 .userdb 来做测试,所以不是很好修,可以的话希望谁能发一份上来(但这包含个人输入数据,比较敏感)

一个比较可行的修复方案是在版本升级后自动重建 .userdb

额,感觉和 *.userdb的关系不大,我删除了该文件夹感觉并没有改善。 我也偏向于是否是9键输入方案的复杂度会高一些? 在我目前用的26键拼音的输入方案上几乎感觉不出很明显的卡顿,但是9键几个键组合的时候就特别明显 比如: 5 2 5 2 5 2 这样,方法的耗时就会超过1秒导致卡死了

另外之前看到有人解决这个卡顿的问题,但他是去掉了方案中 speller下的一些匹配规则 其中发现 - abbrev/^([a-z]).+$/$1/ 这条首字母简拼去掉不会卡, 但是会导致很多情况匹配不到结果了。