AppNest快速开发(2)web功能
AppNest—HybridApp壳子web能力介绍
壳子的web功能使用开源项目AgentWeb作为内核进行封装实现
- 提供了基础的WebViewActivity,WebViewFragment,可以通过预定义好的js方法,加载独立的web页面。
- web带参打开,web带参返回等基础功能。
- 集成了腾讯的X5浏览器内核,展示office,pdf的格式的文件。
- 图片,视频的拍摄或从手机中选择等功能。
- 调起二维码扫描结果返回web页面(华为扫码库实现,实测扫码效率优于其他三方库(可在所以品牌机型上使用,并不限制华为机型))
在AppNestWeb中可以找到提供的预定义的js事件的调用方式及具体示例
WebViewFragment
真正完成web内容渲染的Fragment,WebViewActivity只是完成了对WebViewFragment的承载工作。
WebViewFragment使用androidx.navigation.safeargs的方式,定义了入参,可传入的参数包含
参数名 | 参数意义 |
---|---|
url(必须) | 访问的页面地址 |
title | 页面抬头显示的内容,可以通过js事件动态设置 |
showTitleBar | 是否展示头部的titlebar |
showRightMenu | 是否展示右侧的按钮 |
showBackIcon | 是否展示左侧的返回按钮 |
addTopPadding | 是否添加头部padding,适配刘海屏 |
showRefreshBar | 是否支持原生下拉刷新 |
pushData | 打开页面时携带的js写入页面的参数 |
companion object {
fun newInstance(
url: String,
title: String = "",
showTitleBar: Boolean = true,
showRightMenu: Boolean = false,
showBackIcon: Boolean = true,
addTopPadding: Boolean = false,
showRefreshBar: Boolean = false,
pushData: String = "",
): WebViewFragment {
val fragment = WebViewFragment()
val args = WebViewFragmentArgs
.Builder()
.apply {
this.title = title
this.url = url
this.showTitleBar = showTitleBar
this.showRightMenu = showRightMenu
this.showBackIcon = showBackIcon
this.addTopPadding = addTopPadding
this.showRefreshBar = showRefreshBar
this.pushData = pushData
}
.build()
.toBundle()
fragment.arguments = args
return fragment
}
}
H5WebView
构造方法
参数名 | 参数意义 |
---|---|
context(必须) | 页面上下文对象 |
needBackgrounColor | 是否需要webView背景颜色 |
mWebViewTitleBarController | titlebar功能回调接口 |
mCustomEventCallBack | 自定义事件回调接口 |
class H5WebView(
private val context: Context,
private val needBackgrounColor: Boolean = false,
private val mWebViewTitleBarController: WebViewTitleBarController? = null,
private var mCustomEventCallBack: CustomEventCallBack? = null,
)
在H5WebView的init方法中,会判断context是否为LifecycleOwner,如果是,则会添加对context的生命周期进行观察,在LifecycleOwner的对应生命周期事件发生时会对WebView对象进行相应的处理,避免LifecycleOwner销毁了,WebView没有释放的情况
JSBridge
读取并向WebView中插入JSBridge.js,用于进行web页面与安卓的互调
JSBridge.kt中包含所有预定义的js事件的处理流程,其中包含的基础方法包含
参数名 | 参数意义 |
---|---|
setTitle | 设置原生标题 |
setOptionMenu | 设置右上角按钮图片及文字 |
popWindow | 关闭页面 |
pushWindow | 打开新页面 |
init | 页面初始化 |
postNotification | 向原生投送事件(用户自定义行为统一在这里以事件的形式进行处理) |
pushWindow
pushWindow事件打开新页面
其中会对加载的url地址进行解析,判断是否为图片,视频,office,pdf等的文件地址
如果是则调用图片展示,视频播放,文件预览等功能,进行打开
如果不是则打开一个新的WebViewActivity进行新页面的渲染,其中的“data”参数中的数据会作在新页面打开时被写入到新的页面中,作为初始化参数
popWindow
popWindow事件关闭页面
Web页面控制关闭当前的WebViewActivity
在data中预定义了一些行为
参数名 | 参数意义 |
---|---|
toRoot | 是否回到App首页 |
needCheckPopTo | 是否回跳到指定页面,需要与popTo事件配合使用popTo写入标记,needCheckPopTo回调popTo标记的页面上 |
postNotification
收到自定义事件,会获取对应的事件名,事件请求码,事件参数,交给单独的处理类进行处理
其中:announce为事件请求码
在事件处理回调中会进行返回
添加该参数是因为在实际开发中,发现web页面由多层组件组成,同样的js事件可能在不同层的web页面均包含监听,该参数是为了让web页面能够在回调时,知道应该由那一层的方法去处理数据
例如:在一个页面中包含图片选择功能,评论功能也由该功能,在回调时就要区分是那一层发起的请求,来处理对应数据
js_fun_postNotification -> {
data?.run {
val name = getString("name")
val announce = getIntValue("announce")
val paramsData = getJSONObject("data")
JSActionManager.sendActionEvent(
context,
name,
paramsData,
jsCallBack,
announce
)
}
}
JSNotificationHandler
web作为独立的module,在调用其他模块的功能时会带来不便,添加了JSNotificationHandler类处理跨module的业务需求
abstract class JSNotificationHandler {
val notificationList: MutableList<String> = mutableListOf()
abstract fun onNewNotification(
context: Context,
eventName: String = "",
param: JSONObject,
announce: Int,
jsCallBack: JSCallBack,
): Boolean
}
//其他模块可以在初始化时向webmodule注册自己所提供的能力及对应的回调处理
JSActionManager.jsNotificationHandlers.add(AppJSActionHandler)
object AppJSActionHandler : JSNotificationHandler() {
//注册自己提供的功能
init {
notificationList.add(JSNotificationAction.jsshowGallery + DEFAULT_JS_SUFFIX)
notificationList.add(JSNotificationAction.jsSocialLogin + DEFAULT_JS_SUFFIX)
notificationList.add(JSNotificationAction.jsScan + DEFAULT_JS_SUFFIX)
}
//预定义的功能在有请求时的回调
override fun onNewNotification(
context: Context,
eventName: String,
param: JSONObject,
announce: Int,
jsCallBack: JSCallBack
): Boolean {
return when (eventName) {
JSNotificationAction.jsshowGallery + DEFAULT_JS_SUFFIX -> {
val json = param.getString("data")
try {
val gson = Gson()
val maxBean = gson.fromJson(json, MaxBean::class.java)
MaxViewV2Activity.invoke(KtxManager.currentActivity!!, maxBean)
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
true
}
JSNotificationAction.jsSocialLogin + DEFAULT_JS_SUFFIX -> {
socialLogin(context, jsCallBack, param, announce)
true
}
JSNotificationAction.jsScan + DEFAULT_JS_SUFFIX -> {
HWQRCodeScannerActivity.invoke(context, jsCallBack, announce)
true
}
else -> false
}
}
}