更新记录
1.0.0(2026-05-25)
- 初始版本
- 支持扫描共享存储
.apk.1文件(scanStorage) - 支持从 content URI 导入文件(
importFromUris) - 支持读取 APK 元信息:包名、版本号、图标(Base64)
- 支持安装
.apk.1文件(自动剥离后缀,FileProvider 调起系统安装器) - 支持查询/刷新安装状态
平台兼容性
uni-app(4.87)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | Android插件版本 | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | √ | 1.0.0 | √ | 1.0.0 | 8.0 | 1.0.0 | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(4.87)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | - | - | - | - |
hl-apk1-installer — .apk.1 安装器 UTS 插件
将 .apk.1 重命名包(及多层后缀 .apk.1.1.1 等)自动处理并调起 Android 系统安装器,无需用户手动重命名。同时支持接收其它 App 分享过来的 APK1 文件、转发文件到其它 App。
仅支持 Android(minSdk 26 / Android 8.0+)
功能
| 能力 | 函数 |
|---|---|
扫描存储中的 .apk.1 文件 |
scanStorage() |
| 从 content URI 导入文件 | importFromUris() |
| 接收其它 App 分享过来的 Intent | onSharedIntent() |
| 安装文件(自动剥离后缀) | installFile() |
| 调起系统分享面板发送文件 | shareFile() |
| 查询安装未知应用权限 | canInstall() |
| 跳转安装权限设置页 | openInstallSettings() |
| 查询全盘存储访问权限 | hasStorageAccess() |
| 跳转全盘存储设置页 | openStorageSettings() |
| 批量删除文件 | deleteFiles() |
| 刷新文件的安装状态 | refreshInstallStates() |
快速上手
1. 扫描并安装
import * as Apk1Installer from '@/uni_modules/hl-apk1-installer'
// 扫描存储(需 MANAGE_EXTERNAL_STORAGE 权限,无权限时仅返回导入目录内容)
Apk1Installer.scanStorage((filesJson, error) => {
if (error) { console.error('扫描失败:', error); return }
const files = JSON.parse(filesJson)
// files: ApkFileInfo[]
console.log('找到', files.length, '个文件')
})
// 安装指定文件
Apk1Installer.installFile('/sdcard/Download/demo.apk.1', (success, msg) => {
console.log(success ? '安装器已启动' : '失败: ' + msg)
})
2. 接收其它 App 分享过来的文件(推荐在页面 onLoad 中调用一次)
import * as Apk1Installer from '@/uni_modules/hl-apk1-installer'
export default {
onLoad() {
Apk1Installer.onSharedIntent((filesJson, error) => {
if (error || !filesJson || filesJson === '[]') return
const imported = JSON.parse(filesJson)
// imported: ApkFileInfo[]—已复制到插件私有导入目录
// 在这里提示用户是否立即安装
})
}
}
其他调用者可能发起的 Intent(插件内部已处理,无需区分):
ACTION_SEND:微信 / QQ / 邮箱等单文件分享ACTION_SEND_MULTIPLE:多文件分享ACTION_VIEW:文件管理器点击文件、浏览器下载打开
工作模式:
- 冷启动(App 未运行,其它 App 拉起本 App):
onLoad中调用onSharedIntent后,插件立即读取当前 Activity 的 Intent 并触发回调。 - 热启动(App 在后台,收到新分享 Intent):插件通过
AppHookProxy+ActivityLifecycleCallbacks监听 ActivityonResume,自动读取新 Intent 并触发同一个已注册的回调。页面只需onLoad调用一次即可。
处理完成后插件会自动清空 Intent 的 action / data / EXTRA_STREAM,防止重复触发。
3. 转发文件到其它 App
import * as Apk1Installer from '@/uni_modules/hl-apk1-installer'
Apk1Installer.shareFile(file.filePath, (success, msg) => {
console.log(success ? '分享面板已打开' : '失败: ' + msg)
})
已位于插件导入目录的文件直接通过 FileProvider 提供;共享存储中的文件先复制到 cacheDir/hl-apk1-share-cache/ 再提供,保留原始文件名。
4. 刷新安装状态
import * as Apk1Installer from '@/uni_modules/hl-apk1-installer'
// currentFilesJson 是上次 scanStorage/importFromUris 返回的 JSON 字符串
Apk1Installer.refreshInstallStates(currentFilesJson, (updatedJson) => {
const files = JSON.parse(updatedJson)
// 每个 file.installState 已更新
})
5. 权限检查与引导
import * as Apk1Installer from '@/uni_modules/hl-apk1-installer'
if (!Apk1Installer.canInstall()) {
Apk1Installer.openInstallSettings() // 跳转「安装未知应用」权限页
}
if (!Apk1Installer.hasStorageAccess()) {
Apk1Installer.openStorageSettings() // 跳转「全盘文件访问」权限页
}
6. 完整 demo 页面生命周期示例
import * as Apk1Installer from '@/uni_modules/hl-apk1-installer'
export default {
data() { return { files: [] } },
onLoad() {
// 1) 请求权限
if (!Apk1Installer.canInstall()) Apk1Installer.openInstallSettings()
if (!Apk1Installer.hasStorageAccess()) Apk1Installer.openStorageSettings()
// 2) 扫描已有文件
Apk1Installer.scanStorage((json) => { this.files = JSON.parse(json) })
// 3) 注册分享 Intent 回调(冷启动 + 热启动都会触发)
Apk1Installer.onSharedIntent((json, error) => {
if (error || !json || json === '[]') return
const imported = JSON.parse(json)
const ids = new Set(this.files.map(f => f.id))
imported.forEach(f => { if (!ids.has(f.id)) this.files.unshift(f) })
// 弹框询问是否立即安装...
})
}
}
ApkFileInfo 数据结构
{
id: string // 文件绝对路径(唯一 ID)
filePath: string // 磁盘绝对路径
displayName: string // 文件名(含扩展名)
appName: string // 应用名;解析失败时退化为文件名
packageName: string | null // 包名;null = APK 解析失败
versionName: string | null // 版本名称
versionCode: number // 版本号;-1 = 无法获取
sizeBytes: number // 文件字节大小
modifiedAt: number // 最后修改时间(Unix 毫秒)
installState: number // 0=未安装 1=已安装 2=解析失败
iconBase64: string | null // Base64 PNG 图标(无损)
sourceKind: number // 0=存储扫描 1=手动导入
}
内部安装链路
installFile(filePath)
├─ 检查 canRequestPackageInstalls()
│ └─ false → openInstallSettings() + callback(false)
├─ 复制到 cacheDir/hl-apk1-install-cache/<session>/<name>.apk
├─ Apk1FileProvider.getUriForFile(authority, tempApk)
└─ startActivity(ACTION_VIEW, apk-mime, uri, FLAG_GRANT_READ_URI_PERMISSION)
→ 系统安装器弹窗
分享 Intent 接收机制
[冷启动]
其它 App 发送 Intent → Android 拉起本 App
→ 页面 onLoad 调用 onSharedIntent(callback)
→ ApkBridge 读取 activity.intent + 存储 callback
→ importFromUris(uris, callback) → 复制到私有目录 → 回调为前端
[热启动]
App 运行中 → 其它 App 发送新 Intent
→ Android 调用 Activity.onNewIntent(newIntent) → activity.intent 更新
→ Activity.onResume → AppHookProxy.onActivityResumed
→ ApkBridge.onActivityResumed() → 读取新 intent + 复用已存储 callback
→ 同上链路 → 回调为前端
依赖的 native 机制(插件内部已实现,无需外部配置):
index.uts中导出AppHookProxy implements UTSAndroidHookProxy- HBuilderX 自动在
Application.onCreate时调用AppHookProxy.onCreate - 在其中
application.registerActivityLifecycleCallbacks(...) - Lifecycle callback 中的
onActivityResumed调用ApkBridge.onActivityResumed()
文件目录约定
| 路径 | 用途 |
|---|---|
filesDir/hl-apk1-imports/ |
通过 importFromUris / onSharedIntent 导入的文件(持久,随应用卸载清除) |
cacheDir/hl-apk1-install-cache/ |
安装临时 .apk 根目录;每次安装写入独立 session 子目录 |
cacheDir/hl-apk1-share-cache/ |
分享临时文件(shareFile 复制共享存储文件时使用) |
权限说明
| 权限 | 用途 | 必须 |
|---|---|---|
REQUEST_INSTALL_PACKAGES |
调起系统安装器 | 是 |
MANAGE_EXTERNAL_STORAGE |
全盘扫描(Android 11+) | 否(无此权限仅扫描私有目录) |
READ_EXTERNAL_STORAGE |
旧版 Android 读文件 | 否 |
WRITE_EXTERNAL_STORAGE |
旧版 Android 写文件 | 否 |

收藏人数:
购买普通授权版(
试用
赞赏(0)
下载 276
赞赏 2
下载 12035159
赞赏 1917
赞赏
京公网安备:11010802035340号