Toggle navigation
Toggle navigation
This project
Loading...
Sign in
developOne
/
harmonyPool
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
xugenyuan
2024-10-11 18:05:23 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
a77a8c2b011352273da125fd004966625e7aab48
a77a8c2b
1 parent
cf8f34b2
ref |> 文章详情页增加Web容器复用逻辑01
Signed-off-by: xugenyuan <xugenyuan@wondertek.com.cn>
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
435 additions
and
74 deletions
sight_harmony/commons/wdWebComponent/Index.ets
sight_harmony/commons/wdWebComponent/src/main/ets/pages/WdWebLocalComponent.ets
sight_harmony/commons/wdWebComponent/src/main/ets/webPool/WebArticleEventHandler.ets
sight_harmony/commons/wdWebComponent/src/main/ets/webPool/WebLocalPool.ets
sight_harmony/commons/wdWebComponent/src/main/ets/webPool/WebPoolManager.ets
sight_harmony/features/wdComponent/src/main/ets/components/ImageAndTextWebComponent.ets
sight_harmony/commons/wdWebComponent/Index.ets
View file @
a77a8c2
export { WdWebComponent } from './src/main/ets/pages/WdWebComponent'
export { WdWebLocalComponent } from './src/main/ets/pages/WdWebLocalComponent'
export { WebEvents, WebNodeController } from './src/main/ets/webPool/WebLocalPool'
export { WebPoolManager } from './src/main/ets/webPool/WebPoolManager'
export { WebArticleEventHandler } from './src/main/ets/webPool/WebArticleEventHandler'
\ No newline at end of file
...
...
sight_harmony/commons/wdWebComponent/src/main/ets/pages/WdWebLocalComponent.ets
View file @
a77a8c2
...
...
@@ -9,18 +9,21 @@ import { window } from '@kit.ArkUI';
import { NativeCallH5Type,NativeCallH5Event,eventParams } from './NativeCallH5Type';
import { AudioSuspensionModel } from '../viewmodel/AudioSuspensionModel'
import { BusinessError } from '@kit.BasicServicesKit';
import { WebPoolManager } from '../webPool/WebPoolManager';
import { WebEvents } from '../webPool/WebLocalPool';
const TAG = 'WdWebLocalComponent';
@Component
export struct WdWebLocalComponent {
webPoolTargetId: string = ''
webviewControl: BridgeWebViewControl = new BridgeWebViewControl()
onWebPrepared: () => void = () => {
}
@Prop backVisibility: boolean = false
@Prop webResource: Resource = {} as Resource
@Prop @Watch('onReloadStateChanged') reload: number = 0
@State webHeight:
string | number
= '100%'
@State webHeight:
Length
= '100%'
@Link isPageEnd: boolean
@State videoUrl: string = ''
@State positionWidth: number = 0
...
...
@@ -75,6 +78,18 @@ export struct WdWebLocalComponent {
}
aboutToAppear(): void {
let eventHandler = WebPoolManager.sharedInstance().getWebEventHandler(this.webPoolTargetId)
if (!eventHandler) {
Logger.error(TAG, '当前target不应拿不到nodeController');
} else {
eventHandler.pageEndBlock = this.onPageEnd.bind(this)
if (eventHandler.pageLoadEnd) {
this.onPageEnd()
}
eventHandler.currentPageOperateBlock = this.currentPageOperate.bind(this)
}
EmitterUtils.receiveEvent(EmitterEventId.AUDIO_CHANGE_STATUS, (status) => {
Logger.debug(TAG, "接收音频播放状态 = " + status);
if (!this.isPause) {
...
...
@@ -101,50 +116,7 @@ export struct WdWebLocalComponent {
Row() {
RelativeContainer() {
Web({
src: this.webResource,
controller: this.webviewControl,
renderMode: RenderMode.SYNC_RENDER
})// Web({ src: this.webResource, controller: this.webviewControl })
.backgroundColor(Color.White)
.domStorageAccess(true)
.databaseAccess(true)
.javaScriptAccess(true)
.imageAccess(true)
.mixedMode(MixedMode.All)
.onlineImageAccess(true)
.cacheMode(this.mode)
// .enableNativeEmbedMode(true)
// .layoutMode(WebLayoutMode.FIT_CONTENT)
.nestedScroll({
scrollForward: NestedScrollMode.SELF_FIRST,
scrollBackward: NestedScrollMode.PARENT_FIRST
})
.width('100%')
.height(this.webHeight)
.onPageBegin((event) => {
this.onPageBegin(event?.url);
})
.onPageEnd((event) => {
this.onPageEnd(event?.url)
})
.onLoadIntercept((event) => {
let url: string = event.data.getRequestUrl().toString()
url = url.replace("%(?![0-9a-fA-F]{2})", "%25")
.replace("\\+", "%2B");
url = decodeURIComponent(url)
Logger.debug(TAG, 'Web onLoadIntercept url: ' + url);
if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) {
this.webviewControl.handlerReturnData(url)
return true
}
if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) {
// this.webviewControl.flushMessageQueue()
this.webviewControl.receiveDataFromH5(url)
return true
}
return this.onLoadIntercept(event.data.getRequestUrl().toString());
})
NodeContainer(WebPoolManager.sharedInstance().getWebNodeController(this.webPoolTargetId))
.id('web')
.alignRules({
top: { anchor: "__container__", align: VerticalAlign.Top },
...
...
@@ -171,20 +143,6 @@ export struct WdWebLocalComponent {
}
}
private registerHandlers(): void {
// 注册h5调用js相关
for (let i = 0; i < H5CallNativeType.JsCallTypeList.length; i++) {
let handleName = H5CallNativeType.JsCallTypeList[i];
let handle = (data: Message, f: Callback) => {
Logger.debug('registerHandlers handlerName: ' + JSON.stringify(data.data))
this.setCurrentPageOperate8(data)
this.setCurrentPageOperate9(data)
this.defaultPerformJSCallNative(data, f)
};
this.webviewControl.registerHandler(handleName, { handle: handle });
}
}
//webview 高度设置
private setCurrentPageOperate8: (data: Message) => void = (data) => {
...
...
@@ -198,6 +156,8 @@ export struct WdWebLocalComponent {
{
this.webHeight = Number(data?.data?.webViewHeight) || '100%'
}
WebPoolManager.sharedInstance().getWebNodeController(this.webPoolTargetId)?.updateWebHeight(this.webHeight)
}
}
// 暂停音频悬浮窗
...
...
@@ -228,29 +188,18 @@ export struct WdWebLocalComponent {
/**
* 默认【CallNative】逻辑处理
*/
private defaultPerformJSCallNative: (data: Message, f: Callback) => void = (data: Message, f: Callback) => {
private currentPageOperate: (data: Message, f: Callback) => void = (data: Message, f: Callback) => {
this.setCurrentPageOperate8(data)
this.setCurrentPageOperate9(data)
performJSCallNative(data, f)
}
onPageBegin: (url?: string) => void = () => {
Logger.debug(TAG, 'onPageBegin');
this.registerHandlers();
//有时序问题 必须延时执行
setTimeout(() => {
BridgeUtil.webViewLoadLocalJs(getContext(this), this.webviewControl)
}, 200)
}
onPageEnd: (url?: string) => void = () => {
Logger.debug(TAG, 'onPageEnd');
this.onWebPrepared()
this.isPageEnd = true
}
onLoadIntercept: (url?: string) => boolean = () => {
Logger.debug(TAG, 'onLoadIntercept return false');
return false
}
onReloadStateChanged() {
Logger.info(TAG, `onReloadStateChanged:::refresh, this.reload: ${this.reload}`);
if (this.reload > 0) {
...
...
sight_harmony/commons/wdWebComponent/src/main/ets/webPool/WebArticleEventHandler.ets
0 → 100644
View file @
a77a8c2
import { BridgeUtil, BridgeWebViewControl, Callback } from "wdJsBridge";
import { Message } from "wdJsBridge/src/main/ets/bean/Message";
import { Logger } from "wdKit";
import { H5CallNativeType } from "../pages/H5CallNativeType";
import { WebEvents } from "./WebLocalPool";
const TAG = 'WebArticleEventHandler'
export class WebArticleEventHandler implements WebEvents {
webviewControl?: BridgeWebViewControl
pageLoadEnd: boolean = false
pageEndBlock?:(event: OnPageEndEvent) => void
currentPageOperateBlock?: (data: Message, f: Callback) => void
constructor(webviewControl: BridgeWebViewControl) {
this.webviewControl = webviewControl
}
//MARK: ----
onPrepareForReuse(): void {
}
onPageBegin(event: OnPageBeginEvent): void {
Logger.debug(TAG, 'onPageBegin');
this.registerHandlers();
//有时序问题 必须延时执行
setTimeout(() => {
BridgeUtil.webViewLoadLocalJs(getContext(this), this.webviewControl!)
}, 200)
}
onPageEnd(event: OnPageEndEvent): void {
Logger.debug(TAG, 'onPageEnd');
this.pageLoadEnd = true
if (this.pageEndBlock) {
this.pageEndBlock(event)
}
}
onLoadIntercept(event: OnLoadInterceptEvent): boolean {
let url: string = event.data.getRequestUrl().toString()
url = url.replace("%(?![0-9a-fA-F]{2})", "%25")
.replace("\\+", "%2B");
url = decodeURIComponent(url)
Logger.debug(TAG, 'Web onLoadIntercept url: ' + url);
if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) {
this.webviewControl?.handlerReturnData(url)
return true
}
if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) {
// this.webviewControl.flushMessageQueue()
this.webviewControl?.receiveDataFromH5(url)
return true
}
return false;
}
private registerHandlers(): void {
// 注册h5调用js相关
for (let i = 0; i < H5CallNativeType.JsCallTypeList.length; i++) {
let handleName = H5CallNativeType.JsCallTypeList[i];
let handle = (data: Message, f: Callback) => {
Logger.debug('registerHandlers handlerName: ' + JSON.stringify(data.data))
if (this.currentPageOperateBlock) {
this.currentPageOperateBlock(data, f)
}
};
this.webviewControl?.registerHandler(handleName, { handle: handle });
}
}
}
\ No newline at end of file
...
...
sight_harmony/commons/wdWebComponent/src/main/ets/webPool/WebLocalPool.ets
0 → 100644
View file @
a77a8c2
import { UIContext } from '@ohos.arkui.UIContext';
import { NodeController, BuilderNode, FrameNode } from '@ohos.arkui.node';
import { BusinessError } from '@ohos.base';
import { WebNetErrorList } from '@ohos.web.netErrorList';
import { Logger } from 'wdKit';
// @Builder中为动态组件的具体组件内容
// WebViewPoolData为入参封装类
class WebViewPoolData {
target: string = ''
webOption: WebOptions = { } as WebOptions
webEvents?: WebEvents = undefined
webHeight: Length = 0
constructor(webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) {
this.webOption = webOption
this.webEvents = webEvent
this.target = target
this.webHeight = webHeight
}
}
const TAG = 'WebViewPool';
// 影响web组件加载的网络错误码
class webLoadFail {
static ERR_TIMED_OUT = WebNetErrorList.ERR_TIMED_OUT;
static ERR_NETWORK_CHANGED = WebNetErrorList.ERR_NETWORK_CHANGED;
}
// 不需要重试刷新的网络错误码
class excludeRetryLoad {
static ERR_FAILED = WebNetErrorList.ERR_FAILED;
}
function isErrorCode(code: string) {
return Object.keys(webLoadFail).includes(code);
}
@Component
export struct DynamicWebView {
target: string = '';
private webOption: WebOptions = { } as WebOptions
private webEvents?: WebEvents = undefined
@Prop webHeight: Length
aboutToAppear(): void {
}
build() {
Row() {
Web(this.webOption)
.backgroundColor(Color.White)
.domStorageAccess(true)
.databaseAccess(true)
.javaScriptAccess(true)
.imageAccess(true)
.mixedMode(MixedMode.All)
.onlineImageAccess(true)
.nestedScroll({
scrollForward: NestedScrollMode.SELF_FIRST,
scrollBackward: NestedScrollMode.PARENT_FIRST
})
.width('100%')
.height('100%')
.onPageBegin((event) => {
this.webEvents?.onPageBegin(event)
})
.onPageEnd((event) => {
this.webEvents?.onPageEnd(event)
})
.onLoadIntercept((event) => {
if (!this.webEvents) {
return false
}
return this.webEvents?.onLoadIntercept(event);
})
}.width('100%')
.height(this.webHeight)
}
}
@Builder
export function webBuilder(data: WebViewPoolData) {
DynamicWebView({
webOption: data.webOption,
target: data.target,
webEvents: data.webEvents,
webHeight: data.webHeight
})
}
export class WebNodeController extends NodeController {
private rootNode: BuilderNode
<
[WebViewPoolData]> | null = null;
public webOption: WebOptions = { } as WebOptions
public webEvents?: WebEvents = undefined
private webHeight: Length = 0
private target: string = '';
public webNodeInTree:boolean = false;
constructor(webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) {
super();
this.webOption = webOption
this.webEvents = webEvent
this.target = target
this.webHeight = webHeight
}
aboutToAppear(): void {
this.webNodeInTree = true;
Logger.debug(TAG, `WebNodeController aboutToAppear target: ${this.target} ==> url:${this.webOption.src}`);
}
aboutToDisappear(): void {
try {
this.webNodeInTree = false;
// 下树时给commonWebView加载空白页面
// if (!getNWeb(this.target)) {
// this.webOption.controller?.loadData(
// "
<html><body
bgcolor=
\"#18181A\"
></body></html>
",
// "text/html",
// "UTF-8"
// );
// }
Logger.debug(TAG, `WebNodeController aboutToDisappear target: ${this.target} ==> url:${this.webOption.src}`);
} catch (error) {
Logger.error(TAG,`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
}
makeNode(context: UIContext): FrameNode | null {
if (!context) {
Logger.error(TAG, " uicontext is undifined : " + (context === undefined));
}
if (this.rootNode != null) {
// 返回FrameNode节点
return this.rootNode.getFrameNode();
}
// 返回null控制动态组件脱离绑定节点
return null;
}
// 此函数为自定义函数,可作为初始化函数使用
// 通过UIContext初始化BuilderNode,再通过BuilderNode中的build接口初始化@Builder中的内容
initWeb(uiContext: UIContext) {
if (this.rootNode != null) {
return;
}
this.rootNode = new BuilderNode(uiContext);
this.rootNode.build(wrapBuilder
<
[WebViewPoolData]>(webBuilder),
new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
}
prepareForReuse() {
this.webEvents?.onPrepareForReuse()
}
destoryWeb() {
this.rootNode?.dispose()
this.rootNode = null
}
updateWebHeight(webHeight: Length) {
try {
if (this.rootNode !== null) {
this.webHeight = webHeight
this.rootNode.update(new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
}
} catch (err) {
Logger.warn(TAG, `commonWebView update error : ${err}`);
}
}
updateWebEvents(webEvent: WebEvents) {
try {
if (this.rootNode !== null) {
this.webEvents = webEvent
this.rootNode.update(new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
}
} catch (err) {
Logger.warn(TAG, `commonWebView update error : ${err}`);
}
}
}
export interface WebEvents {
onPrepareForReuse(): void
onPageBegin(event: OnPageBeginEvent): void;
onPageEnd(event: OnPageEndEvent): void;
onLoadIntercept(event: OnLoadInterceptEvent): boolean;
}
// 创建Map保存所需要的NodeController
const nodeMap: Map
<string
,
WebNodeControllerInfo
>
= new Map();
// NodeController内容和创建时间戳
class WebNodeControllerInfo {
webNodeController?: WebNodeController;
createTime: number = 0;
constructor(webNodeController: WebNodeController, createTime: number) {
this.webNodeController = webNodeController;
this.createTime = createTime;
}
}
// 自定义获取NodeController接口
export const getPoolWebNodeController = (target: string): WebNodeController | undefined => {
return nodeMap.get(target)?.webNodeController;
}
// 自定义获取WebviewController接口
export const getPoolWebController = (target: string): WebviewController | undefined => {
return nodeMap.get(target)?.webNodeController?.webOption.controller as WebviewController;
}
// 初始化需要UIContext 需在Ability获取
export const createReuseableWeb = (uiContext: UIContext, webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) => {
// 创建NodeController
let baseNode = new WebNodeController(webOption, target, webHeight, webEvent);
// 初始化自定义Web组件
baseNode.initWeb(uiContext);
nodeMap.set(target, new WebNodeControllerInfo(baseNode, Date.now()));
}
export const destoryReuseableWeb = (target: string) => {
if (nodeMap.get(target)) {
getPoolWebNodeController(target)?.destoryWeb()
nodeMap.delete(target)
}
}
\ No newline at end of file
...
...
sight_harmony/commons/wdWebComponent/src/main/ets/webPool/WebPoolManager.ets
0 → 100644
View file @
a77a8c2
import { BridgeWebViewControl } from "wdJsBridge"
import { AppUtils } from "wdKit"
import { createReuseableWeb, destoryReuseableWeb, getPoolWebNodeController, WebNodeController } from "./WebLocalPool"
import { util } from "@kit.ArkTS"
import { WebArticleEventHandler } from "./WebArticleEventHandler"
interface ArticleWebInterface {
uiContext: UIContext,
webSrc: string | Resource,
webController: BridgeWebViewControl
webArticleEventHandler: WebArticleEventHandler
}
export class WebPoolManager {
private webTargets: string[] = []
private constructor() {
}
private static manager: WebPoolManager
static sharedInstance() : WebPoolManager {
if (!WebPoolManager.manager) {
WebPoolManager.manager = new WebPoolManager()
}
return WebPoolManager.manager
}
createArticleWeb(param: ArticleWebInterface, taking?: boolean) : string {
let target = util.generateRandomUUID()
createReuseableWeb(param.uiContext,
{src: param.webSrc, controller: param.webController},
target,
800,
param.webArticleEventHandler
)
if (taking != true) {
this.webTargets.push(target)
}
return target
}
// 取一个可用的target,不存在则根据参数创建
// 当使用完成,需要调用recycleTarget()
takeAvaiableArticleWebTarget(param: ArticleWebInterface): string {
if (this.webTargets.length) {
return this.webTargets.slice(0, 1).pop()!
}
return this.createArticleWeb(param, true)
}
// 回收target
recycleTarget(target: string) {
let nodeController = getPoolWebNodeController(target)
if (!nodeController) {
let index = this.webTargets.indexOf(target)
if (index >= 0) {
this.webTargets.splice(index, 1)
}
return
}
nodeController.prepareForReuse()
this.webTargets.push(target)
}
// 撤底销毁
destoryTarget(target: string) {
destoryReuseableWeb(target)
let index = this.webTargets.indexOf(target)
if (index >= 0) {
this.webTargets.splice(index, 1)
}
}
getWebNodeController(target: string) : WebNodeController | undefined {
return getPoolWebNodeController(target)
}
getWebController(target: string) : BridgeWebViewControl | undefined {
return getPoolWebNodeController(target)?.webOption.controller as BridgeWebViewControl
}
getWebEventHandler(target: string) : WebArticleEventHandler | undefined {
return getPoolWebNodeController(target)?.webEvents as WebArticleEventHandler
}
}
\ No newline at end of file
...
...
sight_harmony/features/wdComponent/src/main/ets/components/ImageAndTextWebComponent.ets
View file @
a77a8c2
...
...
@@ -8,7 +8,7 @@ import {
} from 'wdBean';
import { Logger, SPHelper, NetworkUtil, DisplayUtils } from 'wdKit';
import { SpConstants } from 'wdConstant';
import { WdWebLocalComponent } from 'wdWebComponent';
import { WdWebLocalComponent
, WebArticleEventHandler, WebPoolManager
} from 'wdWebComponent';
import { NativeCallH5Type } from 'wdWebComponent/src/main/ets/pages/NativeCallH5Type';
import { BridgeWebViewControl } from 'wdJsBridge/Index';
...
...
@@ -19,6 +19,7 @@ export struct ImageAndTextWebComponent {
@State reload: number = 0;
@Link isPageEnd: boolean
@Prop @Watch('onDetailDataUpdated') contentDetailData: ContentDetailDTO [] = [] as ContentDetailDTO []
webPoolTargetId: string = ''
webviewControl: BridgeWebViewControl = new BridgeWebViewControl()
private h5ReceiveAppData: H5ReceiveDetailBean = { dataSource: '2' } as H5ReceiveDetailBean
private webPrepared = false;
...
...
@@ -95,11 +96,20 @@ export struct ImageAndTextWebComponent {
aboutToAppear(): void {
Logger.debug(TAG, 'H5模板加载控件 aboutToAppear');
this.webPoolTargetId = WebPoolManager.sharedInstance().takeAvaiableArticleWebTarget({
uiContext: this.getUIContext(),
webSrc: $rawfile('apph5/index.html'),
webController: this.webviewControl,
webArticleEventHandler: new WebArticleEventHandler(this.webviewControl)
})
this.webviewControl = WebPoolManager.sharedInstance().getWebController(this.webPoolTargetId)!
}
build() {
Column() {
WdWebLocalComponent({
webPoolTargetId: this.webPoolTargetId,
webviewControl: this.webviewControl,
reload:this.reload,
webResource: $rawfile('apph5/index.html'),
...
...
Please
register
or
login
to post a comment