文章大綱
- 引言
- 一、Want概述
- 二、Want的類型
- 1、顯式Want
- 2、隱式Want
- 3、隱式Want的匹配
- 三、隱式啟動Want 源碼概述
- 1、有且僅有一個Ability匹配
- 2、有多個Ability 匹配需要彈出選擇對話框
- 3、ImplicitStartProcessor::ImplicitStartAbility
- 3.1、GenerateAbilityRequestByAction
- 3.1.1、GetBundleManagerHelper 獲取BMS對象
- 3.1.2、GetBundleManagerHelper()->ImplicitQueryInfos
- 3.1.3、ImplicitStartProcessor::FilterAbilityList
- 3.2、定義回調
- 3.3、根據dialogAppInfos和deviceType的組合進行不同的分支處理
- 4、當有多個匹配結果時會先執行FilterAbilityList(有條件執行)
- 小結
引言
在Android 中Activity及其他四大組件之間是通過Intent來傳遞信息的,在我們的鴻蒙操作系統中同樣有自己組件的“Intent”,今天我們介紹下鴻蒙中的“Intent”。
暫且對照著Android中的Intent 來理解。
一、Want概述
Want 是對象間信息傳遞的載體,可以用于應用組件間的信息傳遞。其使用場景之一是作為startAbility()的參數,包含了指定的啟動目標以及啟動時需攜帶的相關數據,如bundleName和abilityName字段分別指明目標Ability所在應用的包名以及對應包內的Ability名稱。當UIAbilityA啟動UIAbilityB并需要傳入一些數據給UIAbilityB時,Want可以作為一個載體將數據傳給UIAbilityB。
二、Want的類型
1、顯式Want
在啟動Ability時指定了abilityName和bundleName的Want稱為顯式Want。
當有明確處理請求的對象時,通過提供目標Ability所在應用的包名信息(bundleName),并在Want內指定abilityName便可啟動目標Ability。顯式Want通常用于在當前應用開發中啟動某個已知的Ability。
let wantInfo = {deviceId: '', // deviceId為空表示本設備bundleName: 'com.example.myapplication',abilityName: 'FuncAbility',
}
2、隱式Want
當請求處理的對象不明確時,希望在當前應用中使用其他應用提供的某個能力(通過??skills標簽??定義),而不關心提供該能力的具體應用,可以使用隱式Want。例如使用隱式Want描述需要打開一個鏈接的請求,而不關心通過具體哪個應用打開,系統將匹配聲明支持該請求的所有應用。
3、隱式Want的匹配
根據系統中待匹配Ability的匹配情況不同,使用隱式Want啟動Ability時會出現以下三種情況。
- 未匹配到滿足條件的Ability:啟動失敗。
- 匹配到一個滿足條件的Ability:直接啟動該Ability。
- 匹配到多個滿足條件的Ability(UIAbility):彈出選擇框讓用戶選擇。
匹配約束:
- 調用方傳入的want參數中不帶有abilityName和bundleName,則不允許通過隱式Want啟動所有應用的ServiceExtensionAbility。
- 調用方傳入的want參數中帶有bundleName,則允許使用startServiceExtensionAbility()方法隱式Want啟動ServiceExtensionAbility,默認返回優先級最高的ServiceExtensionAbility,如果優先級相同,返回第一個。
let wantInfo: Want = {deviceId: "",action: "ohos.want.action.viewData",entities: ['entity.system.videoPlayer'],abilityName: "",uri: "",type: "",parameters: {}}
通過上面表格可以除了bundleName和moduleName,只有uri、type、action、entities 字段參與匹配,換句話說提供相應能力的hap中的moudle.json5 里的skills 的這四個字段的值會影響到匹配結果。
三、隱式啟動Want 源碼概述
foundation/ability/ability_runtime/services/abilitymgr/src/implicit_start_processor.cpp 處理隱式匹配規則
foundation/ability/ability_runtime/services/abilitymgr/src/system_dialog_scheduler.cpp 彈出模態對話框
1、有且僅有一個Ability匹配
2、有多個Ability 匹配需要彈出選擇對話框
3、ImplicitStartProcessor::ImplicitStartAbility
對應的代碼由ImplicitStartProcessor::ImplicitStartAbility開始處理,首先AbilityRequest的callType = 0,傳入的DeviceType為tablet,而社區默認是default。
3.1、GenerateAbilityRequestByAction
傳入std::vector dialogAppInfos、deviceType 成功創建AbilityRequest對象,
3.1.1、GetBundleManagerHelper 獲取BMS對象
3.1.2、GetBundleManagerHelper()->ImplicitQueryInfos
3.1.3、ImplicitStartProcessor::FilterAbilityList
3.2、定義回調
auto startAbilityTask = [imp = shared_from_this(), request, userId, identity](const std::string& bundle, const std::string& abilityName) mutable {HILOG_INFO("implicit start ability call back.");IPCSkeleton::SetCallingIdentity(identity);AAFwk::Want targetWant = request.want;targetWant.SetElementName(bundle, abilityName);auto callBack = [imp, targetWant, request, userId]() -> int32_t {return imp->ImplicitStartAbilityInner(targetWant, request, userId);};return imp->CallStartAbilityInner(userId, targetWant, callBack, request.callType);
};
3.3、根據dialogAppInfos和deviceType的組合進行不同的分支處理
-
dialogAppInfos.size() == 0 && (deviceType == STR_PHONE || deviceType == STR_DEFAULT
-
dialogAppInfos.size() == 0 && deviceType != STR_PHONE && deviceType != STR_DEFAULT
-
dialogAppInfos.size() == 0 && deviceType != STR_PHONE && deviceType != STR_DEFAULT
-
deviceType == STR_PHONE || deviceType == STR_DEFAULT
DelayedSingleton::GetInstance()->GetSelectorDialogWant(dialogAppInfos, request.want, request.callerToken)
以上四種情況之外的
4、當有多個匹配結果時會先執行FilterAbilityList(有條件執行)
- MatchTypeAndUri
- AddAbilityInfoToDialogInfos
- 循環從extensionInfos中獲取dialogAppInfo信息并存入dialogAppInfos
小結
彈出對話框的SelectorDialog_Service是一個ServiceExtensionAbility,進程為com.ohos.amsdialog,連接上Selector_Dialog_Service后會觸發其onCreate->OnRequest函數執行,createWindow時
private async createWindow(name: string, windowType: number, rect) {let deviceTypeInfo = deviceInfo.deviceType;console.info(TAG, 'create window');try {win = await window.create(globalThis.selectExtensionContext, name, windowType);if (windowType === window.WindowType.TYPE_DIALOG) {await win.bindDialogTarget(globalThis.callerToken.value, () => {win.destroyWindow();winNum--;if (winNum === 0) {globalThis.selectExtensionContext.terminateSelf();}});}if (deviceTypeInfo !== 'default') {await win.hideNonSystemFloatingWindows(true);}await win.moveTo(rect.left, rect.top);await win.resetSize(rect.width, rect.height);if (globalThis.params.deviceType === 'phone' || globalThis.params.deviceType === 'tablet') {await win.loadContent('pages/selectorPhoneDialog');} else {await win.loadContent('pages/selectorPcDialog');}await win.setBackgroundColor('#00000000');await win.show();} catch (e) {console.error(TAG, 'window create failed: ' + JSON.stringify(e));}}
然后去觸發方舟編譯器解包ams_system_dialog.hap執行,
最終結論:
1、win.hideNonSystemFloatingWindows(true); 的父類實現中默認報異常中斷,所以需要在子類中實現該函數。(但是問題來了,里面的細節如何實現呢?社區中都是直接返回true)
2、在不同的設備上彈出的匹配對話框的位置和尺寸大小還需要適配,在
this.createWindow(‘SelectorDialog’ + startId, windowType, navigationBarRect);