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

k40为什么data进不去

发布网友 发布时间: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

因为谷歌在Android11及以上系统中采用了文件沙盒存储模式,导致红米k40无法像以前一样访问Android/data目录。

解决方法:

1、使用的方式是SAF框架(Android Storage Access Framework)。

2、使用android.intent.action.OPEN_DOCUMENT_TREE(调用SAF框架的文件选择器选择一个文件夹)的Intent就可以授权获取某个文件目录的权限了。

3、通过DocumentFile Api访问目录,从一个文件夹URI生成DocumentFile对象(treeUri就是文件夹URI)。

但是我们有的的是一个文件夹uri,当然使用这个方法来生成DocumentFile对象,不同方法生成的DocumentFile对象有不同效果,如果用fromTreeUri生成的默认是文件夹对象,

有ListFiles() 方法,DocumentFile.ListFiles()也就是列出文件夹里面的全部子文件,类似于File.listFiles()方法。

然后得到了DocumentFile对象就可以进行操作了,比如列出子文件,删除文件,移动,删除什么的都可以,Android/data目录就是这样进行操作和访问的。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
王卡专属免流包是什么意思 腾讯王卡专属流量是什么意思 人防临空墙排烟机房可以留洞吗? 瑞士转机的问题? 雅阁、第九代凯美瑞、迈腾,大空间的中级车推荐 云顶之弈双人排位介绍 金铲铲之战 金铲铲段位相差多少可以一起打? 现在上海办理居住证需要本人去吗 34周加5天是几个月 孕34周是几个月了 孕34周如何预防早产 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数据库的网站,没有会员系统 如何解决ASP中多用户对access的操作问题 android系统怎样设置内部存储的访问权限 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't have permission to access / on this server应该怎样设置?~~ 如何在服务器上连接access数据库 打包发布apk怎么解决android.permission.access