Android_TRY
Android_TRY
  • 发布:2019-07-26 17:43
  • 更新:2024-12-04 17:32
  • 阅读:406427

适配Android10+设备注意事项

分类:uni-app

名词介绍: Android 10+ 表示android系统版本大于等于10 既包括android 10以及android 11的系统统称

Android 10+ 概述

Android 10系统开始 进一步增强了平台功能,为外部存储设备上的应用和用户数据提供了更好的保护。作为这项工作的一部分,平台引入了进一步的改进,以简化向分区存储的转换。
为了让用户更好地控制自己的文件,保护用户隐私数据,并限制文件混乱情况,Android 11在分区存储基础上限制了应用访问其他应用的文件。

分区存储

先说一下为什么会有分区存储这个机制出现。

在分区存储之前,某些应用中,即使功能很简单,大部分都不需要这么宽泛的权限。
这就使得某些应用程序

1、乱占空间 :各种各样的文件散布在磁盘的各个地方,当用户卸载应用之后,这些被遗弃的文件被滞留在原地,无人管理,占用了磁盘空间,最终结果就会导致磁盘不足
2、随意读取用户的数据
3、随意读取应用的数据

因此分区存储诞生了,限制了过于宽泛的 存储权限。

什么是分区存储?

Google针对外部存储引入了一个新特性,它的名字叫:Scoped Storage,Google官方对它的翻译为分区存储,我们也可以把它叫做沙盒存储。
更明确了空间的归属,也就是对目录有一个更明确的所有权结构。

分区存储是一种安全机制,用于防止应用读取其他应用的数据。

  • 每个应用程序都有自己的存储空间。
  • 应用程序不能翻过自己的目录,去访问公共目录。
  • 应用程序请求的数据都要通过权限检测,不符合要求不会被放行。

分区存储机制下uni-app/5+ 开发者的影响

android 9及以下系统未做分区存储,除其他应用的内部存储空间不可以读写,其他任意存储目录下的资源文件都可以正常读写操作。
android 10仅对targetSdkVersion>=29则会开启分区存储。targetSdkVersion小于29则不会有任何限制与android9及以下同理。
andorid 11强制执行分区存储。不允许应用读写操作非应用沙盒目录和系统公共目录下的资源文件。

dcloud已对分区存储机制做了适配工作。但也增加了开发者对文件目录操作的规则。在分区存储的环境下分出两个可操文件数据目录系统公共目录应用沙盒目录.

系统公共目录:Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones等

  • 公共目录的文件在App卸载后,不会删除
  • 通过plus.gallery.pick获取
  • 拥有权限,也能通过路径直接访问

公共目录对照表:

  • Downloads 对应的绝对路径 file:///storage/emulated/0/Download
  • Documents 对应的绝对路径 file:///storage/emulated/0/Documents
  • Pictures 对应的绝对路径 file:///storage/emulated/0/Pictures
  • DCIM 对应的绝对路径 file:///storage/emulated/0/DCIM
  • Movies 对应的绝对路径 file:///storage/emulated/0/Movies
  • Music 对应的绝对路径 file:///storage/emulated/0/Music
  • Ringtones 对应的绝对路径 file:///storage/emulated/0/Ringtones

系统公共目录缺陷:

  • 系统公共目录仅支持读取媒体文件 如:音频文件、视频文件、图片文件。其他类型文件不支持!!!!
  • 系统公共目录下创建的文件是公用的,你需要确保你的文件命名是惟一的。否则会出现名称对应不上的问题。多数重命名会文件名尾部(i++)处理
  • 系统公共目录不可随意删减。该文件谁创建的谁才有权限删除修改。如果不是当前应用创建的文件是无权权限删改的。
  • 系统公共目录删减文件会被系统认为恶意操作。将会弹通知栏。告诫手机用户当前XXX应用删除了什么文件。

系统公共目录地址的获取来源是哪里:

这里多数是通过plus.gallery.pick获取到的媒体文件。如图片视频等。多数是DCIM、Pictures、Movies等。其他Ringtones、Documents仅提供绝对路径让开发者对比,实际业务中可能并不会获取到相关路径目录的。

目前业务逻辑会用到系统公共目录多数为分享。需要将图片等资源拷贝到系统公共目录下。别的三方应用才有权读取该文件。进行操作业务逻辑。

应用沙盒目录(应用专属目录)

  • 应用沙盒目录只能自己直接访问
  • App卸载,数据会清除。

应用沙盒目录分为:

  • 内部存储空间目录

    • plus.io.PRIVATE_WWW 对应绝对路径 file:///data/user/0/%PACKAGENAME%/files/apps/%APPID%/www

    • plus.io.PRIVATE_DOC 对应绝对路径 file:///data/user/0/%PACKAGENAME%/files/apps/%APPID%/doc

  • 外部存储空间目录

    • plus.io.PUBLIC_DOCUMENTS 对应绝对路径 file:///storage/emulated/0/Android/data/%PACKAGENAME%/.%APPID%/documents

    • plus.io.PUBLIC_DOWNLOADS 对应绝对路径 file:///storage/emulated/0/Android/data/%PACKAGENAME%/.%APPID%/downloads

具体请参考文档

以下是针对分区存储机制作出文件操作目录限制的5+API:

以上API设置了不支持的目录部分会返回code =15的错误信息!,部分会返回执行失败的错误回调具体以API为准!

建议

可预见的未来android系统的更新将会对存储数据操作更加严格。推荐广大开发者文件数据操作都在应用本身的沙盒目录下完成。减少未来不必要的适配工作。相对的你的应用也会更加安全可靠。

扩展问题:

Q:分区存储 我的应用那些需要改动?

A: 目前应用多数与文件操作数据都已适配。用户只需关注部分API操作的文件路径是否符合目录限制规则。非应用沙盒目录或非系统公共目录请及时修改或增加判断调整业务逻辑。

Q:我是不是只要targetSdkVersion不设置>=29就可以了?

A: 不是在android11的设备已经强制开启分区存储机制。您必须做出适配。否则在android11的设备上无法正常运行。

Q:我是不是可以将所以之前应用的特殊文件都放入到系统公共目录下不就行了。之后也可以随意操作不需要非要在应用沙盒目录操作文件呀。

A: 首先不推荐这样操作的。这样会导致功系统公共目录下的文件激增导致多应用做操出现的文件名称冲突等问题。`系统公共目录还有个明显缺陷就是不可以随意删除、修改。都会被系统判断为恶意删减或无权限修改。造成业务逻辑错误。

设备位置

推出新的用户选项;只有在前台使用您的应用时,该选项才允许访问设备位置信息。

Android 10开始中如果应用需要在后台时也获得用户位置,则需要申请后台定位权限,uni-app/5+应用会默认申请该权限,其表现为:
targetSdkVersion>=29,用户同意定位权限后,再次弹窗申请后台定位权限。
targetSdkVersion<29,申请定位权限弹窗中会增加“始终允许”选项。

后台应用启动

针对在没有用户互动的情况下从后台启动活动的新限制。

硬件标识符

对设备硬件标识符(如 IMEI、序列号、MAC 和类似数据)访问权限的新限制。
为保护用户隐私,Android 10开始将不会开放众多可以确定设备的唯一标识。因此Android 10+设备中plus.device.getInfo返回的imei、uuid将会为空。

摄像头和连接性

针对完整摄像头元数据的访问权限实施了新限制,并且现在许多连接工作流都需要精确位置权限。

Android 10+新特性官方说明:https://developer.android.google.cn/preview/privacy

5 关注 分享
最光阴 vpanda HRK_01 retion_p 是空空呀

要回复文章请先登录注册

c***@126.com

c***@126.com

回复 1***@qq.com :
你好,麻烦问一下这个问题您后面是怎么解决的呢
2024-12-04 17:32
e***@126.com

e***@126.com

回复 e***@126.com :
这个主要解决用5+ vue离线打包成app后,用plus.gallery.pick函数选择相册上传的时候使用FileReader时报错的问题,{"type": "loadend", "bubbles": false, "cancelBubble": false, "cancelable": false, "lengthComputable": false, "loaded": 0, "total": 0, "target": {"fileName": "/storage/emulated/0/Pictures/JPEG_20241024_215229.jpg", "readyState": 2, "result": null, "error": {"code": 15, "message": "targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径。请更改为应用运行路径!具体请看:https://ask.dcloud.net.cn/article/36199"}, "onloadstart": null, "onprogress": null, "onload": null, "onabort": null, "onerror": null, "onloadend": function()}}
2024-10-31 13:30
e***@126.com

e***@126.com

这个问题本身很好解决,在android层面直接读数据返回就可以了,但是dcloud加各种限制,所有才有这样的问题
简单说就是在java里面加一个读取文件为dataURL数据的类,然后在vue中用plus.android.importClass导入这个类执行就可以了,什么问题都没有,具体如下
vue中增加一个函数包装
async imageToDataURL(imagePath) {
return new Promise((resolve, reject) => {
var ImageConverter = plus.android.importClass('com.funlink.likecarry.ImageConverter');

// 调用 imageToDataURL 方法
console.log('imagePath',imagePath)
var data = ImageConverter.imageToDataURL(imagePath)
console.log('data-size: ', data.length)
resolve(data)
});
}

在android的java目录下,创建一个类文件,假设是src/java/com/funlink/likecarry/ImageConverter.java
package com.funlink.likecarry;

import android.util.Base64;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class ImageConverter {

public static String imageToDataURL(String imagePath){
FileInputStream inputStream = null;
ByteArrayOutputStream outputStream = null;

// 如果路径以 file:/// 开头,转换为文件路径
if (imagePath.startsWith("file:///")) {
imagePath = imagePath.replace("file://", "");
}

try {
// 检查文件是否存在
File file = new File(imagePath);
if (!file.exists()) {
return "error,file_not_found";
}

inputStream = new FileInputStream(file);
outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[102400]; // 创建缓冲区
int bytesRead;

// 读取文件数据
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
Log.i("ImageConverter", "output-size=" + outputStream.size());

byte[] byteArray = outputStream.toByteArray();
if (byteArray.length > 0) {
String mimeType = file.getName().toLowerCase().endsWith(".png") ?
"data:image/png;base64," : "data:image/jpeg;base64,";

String dataURL = mimeType + Base64.encodeToString(byteArray, Base64.NO_WRAP);
Log.i("ImageConverter","dataURL-size=" + dataURL.length());
return dataURL; // 返回结果
} else {
return "error,byteArray_is_empty";
}
} catch (IOException e) {
Log.e("ImageConverter", "Error reading image: " + e.getMessage(), e);
return "error,error_reading_image";
} finally {
// 关闭输入输出流
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
Log.e("ImageConverter", "Error closing streams: " + e.getMessage(), e);
}
}
}
}

然后调用的地方
chooseImages() {
if (window.plus) {
window.plus.android.requestPermissions(['android.permission.READ_MEDIA_IMAGES'], (permission) => {
if (permission.granted.length > 0) {
// 使用 H5+ API 打开图库选择器
window.plus.gallery.pick((file) => {
this.imageToDataURL(file).then(result => {
console.log('result: ', result.length)
if (result != null) {
// result就是图像的dataURL数据,可以这里做显示或上传处理
}
})
}
}, (error) => {
console.error('选择图片失败: ' + error.message)
},
{
filter: '.png,.jpg,.jpeg', // 允许的文件类型
multiple: false, // 是否允许多选
system: false // 使用系统相册
})
} else {
console.log('没有获取到权限: ', permission)
}
})
} else {
// 兼容浏览器环境
this.$refs.images.click()
}
},

为了这个问题,折腾了一天,总算圆满解决,速度也很快,希望能够帮到有需要的人
2024-10-31 13:25
t***@163.com

t***@163.com

回复 1***@qq.com :
直接使用uni.savafile转为_doc/格式的文件就可以绕过这个限制了
2024-10-26 10:54
t***@163.com

t***@163.com

回复 LeeSir :
直接使用uni.savafile转为_doc/格式的文件就可以绕过这个限制了,不用谢哈哈哈哈
2024-10-26 10:53
LeeSir

LeeSir

关于如何实现将下载的文件放在file:///storage/emulated/0/Documents/ 安卓手机的公共目录下,可参考
https://segmentfault.com/q/1010000044977913/a-1020000045355121
2024-10-11 17:04
1***@qq.com

1***@qq.com

回复 1***@qq.com :
我也想问这个问题,该怎么解决啊
2024-08-30 09:53
1***@qq.com

1***@qq.com

回复 adxpcc :
现在好了吗 要是没好联系我
2024-06-23 15:29
1***@qq.com

1***@qq.com

为什么删除file:///storage/emulated/0/Download/shuili/document/9999999-hello-723162920011173888-hello-1eeccebfdf0642ffaec8dfd1d9b0318a.pdf这个文件 提示targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径。请更改为应用运行路径!具体请看:https://ask.dcloud.net.cn/article/36199 可是file:///storage/emulated/0/Download这个路径不是可以访问的吗
2024-06-23 15:28
justR

justR

回复 justR :
图方便的,可以把targetsdk设置为28,然后android10+的系统申请“所有文件访问权限”就可以保证都能访问外部文件。但是如果你要上架应用市场,targetsdk可能会有要求,这个时候要么修改你的存储方式到沙盒目录,否则就只有和我一样hook掉相关的api用原生插件来解决。
2024-06-18 11:06