Android 输入法窗口焦点获取流程(1),窗口和Sessio
基于Android9.x
目录
-1 窗口创建和WindowSession创建
-1.1 窗口创建
-1.1.1 attatch
-1.2 WindowSession创建过程
-1.2.1 getWindowSession
-1.2.2 openSession
-1.2.3 new Session
-1.2.4 addClient
-1.2.5 linkToDeath
-1.2.6 binderDied
-1.2.6.1 removeClient
-1.2.6.2 killSessionLocked
-2 窗口创建和WindowSession创建依赖图
-3 总结
窗口创建和WindowSession创建
窗口创建
输入法#拉起流程#窗口初始化.pngattatch
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
//mWindow.getAttribute().softInputMode默认为0,因此不会执行softInputMode
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
...
}
该过程主要是完成PhoneWindow的创建,为布局内容的填充做准备;
跟输入法场景相关的是setSoftInputMode,不过,对于Activity,softInputMode = SOFT_INPUT_STATE_UNSPECIFIED;不会执行setSoftInputMode
WindowSession创建过程
getWindowSession.png如上图,主要看getWindowSession方法
getWindowSession
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
//单例模式,每个进程只会初始化一次
if (sWindowSession == null) {
try {
//获取InputMethodManager对象
InputMethodManager imm = InputMethodManager.getInstance();
//获取IWindowManager对象
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
imm.getClient():InputMethodManager中的IInputMethodClient对象
imm.getInputContext():InputMethodManager中的IInputContext对象
InputMethodManager.getInstance
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
//单例模式,每个进程只会初始化一次
if (sInstance == null) {
try {
//单例模式创建InputMethodManager
sInstance = new InputMethodManager(Looper.getMainLooper());
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
}
return sInstance;
}
}
InputMethodManager(Looper looper) throws ServiceNotFoundException {
this(IInputMethodManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)), looper);
}
InputMethodManager(IInputMethodManager service, Looper looper) {
//远程IMMS服的本地对象
mService = service;
//application's main looper
mMainLooper = looper;
mH = new H(looper);
mIInputContext = new ControlledInputConnectionWrapper(looper,
mDummyInputConnection, this);
}
mClient
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub()
通过以上Session创建过程,我们获取以下信息:
- 每个应用进程持有一个IWindowSession对象
- IWindowSession代表WMS端的Session对象,持有应用端的IWindowSessionCallback,IInputMethodClient和IInputContext对象
IWindowSessionCallback
mClient:IInputMethodClient
mIInputContext:ControlledInputConnectionWrapper
接着看下,wms端的创建过程:
openSession
WMS.JAVA
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
//根据应用传递的参数,创建Session对象
Session session = new Session(this, callback, client, inputContext);
return session;
}
new Session
/**
* This class represents an active client session. There is generally one
* Session object per process that is interacting with the window manager.
*/
public Session(WindowManagerService service, IWindowSessionCallback callback,
IInputMethodClient client, IInputContext inputContext) {
mService = service;
mCallback = callback;
mClient = client;
//得到改应用进程的uid和pid
mUid = Binder.getCallingUid();
mPid = Binder.getCallingPid();
mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
//check 应用操作系统窗口权限
mCanAddInternalSystemWindow = service.mContext.checkCallingOrSelfPermission(
INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED;
mCanHideNonSystemOverlayWindows = service.mContext.checkCallingOrSelfPermission(
HIDE_NON_SYSTEM_OVERLAY_WINDOWS) == PERMISSION_GRANTED;
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
== PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
mDragDropController = mService.mDragDropController;
//获取IMMS服务的本地对象
synchronized (mService.mWindowMap) {
if (mService.mInputMethodManager == null && mService.mHaveInputMethods) {
IBinder b = ServiceManager.getService(
Context.INPUT_METHOD_SERVICE);
mService.mInputMethodManager = IInputMethodManager.Stub.asInterface(b);
}
}
long ident = Binder.clearCallingIdentity();
try {
// Note: it is safe to call in to the input method manager
// here because we are not holding our lock.
if (mService.mInputMethodManager != null) {
//调用IMMS的addClient方法,将应用信息与IMMS进行绑定
mService.mInputMethodManager.addClient(client, inputContext,
mUid, mPid);
} else {
client.setUsingInputMethod(false);
}
//监听应用客户端是否死亡,死亡,则会调用
client.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
// The caller has died, so we can just forget about this.
try {
if (mService.mInputMethodManager != null) {
mService.mInputMethodManager.removeClient(client);
}
} catch (RemoteException ee) {
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
主要代码逻辑如下:
-
获取调用方,也就是应用进进程的uid,pid,连同应用传递的client和inputContext对象,传递到IMMS服务
-
注册应用端进程死亡回调,在应用进程死亡时,调用IMMS的removeClient方法,解绑IMMS和应用的IInputMethodClient
addClient
IMMS.JAVA
@Override
public void addClient(IInputMethodClient client,
IInputContext inputContext, int uid, int pid) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) {
mClients.put(client.asBinder(), new ClientState(client,
inputContext, uid, pid));
}
}
将应用层的IInputMethodClient和IInputContext对象,存储在IMMS服务的本地map对象mClients中
linkToDeath
该过程client.asBinder().linkToDeath(this, 0);注册一个回到给应用层Binder,当进程死亡时,
会回调Session的binderDied方法,为应用程,Session和IMMS解绑
binderDied
@Override
Session.java
public void binderDied() {
// Note: it is safe to call in to the input method manager
// here because we are not holding our lock.
try {
if (mService.mInputMethodManager != null) {
//IMMS调用removeClient删除map中的记录
mService.mInputMethodManager.removeClient(mClient);
}
} catch (RemoteException e) {
}
synchronized(mService.mWindowMap) {
//应用端和Session解绑
mClient.asBinder().unlinkToDeath(this, 0);
mClientDead = true;
//WMS删除关于改应用的Session对象,输入法应用服务未启动前,灭有Session被创建;创建过程,后续输入法弹出过程再讲
killSessionLocked();
}
}
removeClient
IMMS.JAVA
@Override
public void removeClient(IInputMethodClient client) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) {
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
clearClientSessionLocked(cs);
if (mCurClient == cs) {
if (mBoundToMethod) {
mBoundToMethod = false;
if (mCurMethod != null) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
MSG_UNBIND_INPUT, mCurMethod));
}
}
mCurClient = null;
}
if (mCurFocusedWindowClient == cs) {
mCurFocusedWindowClient = null;
}
}
}
}
删除存储在Map中的IInputMethodClient对象
killSessionLocked
本流程暂不涉及
窗口创建和WindowSession创建依赖图
输入法#拉起流程#窗口初始化#组件依赖图.png- 应用进程中,每个对象都是进程单例持有
mGlobal : WindowManagerGlobal
IMM : InputMethodManager
sWindowSession : IWindowSession
mClient : IInputMethodClient
mIInputContext : IInputContext
IInputContext:最总会被传递给InputMethod应用(如搜狗输入法);负责InputMethod进程和应用进程的编辑框的通信,如上屏、查询光标前后字符等
IInputMethodClient:IMMS使用该接口查找和IMS对应的客户端应用进程,并通知应用进程绑定/解绑输入法
- IMMS服务中,每个ClientState代码一个应用进程,进程创建时,通过addClient创建;进程死亡时,通过removeClient删除
总结
attatch和getWindowSession主要完成PhoneWindow和Session的创建,并将IInputContext和IInputMethodClient通过addClient添加到IMMS
中;进程死亡时,通过removeClient删除应用ClientState
当窗口和Session创建完毕,应用集成将请求获取窗口焦点,下一章,我们看下窗口获取焦点过程,跟输入法相关的过程
Android 输入法(2) ,输入法窗口和应用窗口绑定