多屏模式输入法可以正确切换屏幕展示原理剖析

2023-12-07 12:44

本文主要是介绍多屏模式输入法可以正确切换屏幕展示原理剖析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景

hi,粉丝朋友们:
近期有个学员问到了一个输入法相关问题。刚好梳理了一下输入法相关的在多屏模式的一个展示流程,这里做个记录,也相当于深入理解窗口相关的一篇干货blog。

在这里插入图片描述
在这里插入图片描述
如上面两幅图展示,输入法可以自由自在显示在双屏的任意屏幕,那么下面主要就是来解密输入到底如何做到的自由双屏展示。

分析思路

如果对输入相关业务不知道的话,那么就只能考虑从窗口层面入手,一般输入法都是其实是一个dialog,这个类是SoftInputWindow
在这里插入图片描述一般输入法窗口显示到了其他屏幕,肯定相关的displayid是要变化的这里可以dumpsys window windows可以看出来
输入法窗口在副屏:

Window #1 Window{e79aca4 u0 InputMethod}:mDisplayId=2 rootTaskId=35 mSession=Session{d9973cd 1240:u0a10084} mClient=android.os.BinderProxy@e774f37mOwnerUid=10084 showForAllUsers=false package=com.android.inputmethod.latin appop=NONEmAttrs={(0,0)(fillxfill) gr=BOTTOM CENTER_VERTICAL sim={adjust=pan} ty=INPUT_METHOD fmt=TRANSPARENT wanim=0x1030056 receive insets ignoring z-orderfl=NOT_FOCUSABLE LAYOUT_IN_SCREEN SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDSpfl=USE_BLAST FIT_INSETS_CONTROLLEDbhv=DEFAULTfitTypes=STATUS_BARS NAVIGATION_BARSfitSides=LEFT TOP RIGHT}Requested w=1120 h=2960 mLayoutSeq=64mIsImWindow=true mIsWallpaper=false mIsFloatingLayer=truemBaseLayer=131000 mSubLayer=0    mToken=WindowToken{8e22fba type=2011 android.os.Binder@8ee38e5}mViewVisibility=0x0 mHaveFrame=true mObscured=falsemGivenContentInsets=[0,1759][0,0] mGivenVisibleInsets=[0,1759][0,0]mTouchableInsets=3 mGivenInsetsPending=falsetouchable region=SkRegion((0,1759,1440,2792))mFullConfiguration={1.0 310mcc260mnc [en_US] ldltr sw411dp w411dp h797dp 560dpi nrml long port finger qwerty/v/v dpad/v winConfig={ mBounds=Rect(0, 0 - 1440, 2960) mAppBounds=Rect(0, 0 - 1440, 2792) mMaxBounds=Rect(0, 0 - 1440, 2960) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.49 fontWeightAdjustment=0}mLastReportedConfiguration={1.0 310mcc260mnc [en_US] ldltr sw411dp w411dp h797dp 560dpi nrml long port finger qwerty/v/v dpad/v winConfig={ mBounds=Rect(0, 0 - 1440, 2960) mAppBounds=Rect(0, 0 - 1440, 2792) mMaxBounds=Rect(0, 0 - 1440, 2960) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.49 fontWeightAdjustment=0}mHasSurface=true isReadyForDisplay()=true mWindowRemovalAllowed=falseFrames: parent=[0,0][1440,2960] display=[0,0][1440,2960] frame=[0,0][1440,2960] last=[0,0][1440,2960] insetsChanged=falsesurface=[0,0][0,0]ContainerAnimator:mLeash=Surface(name=Surface(name=e79aca4 InputMethod)/@0xd668dc2 - animation-leash of insets_animation)/@0xaeadaa0 mAnimationType=insets_animationAnimation: com.android.server.wm.InsetsSourceProvider$ControlAdapter@c5c0659ControlAdapter mCapturedLeash=Surface(name=Surface(name=e79aca4 InputMethod)/@0xd668dc2 - animation-leash of insets_animation)/@0xaeadaa0WindowStateAnimator{ef4021e InputMethod}:mSurface=Surface(name=InputMethod)/@0x7be3affSurface: shown=true layer=0 alpha=1.0 rect=(0.0,0.0)  transform=(1.0, 0.0, 0.0, 1.0)mDrawState=HAS_DRAWN       mLastHidden=falsemEnterAnimationPending=false      mSystemDecorRect=[0,0][0,0]mForceSeamlesslyRotate=false seamlesslyRotate: pending=null    isOnScreen=trueisVisible=truekeepClearAreas: restricted=[], unrestricted=[]

输入法在主屏幕:

Window #7 Window{ee26d8d u0 InputMethod}:mDisplayId=0 rootTaskId=1 mSession=Session{d9973cd 1240:u0a10084} mClient=android.os.BinderProxy@7424e24mOwnerUid=10084 showForAllUsers=false package=com.android.inputmethod.latin appop=NONEmAttrs={(0,0)(fillxfill) gr=BOTTOM CENTER_VERTICAL sim={adjust=pan} ty=INPUT_METHOD fmt=TRANSPARENT wanim=0x1030056 receive insets ignoring z-orderfl=NOT_FOCUSABLE LAYOUT_IN_SCREEN SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDSpfl=USE_BLAST FIT_INSETS_CONTROLLEDbhv=DEFAULTfitTypes=STATUS_BARS NAVIGATION_BARSfitSides=LEFT TOP RIGHT}Requested w=1440 h=2960 mLayoutSeq=162mIsImWindow=true mIsWallpaper=false mIsFloatingLayer=truemBaseLayer=131000 mSubLayer=0    mToken=WindowToken{ba8dd3a type=2011 android.os.Binder@4152c65}mViewVisibility=0x0 mHaveFrame=true mObscured=falsemGivenContentInsets=[0,1675][0,0] mGivenVisibleInsets=[0,1675][0,0]mTouchableInsets=3 mGivenInsetsPending=falsetouchable region=SkRegion((0,1759,1440,2792))mFullConfiguration={1.0 310mcc260mnc [en_US] ldltr sw411dp w411dp h773dp 560dpi nrml long port finger qwerty/v/v dpad/v winConfig={ mBounds=Rect(0, 0 - 1440, 2960) mAppBounds=Rect(0, 0 - 1440, 2792) mMaxBounds=Rect(0, 0 - 1440, 2960) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.24 fontWeightAdjustment=0}mLastReportedConfiguration={1.0 310mcc260mnc [en_US] ldltr sw411dp w411dp h773dp 560dpi nrml long port finger qwerty/v/v dpad/v winConfig={ mBounds=Rect(0, 0 - 1440, 2960) mAppBounds=Rect(0, 0 - 1440, 2792) mMaxBounds=Rect(0, 0 - 1440, 2960) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.24 fontWeightAdjustment=0}mHasSurface=true isReadyForDisplay()=true mWindowRemovalAllowed=falseFrames: parent=[0,84][1440,2960] display=[0,84][1440,2960] frame=[0,84][1440,2960] last=[0,84][1440,2960] insetsChanged=falsesurface=[0,0][0,0]ContainerAnimator:mLeash=Surface(name=Surface(name=ee26d8d InputMethod)/@0x1dbd753 - animation-leash of insets_animation)/@0xf16d3fd mAnimationType=insets_animationAnimation: com.android.server.wm.InsetsSourceProvider$ControlAdapter@3fd88f2ControlAdapter mCapturedLeash=Surface(name=Surface(name=ee26d8d InputMethod)/@0x1dbd753 - animation-leash of insets_animation)/@0xf16d3fdWindowStateAnimator{fd93b43 InputMethod}:mAnimationIsEntrance=true      mSurface=Surface(name=InputMethod)/@0x85ed5c0Surface: shown=true layer=0 alpha=1.0 rect=(0.0,0.0)  transform=(1.0, 0.0, 0.0, 1.0)mDrawState=HAS_DRAWN       mLastHidden=falsemEnterAnimationPending=false      mSystemDecorRect=[0,0][0,0]mForceSeamlesslyRotate=false seamlesslyRotate: pending=null    isOnScreen=trueisVisible=truekeepClearAreas: restricted=[], unrestricted=[]

明显看到了displayId是变化了,而且window对象不是一个

这里可以考虑在SoftInputWindow的构造方法中加入相关堆栈。
在这里插入图片描述

日志堆栈及源码

上一个屏幕的输入法移除

systemserver发起inputmethodservice的destory

bringDownServiceLocked:4744, ActiveServices (com.android.server.am)
bringDownServiceIfNeededLocked:4566, ActiveServices (com.android.server.am)
removeConnectionLocked:4923, ActiveServices (com.android.server.am)
unbindServiceLocked:3164, ActiveServices (com.android.server.am)
unbindService:12670, ActivityManagerService (com.android.server.am)
unbindService:2079, ContextImpl (android.app)
unbindMainConnection:450, InputMethodBindingController (com.android.server.inputmethod)
unbindCurrentMethod:353, InputMethodBindingController (com.android.server.inputmethod)
startInputUncheckedLocked:2843, InputMethodManagerService (com.android.server.inputmethod)
startInputOrWindowGainedFocusInternalLocked:4156, InputMethodManagerService (com.android.server.inputmethod)
startInputOrWindowGainedFocusInternal:3863, InputMethodManagerService (com.android.server.inputmethod)
startInputOrWindowGainedFocus:3816, InputMethodManagerService (com.android.server.inputmethod)
onTransact:402, IInputMethodManager$Stub (com.android.internal.view)
onTransact:1957, InputMethodManagerService (com.android.server.inputmethod)
execTransactInternal:1280, Binder (android.os)
execTransact:1244, Binder (android.os)
新的输入法service启动
realStartServiceLocked:4373, ActiveServices (com.android.server.am)
bringUpServiceLocked:4228, ActiveServices (com.android.server.am)
bindServiceLocked:2956, ActiveServices (com.android.server.am)
bindServiceInstance:12653, ActivityManagerService (com.android.server.am)
bindServiceInstance:12609, ActivityManagerService (com.android.server.am)
bindServiceCommon:2035, ContextImpl (android.app)
bindServiceAsUser:1974, ContextImpl (android.app)
bindCurrentInputMethodService:466, InputMethodBindingController (com.android.server.inputmethod)
bindCurrentInputMethodServiceMainConnection:479, InputMethodBindingController (com.android.server.inputmethod)
bindCurrentMethod:400, InputMethodBindingController (com.android.server.inputmethod)
startInputUncheckedLocked:2845, InputMethodManagerService (com.android.server.inputmethod)
startInputOrWindowGainedFocusInternalLocked:4156, InputMethodManagerService (com.android.server.inputmethod)
startInputOrWindowGainedFocusInternal:3863, InputMethodManagerService (com.android.server.inputmethod)
startInputOrWindowGainedFocus:3816, InputMethodManagerService (com.android.server.inputmethod)
onTransact:402, IInputMethodManager$Stub (com.android.internal.view)
onTransact:1957, InputMethodManagerService (com.android.server.inputmethod)
execTransactInternal:1280, Binder (android.os)
execTransact:1244, Binder (android.os)

这里可以看出主要是一个跨进程调用发起老的输入法service要stop,destory新的service要进行onCreate,这些堆栈都是在systemserver进程打出来的。
是谁发起跨进程startInputOrWindowGainedFocus方法呢?当然是app端啊,输入焦点EditText那个app

App端发起启动输入法
startInputInner:2379, InputMethodManager (android.view.inputmethod)
startInput:665, InputMethodManager$DelegateImpl (android.view.inputmethod)
startInputAsyncOnWindowFocusGain:733, InputMethodManager$DelegateImpl (android.view.inputmethod)
onPostWindowFocus:141, ImeFocusController (android.view)
handleWindowFocusChanged:3699, ViewRootImpl (android.view)
-$$Nest$mhandleWindowFocusChanged:-1, ViewRootImpl (android.view)
handleMessageImpl:5538, ViewRootImpl$ViewRootHandler (android.view)
handleMessage:5452, ViewRootImpl$ViewRootHandler (android.view)
dispatchMessage:106, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
main:7898, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:548, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:936, ZygoteInit (com.android.internal.os)

可以看到在app ViewRootImpl的handleWindowFocusChanged() 方法发起的操作

SoftInputWindow创建堆栈

LatinIME这个service的onCreate创建

<init>:135, SoftInputWindow (android.inputmethodservice)
onCreate:1515, InputMethodService (android.inputmethodservice)
onCreate:608, LatinIME (com.android.inputmethod.latin)
handleCreateService:4515, ActivityThread (android.app)
-$$Nest$mhandleCreateService:-1, ActivityThread (android.app)
handleMessage:2162, ActivityThread$H (android.app)
dispatchMessage:106, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
main:7898, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:548, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:936, ZygoteInit (com.android.internal.os)
创建SoftInputWindow的Display确定

相关创建SoftInputWindow代码关键的是这个context,看看这个context的创建流程

frameworks/base/core/java/android/app/ActivityThread.java

 @UnsupportedAppUsageprivate void handleCreateService(CreateServiceData data) {
//省略大部分ContextImpl context = ContextImpl.getImpl(service.createServiceBaseContext(this, packageInfo));}

调用到了createServiceBaseContext方法

frameworks/base/core/java/android/window/WindowProviderService.java

/** @hide */
@Override
public final Context createServiceBaseContext(ActivityThread mainThread,LoadedApk packageInfo) {final Context context = super.createServiceBaseContext(mainThread, packageInfo);final Display display = context.getSystemService(DisplayManager.class).getDisplay(getInitialDisplayId());//这里是核心关键会创建displayreturn context.createTokenContext(mWindowToken, display);
}

这里可以看到主要核心就是context.getSystemService(DisplayManager.class)
.getDisplay(getInitialDisplayId())获取一个display,这个display也就是最后决定输入在哪个屏幕的关键
这里主要通过getInitialDisplayId获取到合适的displayId

frameworks/base/core/java/android/inputmethodservice/AbstractInputMethodService.java

    /** @hide */@Overridepublic final int getInitialDisplayId() {try {//这里主要调用到wms的getImeDisplayIdreturn WindowManagerGlobal.getWindowManagerService().getImeDisplayId();} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

下面看看wms的getImeDisplayId
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

@Overridepublic int getImeDisplayId() {synchronized (mGlobalLock) {//这里主要获取一下焦点的Display是谁final DisplayContent dc = mRoot.getTopFocusedDisplayContent();//这里有个getImePolicy获取,要等于DISPLAY_IME_POLICY_LOCAL才可以return dc.getImePolicy() == DISPLAY_IME_POLICY_LOCAL ? dc.getDisplayId(): DEFAULT_DISPLAY;}}

那现在就清楚了,输入法显示在哪个Display,是最后调用到了wms的中的getImeDisplayId方法,这个方法主要就是获取当前谁是focused的display,然后把displayid传递回去

总结

整个多屏输入法的流程涉及到了3个进程

在这里插入图片描述

    ---->焦点app发起startInputAsyncOnWindowFocusGain------>systemserver stop老的service,start新service-------->输入法进程创建新的service onCreate执行------->输入法创建带有display的context-------->创建对于输入法窗口带有新的display

更多framework干货获取相关可以 私聊+v(androidframework007)
点击这里 https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw
视频:https://www.bilibili.com/video/BV1ah411d7Y3
在这里插入图片描述

这篇关于多屏模式输入法可以正确切换屏幕展示原理剖析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/465934

相关文章

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

Spring Boot实现多数据源连接和切换的解决方案

《SpringBoot实现多数据源连接和切换的解决方案》文章介绍了在SpringBoot中实现多数据源连接和切换的几种方案,并详细描述了一个使用AbstractRoutingDataSource的实... 目录前言一、多数据源配置与切换方案二、实现步骤总结前言在 Spring Boot 中实现多数据源连接

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

韦季李输入法_输入法和鼠标的深度融合

在数字化输入的新纪元,传统键盘输入方式正悄然进化。以往,面对实体键盘,我们常需目光游离于屏幕与键盘之间,以确认指尖下的精准位置。而屏幕键盘虽直观可见,却常因占据屏幕空间,迫使我们在操作与视野间做出妥协,频繁调整布局以兼顾输入与界面浏览。 幸而,韦季李输入法的横空出世,彻底颠覆了这一现状。它不仅对输入界面进行了革命性的重构,更巧妙地将鼠标这一传统外设融入其中,开创了一种前所未有的交互体验。 想象

hdu4407容斥原理

题意: 有一个元素为 1~n 的数列{An},有2种操作(1000次): 1、求某段区间 [a,b] 中与 p 互质的数的和。 2、将数列中某个位置元素的值改变。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.Inpu