用uniapp
开发过小程序
、H5
、App
的小伙伴们肯定都用过HBuilderX
吧;它的强大我就不提了。当前如果你遇到过哪些坑,欢迎在留言区讨论吐槽一下,也希望它能更加好用~
毕竟它还是帮你解决了很多问题的,是吧?
本文主要跟大家分享一下,如何
在非【HBuilderX】编辑器下
编译 uniapp 并运行 Android apk
。如果你有这样的想法,或者有更好的方法,望不吝赐教~
最近我用的GitHub Copilot
嗝屁了... 这里给大家推荐一下:CodeGeex
。 先简单开个小G。
前言
首先阅读本文前
,希望你已经大概清楚或了解过以下工具、语言基础
:
vscode
(编辑器)java、uniapp离线打包
(原生 Android 开发)gradle
(构建apk工具 / 包管理工具)adb
(Android 调试工具)nodejs
(语言/打包、构建工具)uni cli
(uniapp 脚手架)系统环境变量、命令行/shell
如果你不清楚,没关系,只要你的网络正常
,那么请自行上网科普~
1.uni cli 项目初体验
cli文档链接:uniapp.dcloud.net.cn/quickstart-…
小手动起来~
# 可能无法正常创建 跟网络环境有关!!
npx degit dcloudio/uni-preset-vue#vite unicli-vue3-demo
注意:有时 npm 版本过老(node 版本),也可能无法正常创建的。我这里使用的 npm 9.5.1正常。 没有什么是一成不变的... 不行就换换方式😂
你将得如下结构的一个项目
使用VSCODE
打开此项目,然后打开package.json
,你会发现:
顾名思义,相信你看到这些应该知道怎么运行H5
看看效果了吧。
当前第一步肯定是【npm install】安装依赖啊~~ 不然... 啥也不是
# 调试运行 H5
npm run dev:h5
# 调试运行 app
npm run dev:app
2.它说打开“HBuilderX”
当你使用npm run dev:app
运行后,你将清晰的看到:
咳咳~ 虽然你很好,但是我就是想用用其他编辑器运行到 Android 端。
熟悉/了解 uniapp Android 离线打包
的小伙伴们,应该知道,每次手动 “生成本地打包App资源” 的而后复制、打包的一系列过程。
🤔思考:
如何能自动去处理这一系列操作?关键点是什么呢?
我想不用我多说,很多小伙伴已经知道了大致方向:
监听文件变化
,去执行复制打包所需的资源
;- 执行
gradle 命令打包
,并使用adb安装到设备
;
🎉监听文件变化
这也是我初次的想法,但是后来我抛弃了它,因为想到了个更简单的方法:
nodejs
执行子进程
监听原有命令输出日志
。
监听文件变化
虽然【复杂一点】,但是它可以【做到更细化】的文件复制替换
。 当然两者结合更棒啦~
3.初探【紫荆城】
nodejs
的 线程/子进程
相关的知识,这里就不多说了,反正大家网络是畅通
的。
在项目根目录新增scripts
文件夹,,并创建watch.js
文件:
如图所示,仅需导入内置的几个包。来处理路径、文件、进程。
小试牛刀🚀, 在 watch.js
文件中 编辑以下代码,执行子进程
:
// xxx 省略导包
console.log("开始执行");
const app = spawn("npm run dev:app", { shell: process.platform === 'win32' });
app.stdout.on("data", (data) => {
const log = Buffer.from(data).toString("utf8");
console.log(log);
// 编译成功
if (log.includes("DONE Build complete.")) {
console.log("🎉🎉编译成功🎉🎉");
}
});
app.stderr.on("data", (data) => {
console.log("stderr-->");
const log = Buffer.from(data).toString("utf8").replace(/\n/g, "");
console.log(log);
});
app.on("error", (err) => {
console.log("子进程 error:");
console.log(err);
});
app.on("exit", (code) => {
console.log(`子进程 exit: ${code}`);
});
保存,在终端中输入: node scripts/watch.js
啊哈~ 成功监听到了,并输出了:🎉🎉编译成功🎉🎉
。
去修改点项目源代码,像main.js
、App.vue
这些文件保存😁试试水,🌈nice! 成功触发:
🤔思考:如果此时
Ctrl + C
关闭了当前终端运行的node scripts/watch.js
,这个npm run dev:app
的子进程
它退出了么?为什么?
4.复制App打包资源
当控制台输出 🎉🎉编译成功🎉🎉
的时候, dist/dev/app
已经成功出世啦~ 它就是 uniapp 离线打包需要的 关键资源。
此时应该怎么通知
可以进行复制
操作呢?还是直接接着写代码?
恰好nodejs
内置提供了events
包,方便线程之间通信。 真是太棒了~~
在watch.js
中添加 以下代码:
// xxx 省略
// 进程间通信
const EventEmitter = require('events');
const event = new EventEmitter();
// 避免 魔法 字符串
const EVENTS = {
COPY: 'COPY', // 构建成功后,复制资源
};
// 资源路径
// 当前项目 dev:app 构建出来的 资源目录
const RUN_DIR = path.resolve(__dirname, '../dist/dev/app');
// uniapp Android 离线打包 资源目录
const ANDROID_ASSETS_DIR = 'F:\\uniapp_offline_android\\app\\src\\main\\assets\\apps\\__UNI__XXXXXX\\www';
// 复制文件夹方法
const copyFolderSync = (source, target) => {
// 创建目标文件夹
if (!fs.existsSync(target)) {
fs.mkdirSync(target);
}
// 遍历源文件夹中的每个文件或子文件夹
fs.readdirSync(source).forEach((file) => {
// 构造源文件/文件夹的完整路径
const sourcePath = path.join(source, file);
// 构造目标文件/文件夹的完整路径
const targetPath = path.join(target, file);
// 获取文件/文件夹的详细信息
const stat = fs.statSync(sourcePath);
if (stat.isFile()) {
// 如果是文件,则直接复制文件
fs.copyFileSync(sourcePath, targetPath);
} else if (stat.isDirectory()) {
// 如果是文件夹,则递归复制文件夹
copyFolderSync(sourcePath, targetPath);
}
});
});
// 复制资源
const autoCopy = () => {
console.log('-----> autoCopy');
// 文件夹是否存在,否则创建
copyFolderSync(RUN_DIR, ANDROID_ASSETS_DIR);
console.log('复制成功');
event.emit(EVENTS.RUN);
};
// 监听事件
event.on(EVENTS.COPY, autoCopy);
// xxx 省略
// 编译成功
if (log.includes("DONE Build complete.")) {
console.log("🎉🎉编译成功🎉🎉");
// 传递消息 !!!!!
event.emit(EVENTS.COPY);
}
// xxx 省略
OK~ 通了,通啦! 可以自动复制打包用的App资源啦~
5.执行 gradle 命令
如果你不熟悉,原生 Android 的打包/运行
;那么请自行网络科普一下: gradle 命令
。
为了方便
在 nodejs
中执行对应的命令
。 请先配置一些环境变量
:
java
adb
环境变量
在各个系统中,配置方式不一
,作用都是一样的,就像任意门一样
,让你能在任何地方
,能够执行命令
工具:
本次的主角:installDebug
:
顾名思义,就是安装debug包
。
在AndroidStudio
里,我们通常
是双击运行
这个,那么它怎么在命令行里面执行呢?
不知道?科普一下咯~
命令行
试试水:
sh gradlew installDebug
以上是以 sh(shell)
执行的 gradlew installDebug
。
在nodejs
中,当然同样可以执行
:
const { spawn } = require("child_process");
// cd 移动到 Android 工程跟目录; 然后 执行 gradle 命令
const installProcess = spawn('cd /xxxx/HBuilder-Integrate-AS && gradlew installDevDebug', { shell: process.platform === 'win32' });
🤔思考 执行
spawn
时,为什么要添加参数{ shell: process.platform === 'win32' }
, 它的作用是什么呢?
ok,完善一下代码:
// xxx 省略
const EVENTS = {
COPY: 'COPY', // 构建成功后,复制资源
RUN: 'RUN' // 复制成功后,安装
};
const autoCopy = () => {
// xxx 省略
console.log('复制成功');
// 添加
event.emit(EVENTS.RUN);
};
// 安装 APK
const autoRun = () => {
console.log('-----> autoRun');
const installProcess = spawn('cd F:\\uniapp_offline_android && gradlew installDebug', { shell: process.platform === 'win32' });
installProcess.stdout.on('data', (data) => {
console.log('stdout-->');
const log = Buffer.from(data).toString('utf8');
console.log(log);
if (log.includes('BUILD SUCCESSFUL')) {
// 这里可以 使用 adb 判断 是否已经安装了,执行打开应用等等...
console.log('安装成功!');
}
});
installProcess.stderr.on('data', (data) => {
const log = Buffer.from(data).toString('utf8');
console.log(log);
});
installProcess.on('error', (err) => {
console.log('子进程 error:');
console.log(err);
});
};
// 监听事件
event.on(EVENTS.COPY, autoCopy);
event.on(EVENTS.RUN, autoRun);
// xxx 省略
🤔思考:除了
installDebug
命令,还可以怎样去安装应用呢?
可以怎么去优化这个功能?
它能直接启动应用的某个activity页面不呢?
6.高级的 adb 命令
adb
不仅可以查看连接的设备
,还可以,安装/卸载应用
、检查安装的应用列表
、模拟点击
、获取手机信息
、执行shell命令
等等。高级! 当然,它需要你手机打开开发者模式
,并开启 USB 调试
。
先来看看连接的设备列表吧:
adb devices
列出安装的第三方(非系统)的应用(包名)列表:
adb shell pm list packages -3
安装本电脑上的apk:
adb install /xxx/base.apk
至此,我相信你已经知道
,可以怎么优化自动安装应用了吧
。
总结
有时候,大胆发散一下你的思维,这个功能/需求能不能做!怎么去做,它是怎么个流程,原理是什么。或许并没有想像中的那么难!
动动手,实践一下!
CodeGeex
等AI工具,可以根据我们的思路
快速提供一段代码 demo
;以便我们进一步思索;当然别拿着代码就运行!万一有个 rm -rf
。uni cli 脚手架
,可以在非【HBuilderX】编辑器下,进行愉快编码,但是需要
我们自行扩展功能
;比如:自动开发微信开发者工具、自动发布小程序等等。任何一门语言,都有它对应的工具、依赖、社区;作为一名开发者,多了解一些知识总归是有些用处的
。
欢迎各位码友分享转发
、评论区高见
,辛苦各位点个赞
、收藏
、在看
咯~