当前位置:   article > 正文

Android输入法IME(三)之 管理端(IMMS)启动流程

Android输入法IME(三)之 管理端(IMMS)启动流程

2.2. IME管理端(IMMS)初始化流程

IMMS运行在system server进程中,属于系统服务的一部分,用于控制输入法的显示/隐藏、切换、绑定等操作。 涉及代码文件路径:

IMMS运行在system server进程中,属于系统服务的一部分,用于控制输入法的显示/隐藏、切换、绑定等操作。 涉及代码文件路径: frameworks/base/services/java/com/android/server/SystemServer.java frameworks/base/services/core/java/com/android/server/SystemServiceManager.java frameworks/base/core/java/android/os/SystemService.java frameworks/base/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java frameworks/base/packages/SettingsProvider/res/values/defaults.xml

2.2.1. 初始化函数流程梳理

  1. # 我们从systemserver的startOtherServices函数开始梳理
  2. # 此处需要注意,因为我梳理的是IMMS,而Google还提供了一个MultiClientInputMethodManagerService多客户端输入法服务进程,此处不梳理
  3. # PS:从InputMethodManagerService代码文件中可以看到,Lifecycle是里面的一个内部类,继承systemservice
  4. SystemServer.java -- startOtherServices,然后通过SystemServiceManager的startService启动IMMS,传入class name"InputMethodManagerService.Lifecycle.class"
  5. ---> SystemServiceManager.java -- startService有好几个重载的方法,说明下:
  6. 1)第一个startService方法,入参className即"InputMethodManagerService.Lifecycle.class",将其作为入参调用loadClassFromLoader
  7. 2)loadClassFromLoader会通过反射方法得到具体的Class类,返回Class<SystemService>类型的服务类,即继承SystemService的Lifecycle
  8. 3)调用第二个startService方法,入参即serviceClass
  9. 4)先通过"SystemService.class.isAssignableFrom(serviceClass)"判断该类是否是SysteService的子类
  10. 5)然后通过反射构造类的实例"service=constructor.newInstance(mContext)",即实例化Lifecycle类(重点)
  11. 6)调用第三个startService方法,入参该Lifecycle对象
  12. 2)先将该service添加到mServices列表中,然后调用SystemService.java的onStart函数
  13. ---> InputMethodManagerService.java -- 通过上面的流程看到,此处会先调用Lifecycle类的构造函数,然后调用onStart函数
  14. 1)构造函数会创建IMMS实例,即"InputMethodManagerService mService=new InputMethodManagerService(context)"
  15. 2)onStart函数会将该mService通过publishBinderService方法发布到系统服务中,以便其他进行可以进行Binder获取到(即添加到dev/binder域管理)
  16. # 主要讲述IMMS对象被创建,从构造函数梳理
  17. ---》 调用构造函数,主要用于注册一些监听事件, 获取必须的系统服务, UI相关的组件等

PS:

  1. SystemService启动输入法服务时,会有个判断启动IMMS还是MCIMMS。MULTI_CLIENT_IME_ENABLED(即persist.debug.multi_client_ime或ro.sys.multi_client_ime)开启,启动MultiClientInputMethodManagerService服务,否则启动InputMethodManagerService服务
  2. 关于MultiClientInputMethodManagerService就是多会话输入法,支持每屏幕焦点是启用此功能的前提。如果不支持,则无法启用此功能。由于安全限制,每屏幕焦点限制规定只有一小部分设备支持此功能。(详细参考Google官方文档和源码)

2.2.2. systemRunning函数流程梳理

  1. # 我们从systemserver的startOtherServices函数开始梳理
  2. # startBootPhase在服务startservice后执行,该函数将service分段处理,
  3. # 例如此处IMMS在SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE(200)和SystemService.PHASE_LOCK_SETTINGS_READY(480)之间
  4. SystemServer.java -- startOtherServices,然后通过SystemServiceManager的startBootPhase
  5. ---> SystemServiceManager.java -- startBootPhase遍历两个分段之间的服务,然后调用对应service的onBootPhase
  6. ---> InputMethodManagerService.java -- 调用Lifecycle类的onBootPhase函数,然后调用InputMethodManagerService的systemRunning函数,主要内容:
  7. 1)MyPackageMonitor内部类register注册,监听安装包的变化,包含安装,卸载,更新等
  8. 2)SettingsObserver注册,监听当前用户的各种输入法相关的settingprovider变化,例如:默认输入法,输入法列表,输入法语言等
  9. 3)getSelectedInputMethod获取用户设置的输入法default_input_method,此处是查询settings数据库的默认输入法(frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java)
  10. 4)buildInputMethodListLocked,如果没有默认输入法则入参false,该函数内容如下:
  11. ---》 查询输入法服务信息,然后将信息储存到mMethodList,mMethodMap,mMyPackageMonitor中;
  12. ---》 调用chooseNewDefaultIMELocked选择一个新的输入法;
  13. ---》 updateInputMethodsFromSettingsLocked遍历所有输入法,如果输入法存在被禁用的组件,则重新启用调用setInputMethodLocked方法完成对输入法设置,和输入法发生变化的广播(ACTION_INPUT_METHOD_CHANGED)的发送(该函数中调用setInputMethodLocked)

一般我们修改默认输入法,packages/SettingsProvider/res/values/defaults.xml 数据库配置添加def_input_method和def_enable_input_methods,然后frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java对应添加loadStringSetting加载引用DEFAULT_INPUT_METHOD和ENABLED_INPUT_METHODS

2.2.3. 代码详细说明

  1. //SystemServer.java
  2. private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
  3. ......
  4. // Bring up services needed for UI.
  5. if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
  6. t.traceBegin("StartInputMethodManagerLifecycle");
  7. if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) {
  8. mSystemServiceManager.startService(
  9. MultiClientInputMethodManagerService.Lifecycle.class);
  10. } else {
  11. //启动IMMS服务
  12. mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
  13. }
  14. t.traceEnd();
  15. ......
  16. }
  17. ...
  18. }
  19. //SystemServiceManager.java
  20. //第一个startService函数
  21. public SystemService startService(String className) {
  22. //调用loadClassFromLoader
  23. final Class<SystemService> serviceClass = loadClassFromLoader(className,
  24. this.getClass().getClassLoader());
  25. return startService(serviceClass);
  26. }
  27. private static Class<SystemService> loadClassFromLoader(String className,
  28. ClassLoader classLoader) {
  29. try {
  30. //通过反射方法得到具体的Class类,返回Class<SystemService>类型的服务类,即继承SystemService的Lifecycle
  31. return (Class<SystemService>) Class.forName(className, true, classLoader);
  32. } catch (ClassNotFoundException ex) {
  33. .......
  34. }
  35. }
  36. //第二个startService函数
  37. public <T extends SystemService> T startService(Class<T> serviceClass) {
  38. try {
  39. final String name = serviceClass.getName();
  40. Slog.i(TAG, "Starting " + name);
  41. Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);
  42. // 判断该class该类是否是SysteService的子类
  43. if (!SystemService.class.isAssignableFrom(serviceClass)) {
  44. throw new RuntimeException("Failed to create " + name
  45. + ": service must extend " + SystemService.class.getName());
  46. }
  47. final T service;
  48. try {
  49. //通过反射构造类的实例,即实例化Lifecycle类
  50. Constructor<T> constructor = serviceClass.getConstructor(Context.class);
  51. //newInstance实例化
  52. service = constructor.newInstance(mContext);
  53. } catch (InstantiationException ex) {
  54. throw new RuntimeException("Failed to create service " + name
  55. + ": service could not be instantiated", ex);
  56. } ......
  57. ......
  58. //调用第三个startService
  59. startService(service);
  60. return service;
  61. } finally {
  62. Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
  63. }
  64. }
  65. //第三个startService函数
  66. public void startService(@NonNull final SystemService service) {
  67. // Register it.将service注册到mServices列表中
  68. mServices.add(service);
  69. // Start it.
  70. long time = SystemClock.elapsedRealtime();
  71. try {
  72. //调用该service的onStart函数
  73. service.onStart();
  74. } catch (RuntimeException ex) {
  75. throw new RuntimeException("Failed to start service " + service.getClass().getName()
  76. + ": onStart threw an exception", ex);
  77. }
  78. warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
  79. }
  80. //InputMethodManagerService.java
  81. public static final class Lifecycle extends SystemService {
  82. private InputMethodManagerService mService;
  83. //实例化时调用构造函数
  84. public Lifecycle(Context context) {
  85. super(context);
  86. //创建InputMethodManagerService IMMS对象,然后调用IMMS构造函数
  87. mService = new InputMethodManagerService(context);
  88. }
  89. //在startService中调用到此处
  90. @Override
  91. public void onStart() {
  92. //将IMMS service添加到LocalServices
  93. LocalServices.addService(InputMethodManagerInternal.class,
  94. new LocalServiceImpl(mService));
  95. //发布到系统服务中,以便其他进行可以进行Binder获取到(即添加到dev/binder域管理)
  96. publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/,
  97. DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
  98. }
  99. .......
  100. }
  101. public class InputMethodManagerService extends IInputMethodManager.Stub
  102. implements ServiceConnection, Handler.Callback {
  103. ....
  104. //IMMS构造函数
  105. public InputMethodManagerService(Context context) {
  106. mIPackageManager = AppGlobals.getPackageManager();
  107. mContext = context;
  108. mRes = context.getResources();
  109. mHandler = new Handler(this);
  110. // Note: SettingsObserver doesn't register observers in its constructor.
  111. // SettingsObserver类型,用于监听来自设置的输入法配置, 比如默认输入法, 启用的输入法, 选择的输入法等
  112. mSettingsObserver = new SettingsObserver(mHandler);
  113. mIWindowManager = IWindowManager.Stub.asInterface(
  114. ServiceManager.getService(Context.WINDOW_SERVICE));
  115. mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
  116. mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
  117. mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
  118. mImeDisplayValidator = displayId -> mWindowManagerInternal.getDisplayImePolicy(displayId);
  119. .....
  120. // 状态栏输入法图标名称, 会根据这个名称设置输入法的图标显示
  121. mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
  122. mIsLowRam = ActivityManager.isLowRamDeviceStatic();
  123. // 切换输入法时的通知
  124. Bundle extras = new Bundle();
  125. extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
  126. .....
  127. //获取UID
  128. int userId = 0;
  129. try {
  130. userId = ActivityManager.getService().getCurrentUser().id;
  131. } catch (RemoteException e) {
  132. Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
  133. }
  134. // 最近切换的UID
  135. mLastSwitchUserId = userId;
  136. //应在buildInputMethodListLocked之前创建mSettings
  137. //类型InputMethodSettings,输入法设置对象
  138. mSettings = new InputMethodSettings(
  139. mRes, context.getContentResolver(), mMethodMap, userId, !mSystemReady);
  140. updateCurrentProfileIds();
  141. AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
  142. mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
  143. mSettings, context);
  144. mMenuController = new InputMethodMenuController(this);
  145. }
  146. ......
  147. }

IMMS.java中几个重要的变量:

  1. String mCurMethodId:系统当前默认的输入法id, 可能为空, 与Settings.Secure#DEFAULT_INPUT_METHOD值保持一致,在setInputMethodLocked中赋值
  2. String mCurId:当前已经绑定的输入法id, 如果没有输入法绑定上的话, 值为null
  3. ClientState mCurClient:用于当前激活的IME, 只有持有这个令牌的IME才被系统认可
  4. IInputMethod mCurMethod:当前已经绑定的输入法接口, 如果为null, 说明没有任何输入法连接上
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/683377
推荐阅读
相关标签
  

闽ICP备14008679号