Android 5.0 ~ 14访问Android/data(obb)目录的方法

这篇具有很好参考价值的文章主要介绍了Android 5.0 ~ 14访问Android/data(obb)目录的方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

众所周知,安卓每次出新版本的时候都会收紧权限,存储权限也不例外。虽说官方的意思是为了保护隐私安全,但这些改动着实令开发者和用户感到头疼,尤其是Android/data、Android/obb目录的访问。毕竟用户更难操作,开发者也要费力适配。那么今天就来探索下怎么适配这些变更点吧。

不同安卓版本存储权限差异

1、Android 6.0 之前

应用只需要在 AndroidManifest.xml 下声明以下权限即可自动获取存储权限: 

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2、Android 6.0 起

从Android 6.0开始,除了以上操作以外,还需要在代码中动态申请权限。

// 检查是否有存储权限
boolean granted = context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
    PackageManager.PERMISSION_GRANTED;
// 在activity中请求存储权限
requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);

3、Android 10

Android 10 开始引入了沙盒机制,应用在 sdcard 中默认只能读写私有目录(即/sdcard/Android/data/[应用包名]/),其他目录即便执行前面的操作也无法读写。除非在 AndroidManifest.xml 下声明以下属性:

<application
        ...
        android:requestLegacyExternalStorage="true">

这样的话就会暂时停用沙盒机制,正常读写/sdcard下文件。

4、Android 11

Android 11开始,且应用的目标版本在30及以上,以上的操作也无法再读写sdcard目录。需要声明以下权限:

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

再动态申请权限:

// 检查是否有存储权限
boolean granted = Environment.isExternalStorageManager();
// 在activity中请求存储权限
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
    .setData(Uri.parse("package:"+getPackageName()));
startActivityForResult(intent, 0);

执行以上操作后,sdcard已能够正常读写。

但是,有2个特殊的目录仍然无法读写:

/sdcard/Android/data/sdcard/Android/obb

这两个路径需要安卓自带的 DocumentsUI 授权才能访问。

首先,最重要的一点,添加 documentfile 依赖(SDK自带的那个版本有问题):

implementation "androidx.documentfile:documentfile:1.0.1"

Activity请求授权:

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
                Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    // 请求Android/data目录的权限,Android/obb目录则把data替换成obb即可。
    Uri treeUri = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata/document/primary%3AAndroid%2Fdata");
    DocumentFile df = DocumentFile.fromTreeUri(this, treeUri);
    if (df != null) {
        intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, df.getUri());
    }
}
startActivityForResult(intent, 1);

还需要在回调中保存权限:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Uri uri;
    if (data != null && (uri = data.getData()) != null) {
        // 授权成功
        getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    } else {
        // 授权失败
    }
}

在请求授权时,会跳转到以下界面。点击下方按钮授权即可。

android14解锁data目录,android

android14解锁data目录,android

然后,使用接口获取文件列表:

// Android 11~12转换路径。例如:/sdcard/Android/data,转换成:
// Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata/document/primary%3AAndroid%2Fdata")

// 路径 /sdcard/Android/data/com.xxx.yyy,转换成:
// Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata/document/primary%3AAndroid%2Fdata%2Fcom.xxx.yyy")
// 以此类推。
Uri pathUri = pathToUri(path);
DocumentFile documentFile = DocumentFile.fromTreeUri(context, pathUri);
if (documentFile != null) {
    DocumentFile[] documentFiles = documentFile.listFiles();
    for (DocumentFile df : documentFiles) {
        // 文件名
        String fName = df.getName();
        // 路径
        String fPath = path + "/" + fName;
    }
}

5、Android 13

Android 13 开始,上面提到的授权 Android/data、Android/obb目录的方法失效了。请求授权会出现如下界面:

android14解锁data目录,android

点击下方按钮会提示:

android14解锁data目录,android

说明安卓13无法再直接授权 Android/data 和 Android/obb 这两个目录了。但也并非无计可施。我们可以授权他们的子目录。

我们知道,这个目录下的文件夹名称一般都是应用的包名。我们可以直接用应用包名的路径来请求授权。

这里要注意,Android 13和Android 11~12的路径转换规则不一样。

// Android 13转换路径。例如:/sdcard/Android/data/com.xxx.yyy,转换成:
// Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fcom.xxx.yyy/document/primary%3AAndroid%2Fdata%2Fcom.xxx.yyy")

// 路径 /sdcard/Android/data/com.xxx.yyy/files,转换成:
// Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fcom.xxx.yyy/document/primary%3AAndroid%2Fdata%2Fcom.xxx.yyy%2Ffiles")
// 以此类推。

请求授权时,出现如下界面。照常授权即可。

android14解锁data目录,android

6、Android 14

Android 14对于data、obb目录的授权进一步收紧。在Android 14的后期版本和Android 15预览版中,以上方法已失效(传入uri请求授权只会跳转到sdcard根目录)。这种情况下,想要访问data和obb目录就需要使用Shizuku了。(目前MT、FV就是用这种方法访问的)

Shizuku的用法可以查阅相关教程,这里不多赘述。请先将Shizuku启动,便于后续使用。

首先,添加Shizuku的依赖:

def shizuku_version = "13.1.5"
implementation "dev.rikka.shizuku:api:$shizuku_version"
// Add this line if you want to support Shizuku
implementation "dev.rikka.shizuku:provider:$shizuku_version"

AndroidManifest.xml添加以下内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-sdk tools:overrideLibrary="rikka.shizuku.api, rikka.shizuku.provider, rikka.shizuku.shared, rikka.shizuku.aidl" />
    <uses-permission android:name="moe.shizuku.manager.permission.API_V23" />

    <application>
        <provider
            android:name="rikka.shizuku.ShizukuProvider"
            android:authorities="${applicationId}.shizuku"
            android:enabled="true"
            android:exported="true"
            android:multiprocess="false"
            android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
        <meta-data
            android:name="moe.shizuku.client.V3_SUPPORT"
            android:value="true" />
    </application>
</manifest>

判断Shizuku是否安装:

private static boolean isShizukuInstalled() {
    try {
        context.getPackageManager().getPackageInfo("moe.shizuku.privileged.api", 0);
        return true;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    return false;
}

判断Shizuku是否可用:

boolean available = Shizuku.pingBinder();

检查Shizuku是否授权:

boolean granted = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED;

请求Shizuku权限:

Shizuku.requestPermission(0);

监听授权结果:

Shizuku.addRequestPermissionResultListener(listener);
Shizuku.removeRequestPermissionResultListener(listener);

授权后,自己定义一个aidl文件:

IFileExplorerService.aidl

package com.magicianguo.fileexplorer.userservice;

import com.magicianguo.fileexplorer.bean.BeanFile;

interface IFileExplorerService {
    List<BeanFile> listFiles(String path);
}

BeanFile.java

public class BeanFile implements Parcelable {
    public BeanFile(String name, String path, boolean isDir, boolean isGrantedPath, String pathPackageName) {
        this.name = name;
        this.path = path;
        this.isDir = isDir;
        this.isGrantedPath = isGrantedPath;
        this.pathPackageName = pathPackageName;
    }

    /**
     * 文件名
     */
    public String name;
    /**
     * 文件路径
     */
    public String path;
    /**
     * 是否文件夹
     */
    public boolean isDir;
    /**
     * 是否被Document授权的路径
     */
    public boolean isGrantedPath;
    /**
     * 如果文件夹名称是应用包名,则将包名保存到该字段
     */
    public String pathPackageName;

    protected BeanFile(Parcel in) {
        name = in.readString();
        path = in.readString();
        isDir = in.readByte() != 0;
        isGrantedPath = in.readByte() != 0;
        pathPackageName = in.readString();
    }

    public static final Creator<BeanFile> CREATOR = new Creator<BeanFile>() {
        @Override
        public BeanFile createFromParcel(Parcel in) {
            return new BeanFile(in);
        }

        @Override
        public BeanFile[] newArray(int size) {
            return new BeanFile[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeString(path);
        dest.writeByte((byte) (isDir ? 1 : 0));
        dest.writeByte((byte) (isGrantedPath ? 1 : 0));
        dest.writeString(pathPackageName);
    }
}

IFileExplorerService实现类:

public class FileExplorerService extends IFileExplorerService.Stub {

    @Override
    public List<BeanFile> listFiles(String path) throws RemoteException {
        List<BeanFile> list = new ArrayList<>();
        File[] files = new File(path).listFiles();
        if (files != null) {
            for (File f : files) {
                list.add(new BeanFile(f.getName(), f.getPath(), f.isDirectory(), false, f.getName()));
            }
        }
        return list;
    }
}

然后使用Shizuku绑定UserService:

private static final Shizuku.UserServiceArgs USER_SERVICE_ARGS = new Shizuku.UserServiceArgs(
    new ComponentName(packageName, FileExplorerService.class.getName())
).daemon(false).debuggable(BuildConfig.DEBUG).processNameSuffix("file_explorer_service").version(1);

public static IFileExplorerService iFileExplorerService;

private static final ServiceConnection SERVICE_CONNECTION = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(TAG, "onServiceConnected: ");
        iFileExplorerService = IFileExplorerService.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(TAG, "onServiceDisconnected: ");
        iFileExplorerService = null;
    }
};

// 绑定服务
public static void bindService() {
    Shizuku.bindUserService(USER_SERVICE_ARGS, SERVICE_CONNECTION);
}

绑定之后,调用 aidl 里面的方法来管理文件即可。

源代码:GitHub - MagicianGuo/Android-FileExplorerDemo: 能够访问Android/data(obb)目录,已适配Android 5.0 ~ 14。安卓高版本可以使用Shizuku授权。文章来源地址https://www.toymoban.com/news/detail-842940.html

到了这里,关于Android 5.0 ~ 14访问Android/data(obb)目录的方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android 11 访问 Android/data/或者getExternalCacheDir() root方式

    前言 : 需求要求安装三方应用ExternalCacheDir()下载下来的apk文件。 getExternalCacheDir() : /storage/emulated/0/Android/data/com. . /cache/ 获取访问权限 如果手机安卓版本为Android10的时候,可以在AndroidManifest.xml中添加下列代码 以此禁用分区存储,但这在Android11及以上版本不起作用。 root方式 (

    2024年02月21日
    浏览(37)
  • Android免Root执行脚本,无Root可以修改权限的目录 :/data/local/tmp

    在Android中,访问data目录是需要root权限,但是有个例外那就是/data/local/tmp目录,这个目录通过使用ADB来打开就会拥有读写权限! 注意: (1)这个目录不能一级一级的打开,需要通过cd  /data/local/tmp 一次性进入 (2)该目录下的文件属性,权限都是可以修改的,例如chmod 777 /d

    2024年02月09日
    浏览(34)
  • 关于安卓13中Android/data目录下的文件夹只能查看无法进行删改的问题

    因为升级了安卓13,然后有个app需要恢复数据,打算和以前一样直接删除Android/data下对应目录再添加,结果不行,以下是结合网上以及自己手机情况来做的一种解决方案。 准备: 待恢复app(包名com.test.ai) 其他app(包名com.other.ai,这个app当做临时变量就行,随便任意app,且知

    2024年02月09日
    浏览(66)
  • 使用 adb 命令修改 Android/data 目录下的文件(给碧蓝档案国服吃布丁)

    本文记录如何使用 adb 命令修改 Android/data 目录下的文件,然后给国服的碧蓝档案打上布丁🍮~ 今天下午刷着刷着微博就看到国服 BA 又又又发了和谐公告 ... 心情复杂。jpg 于是乎终于想起来得吃布丁🍮了,至于此次更新后布丁有没有用还未知,但还是先搞上 食用方法之前就出

    2024年03月24日
    浏览(45)
  • 手机解锁方法:8个顶级的 Android 手机解锁软件

    一般来说,太简单的密码是不安全的,所以我们设置一个安全的密码,可能会稍微复杂一点。然而,我们可能经常会忘记复杂的密码并锁定我们的 Android 智能手机。 如果您遇到过这种情况并且正在寻找一种有效的方法来解锁您的 Android 设备而不丢失数据,您可以看看这篇介绍

    2024年02月03日
    浏览(35)
  • 从 PC 解锁 Android 手机的 6 种有效方法

    在这个数字时代,手机已成为我们生活的重要组成部分。我们将它们用于各种用途,从跟踪我们的工作和社交日程到与亲人交流。 然而,有时我们的手机会成为令人沮丧的源头,尤其是当我们不小心将自己拒之门外时。但是您知道可以使用计算机解锁您的 Android 手机吗?您可

    2024年02月03日
    浏览(30)
  • Android 14新特性,选择性照片和视频访问授权

    本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新。 今天这篇文章给大家介绍一下Android 14系统中的一个新特性,对部分照片和视频进行访问授权,也可以称之为选择性照片和视频访问授权。 这是Android系统在隐

    2024年04月17日
    浏览(34)
  • Android 访问存储卡的三种主要的目录

    Android 访问存储卡(即外部存储)通常涉及以下三种主要的目录: 1. 外部存储公共目录 (Public External Storage) 这些目录对所有应用都是可见的,并且不需要任何特殊权限来读取媒体文件。但是从Android 10(API 级别 29)开始,如果要写入这些目录,则需要 WRITE_EXTERNAL_STORAGE 权限,

    2024年01月24日
    浏览(52)
  • [5 种有效方法] 适用于 Android 的通用解锁图案/密码

    在当今世界,保护您的密码对于您的文件和数据的安全至关重要,尤其是在第三方应用程序盛行的情况下。为这些应用程序注册帐户不是问题,就像记住它们一样。但是,如果您不知何故忘记了手机密码,您仍然可以在不丢失宝贵数据的情况下找到解决方法。 一些工具在互联

    2024年02月11日
    浏览(36)
  • 潇洒郎: 小白一次性成功——红米 Note 12 5G Android12 系统13.0.16/14.0.9 小米红米手机解BL锁+ROOT-刷面具—官方ROM下载-线刷降级—解锁system系统分区

    下载工具 申请解锁小米手机 (miui.com) 驱动安装进入Fastboot模式后,会自动识别已连接, 否则显示未连接

    2024年01月25日
    浏览(101)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包