使用uniapp制作安卓app容器

1. 背景

项目需要做一个安卓app,而且不需要上架应用市场,部门也没有安卓开发,想着就套个webview就行了吧。没有选择react native之类的是因为这些工具需要安装很多环境工具,我只是开发一个壳子没必要这么复杂。

用webview也方便快速修复页面问题。

所以最后选择了uniapp,但是uniapp本身就是套在一个大的webview下的, 所以再套一个webview难免会有一些意想不到的问题,下面就是一些踩过的坑记录。

2. 项目初始化

新建项目就默认模板就行,我只需要壳子。

启动了之后可以看到有两个调试工具

第一个就是网页上常用的vue调试工具,可以看到vue组件属性啥的,第二个就是类似chrome的控制台,但是无法查看元素,还有就是必须让设备和电脑在同一个网段下才行,不然连接不上。

hbuilder的控制台本身也有一些输出,比如页面的console

但是这里输出对象的时候不是很方便查看,如果你需要的话就打开上面说的第二个调试工具。

3. webview使用

整个项目很简单,大概就这样一个页面

html 复制代码
<template>
	<web-view :src='PROJECT_PATH' @message="onMessage"></web-view>
</template>
<script>
    // ...
</script>

3.1 网页与app通信

这是最重要的一个功能,可以参考官方文档。

网页和app交互总结起来就是这两点:

  • 网页 -> APP:window.uni.postMessage();
  • APP -> 网页:webview.evalJS()

3.1.1. 网页 -> APP

首先要在项目中引入uni.webview.js,这个就相当于jsbridge,可以让网页操作uniapp。

初始化完成后会在window上挂载一个uni对象,通过uni.postMessage就能往app发送消息,app中监听onMessage就行。

这里有几个小坑:

  1. 发送的格式window.uni.postMessage({ data: 数据 }),必须要有个字段data,这样app才能收到数据。源码

2. 发送的数据不需要序列化成字符串,uniapp会转换json。 3. app在message事件中接收到事件参数应该这样解构

ts 复制代码
function onMessage(e) {
    const {
            type,
            data
    } = e.detail.data[0]
}

3.1.2. APP -> 网页

app向网页传输消息就直接调用网页的js就行了。这里我统一封装了一个函数:

ts 复制代码
// app向网页发送消息
const deliverMessage = (msg) => {
    // 调用webview中的deliverMessage函数
    // 这个函数是我在网页挂载的一个全局函数,调用deliverMessage后会触发页面中的一些事件
    currentWebview.evalJS(`deliverMessage(${JSON.stringify(msg)})`)
}

上面的代码例子中出现的currentWebview需要我们自己去获取。

ts 复制代码
// vue2中
const rootWebview = this.$scope.$getAppWebview()
this.currentWebview = rootWebview.children()[0]

// vue3中
import {
    getCurrentInstance,
    ref,
} from "vue";
const currentWebview = ref(null)
const vueInstance = getCurrentInstance()
const rootWebview = vueInstance.proxy.$scope.$getAppWebview()
currentWebview.value = rootWebview.children()[0]

这里也有一个坑,rootWebview.children()如果你一渲染就获取是无法获取到webview实例的,具体原因没有深入研究,估计是异步的原因

这里提供两个思路:

  1. 加一个定时器,延迟获取webview,这个方法虽然听起来不保险,但是实际测试还是挺稳当的。关键是简单。
ts 复制代码
setTimeout(() => {
    currentWebview.value = rootWebview.children()[0]
}, 1000)
  1. 你要是觉得定时器不保险,那就使用plus的api手动创建webview。但是消息处理这块比较麻烦。官网参考
html 复制代码
<template>
    <!-- 这里面就不需要webview组件了 -->
</template>
ts 复制代码
// 我这里vue3为例
onMounted(() => {
    plus.globalEvent.addEventListener('plusMessage', ({data: {type, args}}) => {
        // 是网页调用uni的api
        if(type === 'WEB_INVOKE_APPSERVICE') {
            const {data: {name, arg}} = args
            // 是发送消息事件
            if(name === 'postMessage') {
                // arg就是传过来的数据
            }
        }
    })
    const wv = plus.webview.create("", "webview", {
        'uni-app': 'none',
    })
    wv.loadURL(网页地址)
    rootWebview.append(wv);
})

plus.globalEvent.addEventListener这个是翻源码找到的,主要是我不想改uni.webview.js的源码,所以只有找到正确的监听事件。

WEB_INVOKE_APPSERVICE是uniapp内部定义的一个名字,反正就是用来交互操作的命名空间。

这样基础的互操作就有了。

3.1.3. 整个流程

  1. 网页调用window.uni.postMessage({ data }) => app监听(用组件的onMessage或者自定义的globalEvent)
  2. app调用网页定义的函数deliverMessage并传递参数,网页中的deliverMessage内部处理监听
ts 复制代码
// 网页中的deliverMessage
window.deliverMessage = (msg) => {
  // 触发网页注册的监听器
  eventListeners.forEach((listener) => {
    
  });
};

3.2. 返回拦截

默认情况下,手机按下返回键,app会响应提示是否退出,但是实际我需要网页进入二级路由的时候,按下手机返回键是返回上一级路由而不是退出。当路由是一级路由时才提示是否退出app

ts 复制代码
import {
    onBackPress,
    onShow,
} from '@dcloudio/uni-app'
// 页面当前的路由信息
const pageRoute = shallowRef()
onBackPress(() => {
    // tab页正常app返回逻辑
    if (pageRoute.value?.isTab) {
        return false
    } else {
        // 二级路由拦截app返回
        return true
    }
})

pageRoute是页面当前路由信息,页面通过监听路由变化触发routeChange事件,将路由信息传给app。当按下返回键的时候,判断当前路由配置是不是tab页,如果是就正常退出,不是就拦截返回。

4. 总结

有了通信功能,很多操作就可以实现了,比如获取设备safeArea,获取设备联网状态等等。

相关推荐
沙漠之主7 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++
hasty7 小时前
不上传新包,也能改变用户拿到的版本:npm dist-tag 的 OIDC 权限治理
前端·npm·node.js
Csvn7 小时前
diff 算法(虚拟 DOM Reconciliation)
前端
前端snow8 小时前
ai agent --- 文件存储
前端
Frag0ut8 小时前
Chrome四大版本获取及共存指南:Stable/Beta/Dev/Canary
前端·chrome·浏览器·dev·beta·canary·共存版
IT_陈寒10 小时前
SpringBoot自动配置坑了我一把,原来是这样绕过去的
前端·人工智能·后端
广州华水科技10 小时前
单北斗GNSS变形监测在大坝安全监测中的应用与优势
前端
zeng不错10 小时前
B站 173 个投稿活动看到眼瞎?我写了个扩展,主打一个精准打击
前端
Latchh10 小时前
前端导出的JPEG红字发糊,质量要拉到100才清楚
前端·图像处理·人工智能·计算机视觉
默_笙11 小时前
🚦 请三个 AI 考官给 RAG 打分:量化评估实战(下)
前端·javascript