什么是xapk文件?

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版,欢迎购买。点击进入详情

1. xapk种类

有时候我们会遇到.xapk后缀的Android安装包,而且一般移动设备还不能直接安装,需要专门的xapk安装器来安装。
那什么是xapk文件呢?
xapk有两种形式:
1) XAPK file = APK file + OBB data file
2) XAPK file = Base APK file + Split APKs file, The base and split configuration APKs contained in this APK bundle

2. xapk with obb

第一种包含apk和obb数据包,apk好理解,就是我们平时打包出来的apk, 而obb包是什么呢?

OBB全称:Android Opaque Binary Blob File 格式,可以理解为它是一种数据包格式。
其产生的背景是Google Play要求上传的apk必须在100M以下(比如游戏应用肯定不止100M),大于100M的话必须将apk拆分为apk(小于100M)和obb两部分,而且Google还要求每个obb文件不能超过2G,最大支持2个obb文件,也就是说Google Play上的应用最大不能超过4.1G。
将两种类型的文件一起上传至Google Play。用户在下载应用时会将两者同时一起下载下来。但有个问题是扩展文件Google并不保证一定会下载下来,而且有可能下载下来的扩展文件被误删除,所以我们自身的App还需要有个完整性验证机制。

如果没有分包工具的话,那么分包机制需要我们自己去实现。简单来说,我们一般都是将资源文件保存在assets文件夹中,那么我们可以将assets中的资源文件打包成扩展文件,其它部分就作为apk,这里Google也提供了一种打包方法供大家参考:
https://developer.android.com/studio/command-line/jobb.html

如果将obb和apk不分开,而是打包在一起,这样就形成了我们所说的xapk文件,好处就是直接下载下来就可以用了,而且不受限于Google Play平台的要求。

3. xapk with Split Apks

这种类型的xapk可以理解为多个apk的组合,把.xapk后缀改为.zip,然后解压,就可以看到里面的内容了。
比如:
在这里插入图片描述
然后在manifest.json文件中可以看到配置信息:
在这里插入图片描述

4. 如何制作xapk

https://www.ldplayer.net/blog/How-to-form-a-xapk-file.html

欢迎关注我的技术公众号:国民程序员,我们的目标:输出干货

  1. 每天分享原创技术文章
  2. 海量免费技术资料和视频学习资源
  3. 分享赚钱门道,带领程序员走向财务自由
图片名称
<think>我们正在讨论的是在Android 13上如何绕过Scoped Storage限制来访问特定目录的APK文件。 根据引用[1]和[4],我们知道Scoped Storage政策限制了应用对外部存储的随意访问。在Android 10及更高版本中,应用只能直接访问自己的私有目录和特定类型的媒体文件(通过MediaStore)。对于其他目录,特别是非媒体文件(如APK),访问受到限制。 但是,我们可以通过以下方法来实现访问特定目录的APK文件: 1. 使用MediaStore API访问非媒体文件(APK) 从Android 10开始,MediaStore允许访问非媒体文件,但需要用户通过系统选择器选择文件(即使用Storage Access Framework)。然而,如果我们知道文件的具体路径,我们可以尝试通过MediaStore API获取文件的Uri。 2. 使用SAF(Storage Access Framework)让用户选择文件 这是官方推荐的方式,通过Intent启动文件选择器,用户选择文件后返回Uri,然后我们可以使用该Uri读取文件。 3. 使用FileProvider(适用于应用间共享文件) 如果我们自己的应用将APK文件放在私有目录(如getExternalFilesDir()下的目录),那么我们可以使用FileProvider来生成一个content:// Uri,然后用于安装。 4. 申请MANAGE_EXTERNAL_STORAGE权限(不推荐,但可用于特定情况) 对于需要管理设备上所有文件的特定应用(如文件管理器),可以申请MANAGE_EXTERNAL_STORAGE权限(即所有文件访问权限)。但此权限需要用户手动在设置中授予,且Google Play对使用此权限的应用有严格审核。 下面我们详细说明每种方法: 方法1:使用MediaStore API查询APK文件(需要用户交互) 步骤: a. 使用MediaStore.Files获取APK文件。 b. 查询条件:MIME_TYPE = "application/vnd.android.package-archive" c. 注意:从Android 11开始,应用必须使用SAF来访问其他应用的下载目录(如Download目录)中的文件,除非应用是创建该文件的应用。 方法2:使用SAF选择文件(需要用户交互) 这是最通用的方法,代码如下: ```kotlin val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "application/vnd.android.package-archive" // 只显示APK文件 } startActivityForResult(intent, REQUEST_CODE_APK) ``` 在onActivityResult中,获取返回的Uri,然后使用该Uri读取文件。 方法3:使用FileProvider(适用于应用私有目录下的APK文件) 如果APK文件位于应用的私有目录(如Context.getExternalFilesDir()或Context.getFilesDir()),则可以直接访问,并且可以通过FileProvider分享给其他应用(如安装程序)。 步骤: a. 在AndroidManifest.xml中声明FileProvider(如引用[4]所示)。 b. 创建file_paths.xml定义可分享的目录。 c. 使用FileProvider.getUriForFile()获取Uri,然后启动安装Intent。 方法4:申请MANAGE_EXTERNAL_STORAGE权限(无需用户交互,但需要用户手动授权) 步骤: a. 在AndroidManifest.xml中声明权限: ```xml <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> ``` b. 请求用户授权: ```kotlin if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:${context.packageName}") startActivity(intent) } } ``` c. 一旦用户授权,就可以通过File API访问任何文件路径,包括直接使用路径访问APK文件。 注意:方法4在Google Play上架时,需要声明此权限仅用于特定的合法用途,否则可能会被拒绝。 针对静默安装的需求,我们通常需要直接访问APK文件路径。如果使用方法4,在获得管理权限后,我们可以直接通过路径读取APK文件。但是,静默安装仍然需要系统权限(如之前讨论的)。 总结:在Android 13上访问特定目录的APK文件,最直接的方法是申请MANAGE_EXTERNAL_STORAGE权限,但要注意使用此权限的审核问题。如果不想申请此权限,则需要通过SAF让用户选择文件,或者将APK文件放在应用私有目录下(无需权限即可访问)。 下面我们重点讨论如何通过MANAGE_EXTERNAL_STORAGE权限绕过Scoped Storage限制: 步骤: 1. 检查是否已经获得MANAGE_EXTERNAL_STORAGE权限: ```kotlin if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Environment.isExternalStorageManager()) { // 有权限,可以直接访问任何文件 } else { // 请求权限 val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:${context.packageName}") startActivity(intent) } } else { // 低于Android 11,使用旧的存储权限 } ``` 2. 获得权限后,就可以使用File对象访问任何路径,例如: ```kotlin val apkFile = File("/sdcard/Download/myapp.apk") if (apkFile.exists()) { // 读取文件 } ``` 3. 注意:即使获得了MANAGE_EXTERNAL_STORAGE权限,静默安装仍然需要系统权限(如INSTALL_PACKAGES)或root权限。 因此,我们可以将访问APK文件和安装APK文件分开处理: - 访问APK文件:通过MANAGE_EXTERNAL_STORAGE权限实现。 - 安装APK文件:通过系统安装程序(需要用户交互)或者通过系统权限/root权限实现静默安装。 如果我们的应用是系统应用,那么我们可以直接使用静默安装(如之前回答中的反射方法)。否则,我们只能通过启动系统安装程序来安装。 启动系统安装程序的代码(使用FileProvider生成Uri): ```kotlin fun installApk(context: Context, apkFile: File) { val intent = Intent(Intent.ACTION_VIEW) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 使用FileProvider FileProvider.getUriForFile(context, "${context.packageName}.fileProvider", apkFile) } else { Uri.fromFile(apkFile) } intent.setDataAndType(uri, "application/vnd.android.package-archive") context.startActivity(intent) } ``` 但是,如果APK文件位于外部存储的公共目录(如下载目录),并且我们没有MANAGE_EXTERNAL_STORAGE权限,那么在Android 11及以上版本中,我们无法直接通过路径访问该文件。此时,我们必须通过SAF选择文件,或者使用MediaStore API来访问(如果该文件是由我们的应用创建的,则可以直接访问)。 因此,为了绕过Scoped Storage限制访问特定目录的APK文件,我们有两个主要选择: - 使用SAF(需要用户交互,但不需要额外权限)。 - 申请MANAGE_EXTERNAL_STORAGE权限(需要用户手动授权,但授权后可以无交互访问)。 根据应用的具体需求选择合适的方法。 最后,生成相关问题。</think>### Android 13 绕过 Scoped Storage 限制访问特定目录 APK 文件的方法 在 Android 13 上访问特定目录的 APK 文件需要处理 Scoped Storage 限制,以下是四种实用方法: #### 方法 1:使用 MediaStore API(推荐) ```kotlin // 查询 Download 目录下的 APK 文件 fun queryApkFiles(context: Context) { val projection = arrayOf( MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME ) val selection = "${MediaStore.Files.FileColumns.MIME_TYPE} = ?" val selectionArgs = arrayOf("application/vnd.android.package-archive") context.contentResolver.query( MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, null )?.use { cursor -> while (cursor.moveToNext()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)) val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)) val uri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id) // 获取实际文件路径(Android 10+ 返回 content:// URI) val filePath = getFilePathFromUri(context, uri) } } } // 将 content:// URI 转换为实际路径 fun getFilePathFromUri(context: Context, uri: Uri): String? { val file = File(uri.path?.substringAfterLast("/") ?: return null) return "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOAD)}/${file.name}" } ``` **优点**:符合 Android 规范,无需特殊权限 **限制**:只能访问公共目录(Download/Pictures等)[^1] --- #### 方法 2:申请 MANAGE_EXTERNAL_STORAGE 权限 1. 在 `AndroidManifest.xml` 添加: ```xml <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> ``` 2. 检查并请求权限: ```kotlin if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply { data = Uri.parse("package:${context.packageName}") } startActivity(intent) } else { // 已获得权限,可直接访问任意路径 val apkFile = File("/sdcard/CustomFolder/app.apk") } } ``` **注意**:需用户手动授权,Google Play 对使用此权限的应用有严格审核[^2] --- #### 方法 3:使用 Storage Access Framework (SAF) ```kotlin // 启动文件选择器 val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { putExtra(DocumentsContract.EXTRA_INITIAL_URI, Uri.parse("/sdcard/CustomFolder")) } startActivityForResult(intent, REQUEST_CODE) // 处理返回结果 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) { val treeUri = data?.data val docUri = DocumentsContract.buildDocumentUriUsingTree( treeUri, DocumentsContract.getTreeDocumentId(treeUri) ) // 获取持久访问权限 contentResolver.takePersistableUriPermission( docUri, Intent.FLAG_GRANT_READ_URI_PERMISSION ) } } ``` **优点**:用户可选择特定目录,访问权限持久化 **缺点**:需要用户交互[^4] --- #### 方法 4:使用 FileProvider 共享文件 1. 在 `res/xml/file_paths.xml` 配置路径: ```xml <paths> <external-path name="custom_folder" path="CustomFolder/" /> </paths> ``` 2. AndroidManifest 声明 FileProvider: ```xml <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> ``` 3. 获取文件 URI: ```kotlin val apkFile = File(Environment.getExternalStorageDirectory(), "CustomFolder/app.apk") val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", apkFile) ``` **适用场景**:应用间共享文件,需配合 `Intent.FLAG_GRANT_READ_URI_PERMISSION`[^4] --- ### 关键注意事项 1. **权限声明**: ```xml <!-- 基础存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Android 11+ 需要额外声明 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> ``` 2. **文件路径处理**: - 避免硬编码路径,使用 `Environment.getExternalStoragePublicDirectory()` - 私有文件应存储在 `getExternalFilesDir()` 目录 3. **Android 13 新增权限**: ```xml <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> ``` 4. **降级策略**: ```kotlin if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // 使用 MediaStore 或 SAF } else { // 使用传统 File API + READ_EXTERNAL_STORAGE } ``` --- ### 相关问题 1. 如何在 Android 13 上实现无用户交互的 APK 文件访问? 2. 使用 MANAGE_EXTERNAL_STORAGE 权限的应用如何通过 Google Play 审核? 3. 如何将传统 File API 代码迁移到 Scoped Storage 兼容方案? 4. 在 Android 13 中,哪些目录不需要特殊权限即可直接访问? [^1]: Scoped Storage 政策限制了应用对外部存储的直接访问权限,需要采用新的 API 访问公共目录。 [^2]: MANAGE_EXTERNAL_STORAGE 属于危险权限,需要用户手动授权且 Google Play 有严格审核。 [^4]: FileProvider 通过 content:// URI 安全共享文件,需在 manifest 配置访问路径。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值