Android OS

Android 输入法窗口焦点获取流程(1),窗口和Sessio

2019-10-23  本文已影响0人  古风子

基于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创建

窗口创建

输入法#拉起流程#窗口初始化.png

attatch

 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创建过程,我们获取以下信息:

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);
        }
    }

主要代码逻辑如下:

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对应的客户端应用进程,并通知应用进程绑定/解绑输入法

总结

attatch和getWindowSession主要完成PhoneWindow和Session的创建,并将IInputContext和IInputMethodClient通过addClient添加到IMMS
中;进程死亡时,通过removeClient删除应用ClientState

当窗口和Session创建完毕,应用集成将请求获取窗口焦点,下一章,我们看下窗口获取焦点过程,跟输入法相关的过程
Android 输入法(2) ,输入法窗口和应用窗口绑定

上一篇下一篇

猜你喜欢

热点阅读