问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

android系统怎样设置内部存储的访问权限

发布网友 发布时间:2022-04-11 19:11

我来回答

2个回答

懂视网 时间:2022-04-11 23:32

在了解 storage access framework 之前,我们先来看看 android4.4 中的一个特性。如果我们希望能选择 android 手机中的一张图片,通常都是发送一个 Intent 给相应的程序,一般这个程序是系统自带的图库应用(如果你的手机中有两个图库类的 app 很可能会叫你

在了解storage access framework之前,我们先来看看android4.4中的一个特性。如果我们希望能选择android手机中的一张图片,通常都是发送一个Intent给相应的程序,一般这个程序是系统自带的图库应用(如果你的手机中有两个图库类的app很可能会叫你选择一个),这个Intent一般是这样写的:

Intent intent=new Intent(Intent.ACTION_GET_CONTENT);//ACTION_OPEN_DOCUMENT

intent.addCategory(Intent.CATEGORY_OPENABLE);

intent.setType("image/jpeg");

使用这样的一种方法来选择图片在android4.4中会直接弹出一个很漂亮的界面,有点像一个文件管理器,其实他比文件管理器更强大,他是一个内容提供器,可以按照目录一层一层的选择文件,也可以按照文件种类选择文件,比如图片、视频、音频等,还可以打开一个应用程序选择文件,界面如下:

--

--

其实这是一个叫做documentsui的内置程序,因为它的manifest没有带LAUNCHER的activity所以不会显示在桌面上。

下面是正文:

Storage Access Framework

Android4.4中引入了Storage Access Framework存储访问框架,简称(SAF)。SAF为用户浏览手机中存储的内容提供了方便,这些内容不仅包括文档、图片,视频、音频、下载,而且还包括所有由特定ContentProvider(须具有约定的API)提供的内容。不管这些内容来自于哪里,不管是哪个应用调用浏览系统文件内容的命令,系统都会用一个统一的界面让你去浏览。


这种能力姑且叫做一种生态系统,云存储以及本地存储都可以通过实现DocumentsProvider来参与到这个系统中。而客户端app要使用SAF提供的服务只需几行代码即可。

SAF框架包括以下内容:

(1)Document provider文件内容提供方

这是一个特殊的content provider(内容提供方),他让一个存储服务(比如Google Drive)可以对外展示自己所管理的文件。一个Document provider其实就是实现了DocumentsProvider的子类。document-provider的schema 和传统的文件存径格式一致,但是至于你的内容是怎么存储的完全取决于你自己,android系统中已经内置了几个这样的Document provider,比如关于下载、图片以及视频的Document provider。(注意这里的红色DocumentsProvider是一个类,而分开写的Document provider只是一种描述,因为翻译出来可能会让人忘了他的特殊身份。)

(2)客户端app

一个触发ACTION_OPEN_DOCUMENT或者ACTION_CREATE_DOCUMENTintent的客户端软件。通过触发ACTION_OPEN_DOCUMENT或者ACTION_CREATE_DOCUMENT客户端可以接收来自于Document provider的内容。

(3)选择器Picker

选择器其实就是一个类似于文件管理器的界面,而且是系统级别的界面,他提供了访问满足客户端过滤条件的所有Document provider内容的通道。说的具体点选择器就是文章开头提到的documentsui程序。

SAF的一些特性:

用户可以浏览所有document provider提供的内容,不光是一个app。

提供了长期、持续的访问document provider中文件的能力以及数据的持久化,用户可以实现添加、删除、编辑、保存document provider所维护的内容。

支持多用户以及临时性的内容服务,比如USB storage providers只有当驱动安装成功才会出现。

概要

SAF的核心是实现了DocumentsProvider的子类,即内容提供者(documentprovider)。documentprovider中数据是以传统的文件目录树组织起来的:

流程图

虽说documentprovider中数据是以传统的文件目录树组织起来的,但是那只是对外表现的形式,至于你的数据在内部究竟是怎么样(甚至完全杂乱无章),完全取决于你自己,只要你对外的接口能够通过DocumentsProvider的api访问就可以。

下面的流程图展示了一个photo应用使用SAF可能的结构:

从上图可以看出选择器Picker(System UI)是一个链接调用者与内容提供者的桥梁。它提供了一个UI同时也告诉了调用者可以选择哪些内容提供者,比如这里的DriveDocProvider、UsbDocProvider、CloundDocProvider。

当客户端app与Document provider之间的交互是在触发了ACTION_OPEN_DOCUMENT或者ACTION_CREATE_DOCUMENT intent之后,intent还可以进一步设置过滤条件:比如限制MIME type为’image’。

当intent触发之后选择器去寻找每一个注册了的provider,并将provider的符合条件的根目录显示出来。

选择器(即documentsui)为访问不同形式、不同来源的文件提供了统一的界面,你可以看到我的文件形式可以是图片、视频,文件的内容可以是来自本地或者是Google Drive的云服务。

下图显示了用户在选择图片的时候点中了Google Drive的情况。

客户端是如何调用的

在android4.3时代,如果你想从另外一个app中选择一个文件,比如从图库中选择一张图片文件,你必须触发一个intent比如ACTION_PICK或者ACTION_GET_CONTENT。然后在候选的app中选择一个app,从中获得你想要的文件,最关键的是被选择的app中要具有能为你提供文件的功能,如果一个不负责任的第三方开发者注册了一个恰恰符合你需求的intent,但是没有实现返回文件的功能,那么就会出现意想不到的错误。

在4.4中,你多了一个选择方式,你可以发送ACTION_OPEN_DOCUMENTintent来调用系统的documentsui来选择任何文件,不需要再依赖于其他的app了。

但是并不是说ACTION_GET_CONTENT就完全没有用了,如果你只是打开读取一个文件,ACTION_GET_CONTENT还是可以的,如果你是要有写入编辑的需求,那就用ACTION_OPEN_DOCUMENT。

注: 实际上在4.4系统中ACTION_GET_CONTENT启动的还是documentsui。

下面演示如何用ACTION_OPEN_DOCUMENT选择一张图片:

private static final int READ_REQUEST_CODE = 42;
...
/**
 * Fires an intent to spin up the "file chooser" UI and select an image.
 */
public void performFileSearch() {
 // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
 // browser.
 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
 // Filter to only show results that can be "opened", such as a
 // file (as opposed to a list of contacts or timezones)
 intent.addCategory(Intent.CATEGORY_OPENABLE);
 // Filter to show only images, using the image MIME data type.
 // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
 // To search for all documents available via installed storage providers,
 // it would be "*/*".
 intent.setType("image/*");
 startActivityForResult(intent, READ_REQUEST_CODE);
}

ACTION_OPEN_DOCUMENT intent发出以后documentsui会显示所有满足条件的document provider(显示的是他们的标题),以图片为例,其实它对应的document provider是MediaDocumentsProvider(在系统源码中),而访问MediaDocumentsProvider的URi形式为com.android.providers.media.documents;

如果在intent filter中加入categoryCATEGORY_OPENABLE的条件,则显示结果只有可以打开的文件,比如图片文件(思考一下 ,哪些是不可以打开的呢?);

如果设置intent.setType("image/*")则只显示MIME type为image的文件。

获取返回的结果

返回结果一般是一个uri,数据保存在onActivityResult的第三个参数resultData中,通过resultData.getData()获取uri。

@Override
public void onActivityResult(int requestCode, int resultCode,
 Intent resultData) {
 // The ACTION_OPEN_DOCUMENT intent was sent with the request code
 // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
 // response to some other intent, and the code below shouldn't run at all.
 if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
 // The document selected by the user won't be returned in the intent.
 // Instead, a URI to that document will be contained in the return intent
 // provided to this method as a parameter.
 // Pull that URI using resultData.getData().
 Uri uri = null;
 if (resultData != null) {
  uri = resultData.getData();
  Log.i(TAG, "Uri: " + uri.toString());
  showImage(uri);
 }
 }
}

获取元数据

一旦得到uri,你就可以用uri获取文件的元数据。下面演示了如何得到元数据信息,并打印到log中。

public void dumpImageMetaData(Uri uri) {
 // The query, since it only applies to a single document, will only return
 // one row. There's no need to filter, sort, or select fields, since we want
 // all fields for one document.
 Cursor cursor = getActivity().getContentResolver()
  .query(uri, null, null, null, null, null);
 try {
 // moveToFirst() returns false if the cursor has 0 rows. Very handy for
 // "if there's anything to look at, look at it" conditionals.
 if (cursor != null && cursor.moveToFirst()) {
  // Note it's called "Display Name". This is
  // provider-specific, and might not necessarily be the file name.
  String displayName = cursor.getString(
   cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
  Log.i(TAG, "Display Name: " + displayName);
  int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
  // If the size is unknown, the value stored is null. But since an
  // int can't be null in Java, the behavior is implementation-specific,
  // which is just a fancy term for "unpredictable". So as
  // a rule, check if it's null before assigning to an int. This will
  // happen often: The storage API allows for remote files, whose
  // size might not be locally known.
  String size = null;
  if (!cursor.isNull(sizeIndex)) {
  // Technically the column stores an int, but cursor.getString()
  // will do the conversion automatically.
  size = cursor.getString(sizeIndex);
  } else {
  size = "Unknown";
  }
  Log.i(TAG, "Size: " + size);
 }
 } finally {
 cursor.close();
 }
}

还可以获得bitmap(这段代码我也没看懂):

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
 ParcelFileDescriptor parcelFileDescriptor =
  getContentResolver().openFileDescriptor(uri, "r");
 FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
 Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
 parcelFileDescriptor.close();
 return image;

获得输出流
private String readTextFromUri(Uri uri) throws IOException {
 InputStream inputStream = getContentResolver().openInputStream(uri);
 BufferedReader reader = new BufferedReader(new InputStreamReader(
  inputStream));
 StringBuilder stringBuilder = new StringBuilder();
 String line;
 while ((line = reader.readLine()) != null) {
 stringBuilder.append(line);
 }
 fileInputStream.close();
 parcelFileDescriptor.close();
 return stringBuilder.toString();
}


如何创建一个新的文件

使用ACTION_CREATE_DOCUMENT intent来创建文件

// Here are some examples of how you might call this method.
// The first parameter is the MIME type, and the second parameter is the name
// of the file you are creating:
//
// createFile("text/plain", "foobar.txt");
// createFile("image/png", "mypicture.png");
// Unique request code.
private static final int WRITE_REQUEST_CODE = 43;
...
private void createFile(String mimeType, String fileName) {
 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
 // Filter to only show results that can be "opened", such as
 // a file (as opposed to a list of contacts or timezones).
 intent.addCategory(Intent.CATEGORY_OPENABLE);
 // Create a file with the requested MIME type.
 intent.setType(mimeType);
 intent.putExtra(Intent.EXTRA_TITLE, fileName);
 startActivityForResult(intent, WRITE_REQUEST_CODE);
}

可以在onActivityResult()中获取被创建文件的uri。

删除文件

前提是Document.COLUMN_FLAGS包含SUPPORTS_DELETE

DocumentsContract.deleteDocument(getContentResolver(), uri);

实现自己的document provider

如果你希望自己应用的数据也能在documentsui中打开,你就需要写一个自己的document provider。下面介绍自定义DocumentsProvider的步骤。

api 为19+

首先你需要在manifest文件中声明有这样一个Provider:

Provider的name为类名加包名,比如:

com.example.android.storageprovider.MyCloudProvider

Authority为包名+provider的类型名,如:

Com.example.android.storageprovider.documents

android:exported属性的值为ture

下面是一个provider的例子写法:


 ...
 
 ....
 
  
  
  
 
 

DocumentsProvider的子类

你至少要实现如下几个方法:

queryRoots()

queryChildDocuments()

queryDocument()

openDocument()

还有些其他的方法,但并不是必须的。

下面演示一个实现访问文件(file)系统的DocumentsProvider的大致写法。

queryRoots的实现:
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
 // Create a cursor with either the requested fields, or the default
 // projection if "projection" is null.
 final MatrixCursor result =
  new MatrixCursor(resolveRootProjection(projection));
 // If user is not logged in, return an empty root cursor. This removes our
 // provider from the list entirely.
 if (!isUserLoggedIn()) {
 return result;
 }
 // It's possible to have multiple roots (e.g. for multiple accounts in the
 // same app) -- just add multiple cursor rows.
 // Construct one row for a root called "MyCloud".
 final MatrixCursor.RowBuilder row = result.newRow();
 row.add(Root.COLUMN_ROOT_ID, ROOT);
 row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
 // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
 // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
 // recently used documents will show up in the "Recents" category.
 // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
 // shares.
 row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
  Root.FLAG_SUPPORTS_RECENTS |
  Root.FLAG_SUPPORTS_SEARCH);
 // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
 row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));
 // This document id cannot change once it's shared.
 row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
 // The child MIME types are used to filter the roots and only present to the
 // user roots that contain the desired type somewhere in their file hierarchy.
 row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
 row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
 row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
 return result;
}

queryChildDocuments的实现
@Override
public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
    String sortOrder) throws FileNotFoundException {
 final MatrixCursor result = new
  MatrixCursor(resolveDocumentProjection(projection));
 final File parent = getFileForDocId(parentDocumentId);
 for (File file : parent.listFiles()) {
 // Adds the file's display name, MIME type, size, and so on.
 includeFile(result, null, file);
 }
 return result;
}
queryDocument的实现
@Override
public Cursor queryDocument(String documentId, String[] projection) throws
 FileNotFoundException {
 // Create a cursor with the requested projection, or the default projection.
 final MatrixCursor result = new
  MatrixCursor(resolveDocumentProjection(projection));
 includeFile(result, documentId, null);
 return result;
}

为了更好的理解这篇文章,可以参考下面这些链接。


参考文章

https://developer.android.com/guide/topics/providers/document-provider.htm这篇文章的英文原文要翻墙

http://blog.csdn.net/huangyanan1989/article/details/17263203Android4.4中获取资源路径问题因为Storage Access Framework而引起的

https://github.com/iPaulPro/aFileChooser 一个文件管理器,在4.4中他是直接启用了documentsui

https://github.com/ianhanniballake/LocalStorage一个自定义的DocumentsProvider

https://github.com/xin3liang/platform_packages_providers_MediaProvider 实现了查询多媒体文件的DocumentsProvider,包括查询图片,这个是系统里面的

热心网友 时间:2022-04-11 20:40

AndroidManifest.xml中配置:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
动态申请:

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_REQUEST_CODE);
祝好运!
怎么打开存储权限

1. 首先,打开手机上的“设置”应用。2. 在设置页面中,找到并点击“应用管理”或“应用与权限”选项。3. 接下来,在应用管理或应用与权限页面中,选择需要开启存储权限的具体应用。4. 进入该应用的详情页面后,找到并点击“权限管理”或“应用权限&amp;r...

怎么打开存储权限?

1. Android 6.0及以上版本:在应用程序第一次访问存储空间时,系统会提示用户授权该应用程序访问存储空间。如果您在第一次提示时拒绝了授权,您可以通过以下步骤打开存储权限:- 打开设备的“设置”应用程序。- 选择“应用和通知”或“应用程序”选项。- 选择您想要授权的应用程序。- 选择“权限”选项。

怎么设置才能让App访问照片的存储空间?

1. 打开设备的“设置”应用。2. 滚动查找并点击“应用”或“应用管理”选项。3. 在应用列表中,找到并点击想要修改权限的App名称。4. 进入App详情页后,点击“权限”选项。5. 在权限列表中,找到“存储”或类似的选项,这通常控制着App的读写...

安卓手机如何设置app权限

1. 打开“设置”应用。2. 选择“应用”或“应用管理”。3. 在应用列表中找到目标App。4. 进入App的详细信息页面。5. 点击“权限”。6. 在权限列表中找到“存储”或相关选项,并选择“允许”来启用读写权限。请注意,授予读写权限将允许应用程序访问设备的存储空间,包括内部存储和外部SD卡(如果设...

存储权限在哪里

1、解锁手机,进入系统界面。2、点击桌面“设置”进入页面。3、下拉页面找到“隐私”,点击进入。4、进入页面如图所示,点击“权限管理”。5、进入页面如图所示,点击“权限”。6、进入页面如图所示,点击“存储”。7、进入页面如图所示,可根据需要选择是否授权存储权限,授权则、击右侧开关,不授权则关闭...

安卓12已经限制了andriod/data文件的访问,该如何管理那些

第四步:进入/data目录,探索并管理设备内部的文件和数据。第五步:享受对设备底层文件系统访问所带来的便利。通过上述步骤,你已成功绕过了系统对android/data文件的访问限制,能够直接操作内置存储。第六步:面对谷歌对用户权限的限制,表达了对垄断现象的担忧,并期待未来出现新的替代系统。尽管当前的手机...

怎么更改手机相册的访问权限

1. 打开手机的“设置”应用。2. 找到并点击“应用与权限”或类似选项,进入“权限管理”。3. 在权限列表中,找到“相册”或“存储”权限,点击进入。4. 在应用列表中,找到你想要更改相册权限的应用。5. 点击该应用,然后开启或关闭其访问相册的权限。无论是iOS还是Android系统,都建议用户定期检查和...

华为手机data访问限制怎么关

具体来说,用户需要按照以下步骤操作:1. 进入手机设置:首先,解锁华为手机,并在主界面上找到“设置”图标,点击进入。2. 进入安全和隐私设置:在设置菜单中,用户需要找到并点击“安全和隐私”选项。这个选项通常包含了手机的各种安全设置和隐私保护功能。3. 关闭访问限制:在安全...

安卓11es文件管理器授权办法

1.下载【es文件管理器】APP或其他第三方文件浏览器,打开APP。2.点击【es文件管理器】中的内部存储——点击【Android】文件夹。3.这时会弹出一个授权窗口,说由于系统的限制,需要授权APP才能访问——然后点击授权——然后弹出一个窗口,是否允许【es文件管理器】访问文件。点击允许-访问和文件夹的内容,...

手机怎么进入大容量存储模式

手机进入大容量存储模式的方法取决于具体的手机型号和操作系统。一般来说,大多数Android手机都支持大容量存储模式(也称为USB存储设备模式)。在这种模式下,手机可以被视为一个USB存储设备,从而允许用户在电脑和其他设备上直接访问手机的内部存储空间。要进入大容量存储模式,请按照以下步骤操作:1. 连接...

授权访问存储空间权限在哪里 设置中的存储空间权限在哪里 win7网络访问权限设置 电脑访问权限怎么设置 win10共享无权限访问权限 存储访问权限 允许存储访问权限 储存访问权限什么意思 华为允许存储访问权限是什么意思
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
创新5.10060声卡怎么关闭,为什么音质变得很差?我用的是KX 我已经装了声卡,和Kx管理器。可声音听见还是那么幼稚。怎么把声音调的... ...装不上KX3550,声卡是 创新5.1的 装完KX3550重启以后,提示 初始化... 我买了一个创新5.1 0060声卡,玩龙之谷游戏就声音不完全,只有背景音乐... 声卡5.10060KX驱动3550调试怎么弄 win11玩csgo游戏一直闪退什么原因 win11玩csgo游戏一直闪退的解决... 习惯养成心得体会 饥荒ios高脚鸟蛋怎么孵化高脚鸟怎么养 故事的力量可以从什么角度来分析? 地震前为什么要出现地震云 k40为什么data进不去 rman备份时候show all里控制文件我已经制定路径了,为什么这里显示这个路径 oracle 为什么要使用delete all input 如何手动建立Oracle监听器 oracle中 alter system register什么意思 电脑解决方案 解决方案行销工程师做什么 中国的民生问题和解决方案 电脑显示联机检查解决该问题的解决方案,该怎么办? 在项目中,你认为“目标,需求,解决方案”三者的顺序是什么?为什么?_问一问 【解决方案】 方案一: 新建一个记事本文件,把后缀名改成bat格式。在记事本中写入以下代码: @echo off win10更新系统后,电脑每次开机都要等差不多十分钟黑屏,才能显示桌面,求解决方法 我在桌面图标上点击右键,不显示菜单,闪一下就不见了.怎么办? 离婚最好的解决方案是什么 gameoverview是什么意思 关于asp+access中同时多用户同时提交..写入数据库的问题 如何用ACCESS管理大量成员信息 在局域网上的Access文件不能够被数个用户同时打开 access2013可以多用户吗 都说Access数据库最多支持 255 个并发用户,我现在又一个asp+Access数据库的网站,没有会员系统 android 怎么从storage中获取文件数据 如何获取Android设备挂载的所有存储器 android系统中,有关框架层的代码应该在以下哪个目录中 Android 获取手机内部存储的路径 Firebird Maestro好不好 急。。急。。急。。。目前使用较普遍的数据库软件有哪些?各自有什么特点! 除了access和mySQL外,还有哪些系统自带的,或者小巧方便的数据库系统用于程序开发的? 除了SQL Server数据库还有哪些数据库 心脏支架partner和firebird是什么牌子的?是国产还是进口? 原子经济性中的e-factor是什么? delphi怎么创建带密码的ACCESS数据库? android service权限问题 如何在服务器资源管理器中添加access的连接? 如何给eclipse里面添加权限啊? accesspermission 是做什么用的? Android权限添加了但是还是报错,求助各位 我开网站出现了You don&#39;t have permission to access &#47; on this server应该怎样设置?~~ 如何在服务器上连接access数据库 打包发布apk怎么解决android.permission.access android.permission.access_superuser在 manifest中未定义。 软件出现这个怎么设置,本人知道qq开始的