场景描述
使用原生能力startability启动其他应用前,开发者需要判断目标应用是否安装,从而执行不同的逻辑,例如:
场景一:支付时商户根据实际情况去判断,拉起支付应用还是h5页面。
场景二:分享场景与支付场景,需要列出多个用户可跳转的应用。
业务诉求:
场景一:支付时商户根据实际情况去判断,拉起支付应用还是h5页面
显示效果:
1.支付应用存在,拉起支付应用。
2.支付应用不存在,拉起h5页面进行支付。
核心代码
1.在拉起方的module.json5文件中配置querySchemes字段,表示本应用可能会用到的scheme查询,比如这里配置的payapp代表本应用可以使用bundleManager.canOpenLink(),来查询scheme为payapp的链接是否可以打开(payapp://xx?xx=1&yy=2)
"module": {
"querySchemes": [
"payapp",
],
}
2.在被拉起方的module.json文件中的skill字段中配置该应用支持的scheme协议,表示这个应用可以通过此协议打开。
"abilities": [
{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
],
"uris": [
{
"scheme": 'payapp'
}
],
}
]
}
]
3.在拉起方中通过bundleManager.canOpenLink()判断该链接能否打开,可以打开的话跳转支付应用进行支付,不能打开的话跳转h5页面来下载应用或者支付。
// payapp:// 后的字段可以自定义,需要由被拉起方应用进行处理
let paylink = 'payapp://startpay?apppid=123456&page=xxx/pay&query=10';
let paydata = bundleManager.canOpenLink(paylink);
if (paydata) {
let want: Want = {
uri: paylink
};
let context = getContext(this) as common.UIAbilityContext;
context.startAbility(want, (error: BusinessError) => {
console.error(`error.code = ${error.code}`);
});
} else {
this.pageInfos.pushPath({ name: 'PayWeb' })
}
//PayWeb
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';
@Component
export struct PayWebInfo {
@Consume('pageInfos') pageInfos: NavPathStack;
webviewController: web_webview.WebviewController = new web_webview.WebviewController();
aboutToAppear() {
try {
this.webviewController.loadUrl($rawfile("pay.html"));
} catch (error) {
let e: business_error.BusinessError = error as business_error.BusinessError;
console.error(`ErrorCode: ${e.code}, Message: ${e.message}`);
}
}
build() {
NavDestination() {
Column() {
Web({ src: $rawfile("pay.html"), controller: this.webviewController })
}.width('100%').height('100%')
}.hideTitleBar(true)
}
}
4.被调用方在冷启动热启动的情况下都要拉起同一个支付页面,就需要在onNewWant,onCreate,onWindowStageCreate中都添加跳转支付页面的相关逻辑。
//定义一个接口接收want传入的数据
interface StartParm {
bundleName?: string
uriResult: uri.URI
started: boolean
}
//将want数据存入
function parseStartWant(want: Want): StartParm | undefined {
if (want.uri) {
let startParam: StartParm = {
bundleName: want.bundleName,
uriResult: new uri.URI(want.uri),
started: false
}
return startParam
} else {
return undefined
}
}
export default class EntryAbility extends UIAbility {
startParam?: StartParm
// 冷热启动均执行,用于跳转支付页面
action() {
if (this.startParam && !this.startParam.started) {
this.startParam.started = true
let pageInfos = AppStorage.get<NavPathStack>("pageInfos") as NavPathStack;
//判断支付页面是否存在
let data =pageInfos.getIndexByName('PayPage')
if ( data.length == 0 ) {
pageInfos.pushPath({ name: 'PayPage' })
}else {
pageInfos.removeByName('PayPage')
pageInfos.pushPath({ name: 'PayPage' })
}
}
}
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.startParam = parseStartWant(want)
//本Demo通过AppStorage将传入的数据展示在页面上,例如消费应用,消费金额等
AppStorage.setOrCreate<StartParm>("Param",this.startParam );
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.startParam = parseStartWant(want)
//数据处理同onCreate
AppStorage.setOrCreate<StartParm>("Param",this.startParam );
//跳转支付页面
this.action()
}
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
//由于时序问题,需要使用setTimeout保证页面压栈的顺序
setTimeout(()=>{
//跳转支付页面
this.action()
})
});
}
}
场景二:分享场景与支付场景,需要列出多个用户可跳转的应用。
显示效果:
核心代码
1.同场景一Step1,需要在querySchemes中配置需要进行跳转检测的应用。
"module": {
...
"querySchemes": [
"bank1",
...
"bank9"
]
}
2.在拉起方中遍历想要跳转的所有应用,并在弹窗中显示可以被拉起的应用。
准备被拉起方的app信息,其中目标app的图标需要拉起方应用自己准备。uri字段规格详见文末的常见问题Q1。
private payApps: PayApp[] = [
new PayApp('银行1', $r("app.media.startIcon"), 'bank1://xx?xx'),
...
new PayApp('银行9', $r("app.media.startIcon"), 'bank9://xx?xx'),
]
添加按钮,点击出现支付应用列。
Button("跳转支付列表")
.onClick(() => {
if (this.dialogController != null) {
this.dialogController.open()
}
})
添加自定义弹窗,定义弹窗以及弹窗中分割线的属性:
@State egDivider: DividerTmp = new DividerTmp(1, 10, 10, '# ffe9f0f0')
//增加一个类方便定义分割线属性
class DividerTmp {
strokeWidth: Length = 1 //分割线宽度
startMargin: Length = 10 //分割线距离左端长度
endMargin: Length = 10 //分割线距离右端长度
color: ResourceColor = '# ffe9f0f0'//分割线颜色
constructor(strokeWidth: Length, startMargin: Length, endMargin: Length, color: ResourceColor) {
this.strokeWidth = strokeWidth
this.startMargin = startMargin
this.endMargin = endMargin
this.color = color
}
}
//自定义弹窗
dialogController: CustomDialogController | null = new CustomDialogController({
builder: CustomDialogExample({
payApps: this.payApps,
egDivider: this.egDivider
}),
})
在弹窗显示之前判断应用是否已安装,在弹窗中显示已安装的应用,并实现点击跳转到该应用。
//每行app的信息
class PayApp {
name: string;
icon: Resource;
link: string;
installed: boolean = false
...
}
@CustomDialog
struct CustomDialogExample {
@Prop payApps: PayApp[]
@Prop egDivider: DividerTmp
controller?: CustomDialogController
aboutToAppear(): void {
for (let item of this.payApps) {
item.installed = bundleManager.canOpenLink(item.link)
}
this.payApps = this.payApps.filter((app) => app.installed)
}
build() {
Column() {
...
List() {
ForEach(this.payApps, (item: PayApp) => {
ListItem() {
Row() {
Image(item.icon).width(25).height(25).margin(10)
Text(item.name).fontSize(20)
}
.onClick(() => {
let context = getContext() as common.UIAbilityContext
context.startAbility({ uri: item.link })
})
.justifyContent(FlexAlign.Start)
}
}, (item: PayApp) => item.name.toString())
}
}
}
}