Android輸入法IME(二)

2. IME初始化啟動流程

2.1. IME客戶端(IMM)初始化流程

涉及代碼文件路徑: frameworks/base/core/java/android/view/ViewRootImpl.java frameworks/base/core/java/android/view/WindowManagerGlobal.java frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java frameworks/base/core/java/com/android/internal/view/IInputMethodClient.aidl frameworks/base/core/java/com/android/internal/view/IInputContext.aidl frameworks/base/core/java/com/android/internal/view/IInputMethodManager.aidl frameworks/base/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java

2.1.1. 函數流程梳理

# 每次新增窗口window時,都會實例化ViewRootImpl,而ViewRootImpl在獲取IWindowSession時會檢查輸入法是否已經初始化
ViewRootImpl.java -- 初始化構造函數,調用WindowManagerGlobal.getWindowSession()---> WindowManagerGlobal.java -- getWindowSession()調用InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary() 實例化全局調用InputMethodManager,即初始化IMM---> InputMethodManager.java -- ensureDefaultInstanceForDefaultDisplayIfNecessary()調用forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper()),入參默認displayID和looper# 此處也說明,對于APP層,IMM有且只有一個實例,每次創建ViewRootImpl都會檢查IMM是否實例化完成---》 調用forContextInternal函數,先從緩存Map中查詢是否有IMM實例,如果沒有則創建IMM實例,并添加到Map中---》 調用createInstance創建實例,然后在三目運算中默認固定調用createRealInstance(displayId, looper)---》 調用createRealInstance函數,   (1)獲取輸入法服務service,即Context.INPUT_METHOD_SERVICE(service是AIDL接口文件IInputMethodManager.aidl);(2)new InputMethodManager(service, displayId, looper)創建實例---》 InputMethodManager構造函數---》 new IInputConnectionWrapper 創建虛擬的輸入法上下文,主要用于監聽輸入法服務的激活狀態,接受輸入事件# 添加IMM實例到輸入法service服務中# 此處兩個入參都是AIDL接口類型的對象# (1)IInputMethodClient.aidl:輸入法客戶端, 主要用于報告輸入法當前的狀態, 讓APP應用端的IMM做出相應的處理# (2)IInputContext.aidl:輸入法上下文, 主要用于操作字符輸入操作, 讓當前接收字符的view進行處理(3)調用service.addClient(imm.mClient //[AIDL對象,即IInputMethodClient], imm.mIInputContext//[AIDL對象,IInputContext], displayId)---> IInputMethodManager.aidl -- 調用addClient(跨進程通信到IMMS)---> 服務端InputMethodManagerService.java "extends IInputMethodManager.Stub" --  調用addClient函數,創建ClientState對象---》 調用內部靜態類ClientState的構造函數,保存client相關狀態屬性

綜上代碼流程梳理,可以看出:

  1. 對于每個APP應用,IMM有且只有一個實例,并且每次創建ViewRootImpl時,都會檢查IMM是否已經實例化成功
  2. 實例化IMM對象時,會涉及到兩個AIDL接口文件,一個用于應用端IMM處理輸入法當前狀態,一個用于輸入法上下文,創建一個虛擬的InputContext代表輸入空間,用于監聽輸入法激活狀態
  3. 實例化過程中會有個displayid,用于多屏幕顯示(通常情況下默認是default display=0)
  4. 實例化最后,會通過AIDL的addClient接口函數,將IMM添加到IMMS中,如此IMM實例化完成

2.1.2. 代碼詳細說明

//ViewRootImpl.javapublic ViewRootImpl(Context context, Display display) {this(context, display, WindowManagerGlobal.getWindowSession(),false /* useSfChoreographer */);}//WindowManagerGlobal.javapublic static IWindowSession getWindowSession() {synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {try {//調用該函數,初始化IMMInputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();......} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return sWindowSession;}}//InputMethodManager.javapublic static void ensureDefaultInstanceForDefaultDisplayIfNecessary() {//默認default displayforContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper());}private static InputMethodManager forContextInternal(int displayId, Looper looper) {final boolean isDefaultDisplay = displayId == Display.DEFAULT_DISPLAY;synchronized (sLock) {//從緩存Map中查找是否由default display的IMM實例InputMethodManager instance = sInstanceMap.get(displayId);//如果存在實例,則直接返回if (instance != null) {return instance;}//初始化創建實例instance = createInstance(displayId, looper);//如果是用于default display使用,則存儲到sInstance中作為全局單例實例if (sInstance == null && isDefaultDisplay) {sInstance = instance;}//將IMM實例保存到Map中sInstanceMap.put(displayId, instance);return instance;}}private static InputMethodManager createInstance(int displayId, Looper looper) {//isInEditMode固定返回false,直接調用createRealInstancereturn isInEditMode() ? createStubInstance(displayId, looper): createRealInstance(displayId, looper);}private static InputMethodManager createRealInstance(int displayId, Looper looper) {//IInputMethodManager是AIDL接口文件,用于跨進程通信到IMMS(InputMethodManagerService)final IInputMethodManager service;try {//獲取serviceservice = IInputMethodManager.Stub.asInterface(ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));} catch (ServiceNotFoundException e) {throw new IllegalStateException(e);}//創建IMM實例final InputMethodManager imm = new InputMethodManager(service, displayId, looper);//將PID/UID和每個IME客戶端關聯,然后作為跨進程服務端IPC使用梳理//如果作為同進程內調用梳理,則需要確保Binder.getCalling{Pid, Uid}()返回Process.my{Pid, Uid}()//無論哪種情況,都要調用Binder的{clear, restore}CallingIdentity()函數,對跨進程沒有影響,對同進程可以滿足需求實現final long identity = Binder.clearCallingIdentity();try {// 添加 IMM 實例到輸入法服務// imm.mClient 是一個aidl對象, mClient即new IInputMethodClient.Stub(),AIDL接口// imm.mIInputContext 是一個aidl對象, IInputContext,AIDL接口service.addClient(imm.mClient, imm.mIInputContext, displayId);} catch (RemoteException e) {e.rethrowFromSystemServer();} finally {Binder.restoreCallingIdentity(identity);}return imm;}//InputMethodManagerService.java//由每個APP應用進程調用,作為輸入法開始與交互的準備@Overridepublic void addClient(IInputMethodClient client, IInputContext inputContext,int selfReportedDisplayId) {//獲取調用的uid和pid(即InputMethodManager實際運行所在的UID/PID)//兩種情況下調用此方法://1.IMM正在另一個進程中實例化//2.IMM正在同一個進程中實例化,final int callerUid = Binder.getCallingUid();final int callerPid = Binder.getCallingPid();synchronized (mMethodMap) {// TODO: Optimize this linear search.final int numClients = mClients.size();for (int i = 0; i < numClients; ++i) {final ClientState state = mClients.valueAt(i);if (state.uid == callerUid && state.pid == callerPid&& state.selfReportedDisplayId == selfReportedDisplayId) {throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid+ "/displayId=" + selfReportedDisplayId + " is already registered.");}}//利用IBinder.deathRecipient監聽client存活狀態//如果client的Binder死亡,則將Client從緩存Map中移除final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);try {client.asBinder().linkToDeath(deathRecipient, 0);} catch (RemoteException e) {throw new IllegalStateException(e);}//此處不驗證displayID,后續每當客戶端需要與指定的交互時,就需要檢查displayID//此處創建ClientState對象,將client和inputContext緩存進去,然后將該對象保存到緩存Map mClients中mClients.put(client.asBinder(), new ClientState(client, inputContext, callerUid,callerPid, selfReportedDisplayId, deathRecipient));}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/22077.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/22077.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/22077.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【kubernetes】k8s的面試寶典,等你來拿哦

目錄 一、pod的生命周期 二、創建 pod 的工作流程 三、ingres 有哪些組件并且描述出組件作用 &#xff1f; 四、ingress 的工作原理 五、ingress 暴露服務的方式 六、pod 的組成 七、pod的本身性質&#xff08;pod的種類與說明&#xff09; 八、k8s命令 8.1在k8s中如何…

零基礎入門學習Python第二階04SQL詳解03

MySQL 新特性 JSON類型 很多開發者在使用關系型數據庫做數據持久化的時候&#xff0c;常常感到結構化的存儲缺乏靈活性&#xff0c;因為必須事先設計好所有的列以及對應的數據類型。在業務發展和變化的過程中&#xff0c;如果需要修改表結構&#xff0c;這絕對是比較麻煩和難…

AppStore搜索優化方法(ASO)

在競爭激烈的 App Store 中&#xff0c;如何讓你的應用脫穎而出&#xff0c;吸引更多用戶下載&#xff1f;其實從官方文檔描述中可以總結一些優化技巧&#xff0c;這是官方描述地址&#xff1a;搜索優化 – App Store – Apple Developer。通過官方描述我們可以總結到影響搜索結…

commander.js 入門指南:構建強大的命令行界面 (全網最全教程)

在Node.js的世界里&#xff0c;創建用戶友好的命令行界面&#xff08;CLI&#xff09;對于許多應用程序和工具來說至關重要。Commander.js 是一個廣受歡迎的 Node.js 包&#xff0c;它為開發者提供了一套簡潔而強大的 API&#xff0c;用于快速創建功能完備、用戶友好的命令行界…

如何用TCC方案輕松實現分布式事務一致性

本文作者:小米,一個熱愛技術分享的29歲程序員。如果你喜歡我的文章,歡迎關注我的微信公眾號“軟件求生”,獲取更多技術干貨! 哈嘍,大家好!我是小米,一個熱愛技術的活力小青年,今天要和大家分享的是一種在分布式系統中實現事務的一種經典方案——TCC(Try Confirm Canc…

【Ubuntu】超詳細安裝Ubuntu系統

鑒于有些小伙伴在安裝Ubuntu系統的時候遇到很多問題&#xff0c;因此打算編寫一篇記錄一下安裝Ubuntu系統的整個過程~互相學習&#xff01; 一、制作U盤啟動 準備一個大于8G以上的U盤&#xff0c;這里我使用的是16G的U盤下載UltraISO工具 網站地址&#xff1a;UltraISO準備Ub…

C++ Primer 第五版 第15章 面向對象程序設計

面向對象程序設計基于三個基本概念&#xff1a;數據抽象、繼承和動態綁定。 繼承和動態綁定對編寫程序有兩方面的影響&#xff1a;一是我們可以更容易地定義與其他類相似但不完全相同的新類&#xff1b;二是在使用這些彼此相似的類編寫程序時&#xff0c;我們可以在一定程度上…

HTML靜態網頁成品作業(HTML+CSS)—— 金寶貝兒童教育機構介紹網頁(2個頁面)

&#x1f389;不定期分享源碼&#xff0c;關注不丟失哦 文章目錄 一、作品介紹二、作品演示三、代碼目錄四、網站代碼HTML部分代碼 五、源碼獲取 一、作品介紹 &#x1f3f7;?本套采用HTMLCSS&#xff0c;未使用Javacsript代碼&#xff0c;共有2個頁面。 二、作品演示 三、代…

Stable diffusion prompts 使用語法、參數講解、插件安裝教程

Stable diffusion prompts 使用語法、參數講解、插件安裝教程 本文基于 Stable diffusion WebUI 進行講解&#xff08;安裝在 AutoDL 上&#xff0c;安裝在本地電腦上的也同樣適用本教程&#xff09;。 初始界面&#xff1a; 文件目錄結構&#xff1a; 上圖紅框中的 4 個文件…

requests模塊編寫漏洞檢測工具

#嘗試使用python登錄pikachu爆破模塊 #發送post數據包&#xff0c;包含用戶名密碼&#xff0c;對接受到的響應進行判斷&#xff0c;如何為登錄成功 #爆破密碼 with open(passwor.txt,r) as f: passwordf.readlines() for i in password: data {username: admin, password: i, …

數據結構——算法和算法效率的度量

目錄 一、引言 二、算法 1 算法的基本概念 2 算法的復雜度 2.1 時間復雜度 2.1.1 概念 2.1.2 大O的漸進表示 3 算法的空間復雜度 3.1 概念 3.2 實例 4 實例分析 5 結論 一、引言 大家在寫代碼的時候有沒有發現寫同樣功能的代碼有多種不同的寫法&#xff0c;而不同的代…

51種企業應用架構模式詳解

01 什么是企業應用 我的職業生涯專注于企業應用&#xff0c;因此&#xff0c;這里所談及的模式也都是關于企業應用的。&#xff08;企業應用還有一些其他的說法&#xff0c;如“信息系統”或更早期的“數據處理”。&#xff09;那么&#xff0c;這里的“企業應用”具體指的是什…

[原型資源分享]經典產品餓了么UI模版部件庫

?部件庫預覽鏈接&#xff1a;https://f13gm0.axshare.com 支持版本: Axrure RP 8 文件大小: 3MB 文檔內容介紹 基本部件&#xff1a;表單樣式&#xff1a;12款、數據樣式&#xff1a;10款、服務樣式&#xff1a;6款、導航&#xff1a;5款、業務組件&#xff1a;7款、 模板…

python把簡體中文轉換為繁體中文

Python 可以使用第三方庫來將簡體中文&#xff08;簡體中文&#xff09;轉換為繁體中文&#xff08;繁體中文&#xff09;。一個常用的庫是 opencc-python-reimplemented&#xff0c;它是 Open Chinese Convert (OpenCC) 的 Python 實現&#xff0c;OpenCC 是一個開源的中文簡繁…

MySQL之查詢性能優化(三)

查詢性能優化 重構查詢的方式 在優化有問題的查詢時&#xff0c;目標應該是找到一個更優的方法獲得實際需要的記過——而不是一定總是需要從MySQL獲取一模一樣的結果集。有時候&#xff0c;可以將查詢轉換一種寫法讓其返回一樣的結果&#xff0c;但是性能更好。但也可以通過修…

Python魔法之旅-魔法方法(14)

目錄 一、概述 1、定義 2、作用 二、應用場景 1、構造和析構 2、操作符重載 3、字符串和表示 4、容器管理 5、可調用對象 6、上下文管理 7、屬性訪問和描述符 8、迭代器和生成器 9、數值類型 10、復制和序列化 11、自定義元類行為 12、自定義類行為 13、類型檢…

在Debian系統上賦予普通用戶ping 權限

在Debian系統上&#xff0c;普通用戶默認情況下沒有權限使用 ping 命令&#xff0c;因為它需要發送 ICMP 包&#xff0c;這通常需要 root 權限。為了允許普通用戶使用 ping&#xff0c;可以設置 ping 命令的 setuid 位。以下是具體的步驟&#xff1a; 查找 ping 命令的位置&am…

2024年度自貢市社會民生重大科技計劃項目申報要求、時間流程

一、申報要求 申報項目需符合以下申報要求和申報指南要求&#xff0c;申報資料需在“自貢市科技綜合業務服務平臺”中的“自貢市重點科技計劃項目管理系統”上傳。 &#xff08;一&#xff09;項目申報單位要求。 1.項目申報單位包括項目牽頭單位和項目合作單位。 2.多家單…

【Python】pyinstaller打包時添加詳細信息

在要被打包的py文件同級目錄新建version.txt&#xff0c;寫入以下內容 # UTF-8 # # For more details about fixed file info ffi see: # http://msdn.microsoft.com/en-us/library/aa381058.aspx # VSVersionInfo(ffiFixedFileInfo(filevers(1, 4, 0, 5),prodvers(1, 4, 0, 5…

SpringBoot使用RabbitMQ實現延遲隊列

SpringBoot使用RabbitMQ實現延遲隊列 需求和目標名詞解釋實現方式引入依賴添加配置文件配置類死信隊列消費者即時隊列消費者延遲消息發送結果注意 需求和目標 商城系統&#xff0c;用戶下單后若15分鐘內仍未完成支付&#xff0c;則自動取消訂單&#xff0c;若已支付&#xff0c…