INSTALL_PACKAGES:系统级权限,普通应用无法获取REQUEST_INSTALL_PACKAGES:Android 8.0+ 新增,需要用户授权<!-- AndroidManifest.xml 中已配置 -->
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
适用版本:Android 5.0+ (API 21+)
优点:
缺点:
REQUEST_INSTALL_PACKAGES 权限实现原理:
// 1. 创建 PackageInstaller.Session
PackageInstaller installer = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL
);
int sessionId = installer.createSession(params);
PackageInstaller.Session session = installer.openSession(sessionId);
// 2. 写入 APK 文件
try (OutputStream out = session.openWrite("package", 0, -1)) {
// 将 APK 文件写入流
FileInputStream in = new FileInputStream(apkFile);
byte[] buffer = new byte[65536];
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
}
session.fsync(out);
}
// 3. 提交安装
Intent intent = new Intent(context, InstallReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
);
session.commit(pendingIntent.getIntentSender());
session.close();
适用条件:应用必须是 Device Owner
优点:
缺点:
实现原理:
// 使用 DevicePolicyManager.installPackage()
if (dpm.isDeviceOwnerApp(context.getPackageName())) {
dpm.installPackage(
adminComponent,
Uri.fromFile(apkFile),
new DevicePolicyManager.InstallPackageCallback() {
@Override
public void onPackageInstalled(String packageName, int returnCode, String msg) {
// 安装完成回调
}
}
);
}
注意:此方法在 Android 8.0 中可能已被限制或移除。
适用场景:需要 root 权限或系统签名
缺点:
app-info.vue (下载完成)
↓
KioskModule.installApkSilently() (原生插件)
↓
PackageInstaller API
├─ 检查 Device Owner 权限
├─ 创建安装会话
├─ 写入 APK 文件
├─ 提交安装(带进度回调)
└─ 监听安装结果
↓
安装完成/失败
↓
恢复 Kiosk 模式
InstallModule.javapublic class InstallModule extends UniModule {
@UniJSMethod(uiThread = true)
public void installApkSilently(String apkPath, UniJSCallback callback) {
// 1. 检查 Device Owner
// 2. 使用 PackageInstaller 安装
// 3. 返回安装结果
}
@UniJSMethod(uiThread = true)
public void getInstallProgress(UniJSCallback callback) {
// 返回安装进度
}
}
// 伪代码
public void installApk(String apkPath) {
// 1. 检查权限
if (!checkInstallPermission()) {
requestInstallPermission();
return;
}
// 2. 创建安装会话
PackageInstaller installer = getPackageInstaller();
SessionParams params = new SessionParams(MODE_FULL_INSTALL);
int sessionId = installer.createSession(params);
// 3. 写入 APK
Session session = installer.openSession(sessionId);
writeApkToSession(session, apkPath);
// 4. 提交安装
Intent intent = new Intent(ACTION_INSTALL_COMPLETE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(...);
session.commit(pendingIntent.getIntentSender());
}
| 方案 | 需要退出 Kiosk | 用户交互 | 稳定性 | 实现难度 | 推荐度 |
|---|---|---|---|---|---|
| 当前方案(系统安装器) | ✅ 需要 | ✅ 需要 | ⭐⭐⭐⭐⭐ | ⭐ 简单 | ⭐⭐ |
| PackageInstaller | ❌ 不需要 | ⚠️ 可选 | ⭐⭐⭐⭐ | ⭐⭐⭐ 中等 | ⭐⭐⭐⭐⭐ |
| Device Owner 静默安装 | ❌ 不需要 | ❌ 不需要 | ⭐⭐⭐ | ⭐⭐ 简单 | ⭐⭐⭐⭐ |
| 反射调用 | ❌ 不需要 | ❌ 不需要 | ⭐⭐ | ⭐⭐⭐⭐ 困难 | ⭐ |
REQUEST_INSTALL_PACKAGES 权限
FileProvider 配置
存储权限
WRITE_EXTERNAL_STORAGE 或 MANAGE_EXTERNAL_STORAGEInstallModule 原生插件推荐使用 PackageInstaller API 方案,原因:
实施优先级: