如何在React Native Expo应用内安装下载的.apk文件?
我需要在Expo应用内实现更新功能,安装下载的.apk文件。以下是我的代码:
import React from "react"; import { Button, View } from "react-native"; import * as FileSystem from "expo-file-system"; import { startActivityAsync } from "expo-intent-launcher"; export function Updater() { async function updateApk() { const uri = "https://expo.dev/artifacts/eas/my_apk_name.apk"; const localUri = FileSystem.documentDirectory + "test.apk"; try { await FileSystem.downloadAsync(uri, localUri); await startActivityAsync("android.intent.action.INSTALL_PACKAGE", { data: localUri, flags: 1, }); } catch (error) { alert(`Error during installing APK: ${error}`); } } return ( <View> <Button title="Reset APK" onPress={updateApk} /> </View> ); }
文件可成功下载并存储,但调用startActivityAsync时出现错误:
Encountered an exception while calling native method: Exception occurred while executing exported method startActivity on module ExpoIntentLauncher: file://data/user/0/com.my.app.id/files/test.apk exposed beyond app through Intent.getData()
尝试先将uri传入FileSystem.getContentUriAsync(),此时无错误,但Intent返回结果为0且无任何反应。app.json中的权限配置如下:
"permissions": [ "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE", "CAMERA" ]
疑问:是否需要额外权限?或是Expo无法实现该功能?是否需将文件保存到其他路径?试过android.intent.action.VIEW同样无效。测试环境为Android 13物理设备,应用由EAS构建。
补充必要权限:Android 13及以上版本,安装未知来源APK需要
REQUEST_INSTALL_PACKAGES权限,先在app.json里添加:"permissions": [ "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE", "CAMERA", "REQUEST_INSTALL_PACKAGES" ]然后在代码中动态请求这个权限,避免被系统拦截:
import * as Permissions from 'expo-permissions'; async function requestInstallPermission() { const { status } = await Permissions.askAsync(Permissions.REQUEST_INSTALL_PACKAGES); if (status !== 'granted') { alert('需要开启「安装未知应用」权限才能完成更新'); return false; } return true; }在
updateApk函数开头先调用这个权限请求,确认授权后再继续执行下载和安装流程。修正Intent配置与Uri处理:
直接用file://格式的Uri会触发Android的文件暴露限制,必须用getContentUriAsync生成系统认可的内容Uri,同时要使用正确的Action和Flags:async function updateApk() { const hasPermission = await requestInstallPermission(); if (!hasPermission) return; const uri = "https://expo.dev/artifacts/eas/my_apk_name.apk"; const localUri = FileSystem.documentDirectory + "test.apk"; try { await FileSystem.downloadAsync(uri, localUri); // 生成可被系统访问的内容Uri const contentUri = await FileSystem.getContentUriAsync(localUri); await startActivityAsync("android.intent.action.VIEW", { data: contentUri, type: 'application/vnd.android.package-archive', // 指定APK文件类型 flags: 0x00000001 | 0x00000040, // 授予读取权限 + 新任务启动安装界面 }); } catch (error) { alert(`Error during installing APK: ${error}`); } }注意:
android.intent.action.INSTALL_PACKAGE在Android 8.0+已被弃用,改用VIEW配合type参数就能触发系统的安装流程;Flags里的FLAG_GRANT_READ_URI_PERMISSION能让安装程序获取文件读取权限,FLAG_ACTIVITY_NEW_TASK确保安装界面在独立任务中打开,避免返回异常。路径选择说明:
FileSystem.documentDirectory是应用私有目录,Android 13对其访问限制严格,但通过getContentUriAsync生成的Uri已经可以被系统安装程序正常访问,不需要更换存储路径。如果非要使用外部存储,可选择FileSystem.externalDirectory,但需要额外的存储权限,反而不如私有目录便捷。EAS构建注意事项:
确保EAS构建时app.json的权限配置已正确同步到AndroidManifest.xml,无需手动修改原生代码,重新构建即可生效。
内容的提问来源于stack exchange,提问作者Episodex

