标签: documentfile

如何使用为Android 5.0(Lollipop)提供的新SD卡访问API?

背景

Android 4.4(KitKat)上,谷歌已经限制了对SD卡的访问.

从Android Lollipop(5.0)开始,开发人员可以使用新的API,要求用户确认允许访问特定文件夹,如此Google-Groups帖子所述.

问题

该帖子指导您访问两个网站:

这看起来像是一个内部示例(可能稍后会在API演示中显示),但很难理解发生了什么.

这是新API的官方文档,但它没有详细说明如何使用它.

这是它告诉你的:

如果您确实需要完全访问整个文档子树,请首先启动ACTION_OPEN_DOCUMENT_TREE以允许用户选择目录.然后将生成的getData()传递给fromTreeUri(Context,Uri)以开始使用用户选择的树.

在导航DocumentFile实例树时,您始终可以使用getUri()来获取表示该对象的基础文档的Uri,以便与openInputStream(Uri)等一起使用.

要在运行KITKAT或更早版本的设备上简化代码,可以使用fromFile(File)来模拟DocumentsProvider的行为.

问题

我对新API有几个问题:

  1. 你是如何真正使用它的?
  2. 根据帖子,操作系统会记住应用程序被授予访问文件/文件夹的权限.你如何检查你是否可以访问文件/文件夹?是否有一个函数可以返回我可以访问的文件/文件夹列表?
  3. 你如何在Kitkat上处理这个问题?它是支持库的一部分吗?
  4. 操作系统上是否有设置屏幕,显示哪些应用可以访问哪些文件/文件夹?
  5. 如果在同一设备上为多个用户安装了应用程序,会发生什么?
  6. 是否有关于这个新API的任何其他文档/教程?
  7. 权限可以被撤销吗?如果是这样,是否有意图被发送到应用程序?
  8. 是否会在所选文件夹上递归请求权限工作?
  9. 使用该权限是否也允许用户有机会根据用户的选择进行多次选择?或者应用程序是否需要专门告知意图允许哪些文件/文件夹?
  10. 模拟器上有没有办法尝试新的API?我的意思是,它有SD卡分区,但它作为主要外部存储器,所以已经给出了所有访问权限(使用简单的权限).
  11. 当用户用另一张SD卡替换SD卡时会发生什么?

android android-sdcard android-5.0-lollipop documentfile

113
推荐指数
2
解决办法
7万
查看次数

如何使用新的Lollipop API访问所有SD卡?

背景

从Lollipop开始,应用程序可以访问真正的SD卡(在Kitkat上无法访问之后,并且在之前的版本中尚未获得官方支持),正如我在此处所述.

问题

因为看到支持SD卡的Lollipop设备变得非常罕见,并且因为模拟器实际上没有能力(或者它?)来模拟SD卡支持,所以我花了很长时间来测试它.

无论如何,似乎不是使用普通的File类来访问SD卡(一旦获得了它的许可),你需要使用Uris来使用它,使用DocumentFile.

这限制了对正常路径的访问,因为我找不到将Uris转换为路径的方法,反之亦然(加上它非常烦人).这也意味着我不知道如何检查当前的SD卡是否可访问,因此我不知道何时要求用户允许读取/写入(或向他们).

我试过的

目前,这就是我获取所有SD卡的路径:

  /**
   * returns a list of all available sd cards paths, or null if not found.
   *
   * @param includePrimaryExternalStorage set to true if you wish to also include the path of the primary external storage
   */
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  public static List<String> getExternalStoragePaths(final Context context,final boolean includePrimaryExternalStorage)
    {
    final File primaryExternalStorageDirectory=Environment.getExternalStorageDirectory();
    final List<String> result=new ArrayList<>();
    final File[] externalCacheDirs=ContextCompat.getExternalCacheDirs(context);
    if(externalCacheDirs==null||externalCacheDirs.length==0)
      return result;
    if(externalCacheDirs.length==1)
      {
      if(externalCacheDirs[0]==null)
        return result;
      final String storageState=EnvironmentCompat.getStorageState(externalCacheDirs[0]);
      if(!Environment.MEDIA_MOUNTED.equals(storageState))
        return …
Run Code Online (Sandbox Code Playgroud)

android uri file android-sdcard documentfile

25
推荐指数
1
解决办法
5898
查看次数

使用SAF(存储访问框架)的Android SD卡写权限

关于如何在SD卡(android 5及以上版本)中编写(和重命名)文件的大量调查结果后,我认为android提供的新SAF需要获得用户写入SD卡文件的许可.

我在这个文件管理器应用程序ES文件资源管理器中看到,最初它采用以下方式读取和写入权限,如图片所示.

在此输入图像描述

图2

选择SD卡后,授予写入权限.

因此我尝试使用SAF的方式相同,但重命名文件失败了.我的代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    rename = (Button) findViewById(R.id.rename);

    startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), 42);
}

@Override
public void onActivityResult(int requestCode,int resultCode,Intent resultData) {
    if (resultCode != RESULT_OK)
        return;
    Uri treeUri = resultData.getData();
    DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
    grantUriPermission(getPackageName(), treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}

public void renameclick(View v) {
    File ff = new File("/storage/sdcard1/try1.jpg");
    try {
        ff.createNewFile();
    } catch (Exception e) {
        Log.d("error", "creating");
        e.printStackTrace();
    }
} …
Run Code Online (Sandbox Code Playgroud)

android android-sdcard storage-access-framework android-5.0-lollipop documentfile

19
推荐指数
1
解决办法
2万
查看次数

来自树URI的Android 5.0 DocumentFile

好吧,我进行了搜索和搜索,没有人得到我的确切答案,或者我错过了.我让我的用户通过以下方式选择目录:

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, READ_REQUEST_CODE);
Run Code Online (Sandbox Code Playgroud)

在我的活动中,我想捕捉实际路径,这似乎是不可能的.

protected void onActivityResult(int requestCode, int resultCode, Intent intent){
    super.onActivityResult(requestCode, resultCode, intent);
    if (resultCode == RESULT_OK) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
            //Marshmallow 

        } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
            //Set directory as default in preferences
            Uri treeUri = intent.getData();
            //grant write permissions
            getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            //File myFile = new File(uri.getPath()); 
            DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
Run Code Online (Sandbox Code Playgroud)

我选择的文件夹位于:

Device storage/test/
Run Code Online (Sandbox Code Playgroud)

我已经尝试了以下所有方法来获得确切的路径名称,但无济于事.

File myFile = new File (uri.getPath());
//returns: /tree/1AF6-3708:test

treeUri.getPath();
//returns: /tree/1AF6-3708:test/

pickedDir.getName()
//returns: …
Run Code Online (Sandbox Code Playgroud)

storage android android-sdcard documentfile

11
推荐指数
2
解决办法
8958
查看次数

如何通过为Lollipop提供的API随机访问SD卡上的文件?

我的应用程序使用Java类RandomAccessFile通过SeekableByteChannel接口的实现随机读取/写入SD卡上的文件.现在我需要使用新的Lollipop API为Android 5.0重写它.

我找到了唯一的阅读方式:

InputStream inputStream = getContentResolver().openInputStream(uri);
Run Code Online (Sandbox Code Playgroud)

和写:

ParcelFileDescriptor pfd = getActivity().getContentResolver().openFileDescriptor(uri, "w");
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
Run Code Online (Sandbox Code Playgroud)

从/到新API中的文件.

我希望能够在某个随机位置设置通道,并将字节读/写到该位置.是否可以在新的SDK 21中执行此操作?新的SDK是否意味着获取频道:

FieInputChannel fieInputChannel = fileInputStream.getChannel();
FieOutputChannel fieOutputChannel = fileOutputStream.getChannel();
Run Code Online (Sandbox Code Playgroud)

或其他一些方法?

filechannel android-sdcard randomaccessfile android-5.0-lollipop documentfile

10
推荐指数
1
解决办法
1678
查看次数

在Lollipop上使用Android Storage Access框架列出文件时出错

背景

我有一些应用程序大量使用SD卡进行文件同步.Kitkat上破坏的外部SD卡访问仍然是一个大问题,但我正在尝试使用Lollipop上提供的新API为具有此功能的用户解决此问题.

我成功请求并持有SD卡的权限,我可以列出从授予权限活动返回的根Uri中的文件.

查看有关如何在此处完成此操作的更多信息: 如何使用新的-sd-card-access-api-present-for-lollipop

然后,用户可以选择任何文件夹/子文件夹进行同步,并将文件夹文档Uri作为数据库中的字符串保留.

问题

稍后,可能在重新启动应用程序后,可以启动文件的同步.然后我尝试列出子文件夹中的文件(记住,我已经授予了正确的权限,并且这种工作的持久性也允许我访问所有子文件).

然后,我从存储的字符串创建一个新的DocumentFile实例,并尝试列出文件:

  DocumentFile dir = DocumentFile.fromTreeUri(ctx, Uri.parse(storedUri));
  dir.listFiles();
Run Code Online (Sandbox Code Playgroud)

问题是listFiles总是返回授予根Uri的子节点,而不是实际Uri的子节点我给DocumentFile.fromTreeUri方法.

我已经检查了DocumentFile的源代码,似乎有一个bug,特别是我没有看到需要进一步修改Uri:

public static DocumentFile fromTreeUri(Context context, Uri treeUri) {
  final int version = Build.VERSION.SDK_INT;
  if (version >= 21) {
    return new TreeDocumentFile(null, context,
      DocumentsContractApi21.prepareTreeUri(treeUri));
  } else {
  return null;
}
Run Code Online (Sandbox Code Playgroud)

如果我们查看DocumentsContractApi21.prepareTreeUri的来源,我们会看到重建Uri:

 public static Uri prepareTreeUri(Uri treeUri) {
   return DocumentsContract.buildDocumentUriUsingTree(treeUri,
     DocumentsContract.getTreeDocumentId(treeUri));
 }
Run Code Online (Sandbox Code Playgroud)

它调用的方法:

 public static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) {
   return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(treeUri.getAuthority()).appendPath(PATH_TREE)
                 .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT)
                 .appendPath(documentId).build();
 }

 public static String getTreeDocumentId(Uri documentUri) …
Run Code Online (Sandbox Code Playgroud)

android android-sdcard android-5.0-lollipop documentfile

9
推荐指数
1
解决办法
1852
查看次数

正确删除DocumentFile(尊重MediaStore)

我有DocumentFile以下两种定义:

DocumentFile documentFile;
Uri documentFileUri;
Run Code Online (Sandbox Code Playgroud)

我可以通过以下方法从SD卡中删除文档文件:

  1. documentFile.delete();
  2. DocumentsContract.deleteDocument(contentResolver, documentFileUri);

但以上方法均不能从中删除相应的条目MediaStore

处理该问题的正确方法是什么?如果我使用ContentProvider删除本地文件,它将File从数据库(contentResolver.delete(localFileUri, null, null);)中删除AND行。我希望如果使用,也会发生相同的情况,DocumentsContract但不会发生...

我想要的是

我想了解一下更新MediaStore。通常我会打电话给我,contentResolver.delete(documentFileUri, null, null);但这会失败,但有一个例外,说uri不支持删除...

是否有比我的解决方法更有效的方法?

解决方法

目前,我在删除a后使用以下功能立即更新媒体存储DocumentFile

public static boolean updateAfterChangeBlocking(String path, int timeToWaitToRecheckMediaScanner)
{
    final AtomicBoolean changed = new AtomicBoolean(false);
    MediaScannerConnection.scanFile(StorageManager.get().getContext(),
            new String[]{path}, null,
            new MediaScannerConnection.OnScanCompletedListener() {
                public void onScanCompleted(String path, Uri uri) {
                    changed.set(true);
                }
            });

    while (!changed.get()) {
        try {
            Thread.sleep(timeToWaitToRecheckMediaScanner);
        } catch (InterruptedException e) …
Run Code Online (Sandbox Code Playgroud)

android storage-access-framework documentfile

9
推荐指数
1
解决办法
978
查看次数

Android DocumentFile 无效 URI

我正在尝试使用 DocumentFile 列出 Android 5.1 手机上的外部存储设备中的文件

String rootPathURI = "file:/media/storage/sdcard1/data/example.externalstorage/files/";


File f = new File(URI(rootPathURI));
DocumentFile documentFile = DocumentFile.fromFile(f);
Run Code Online (Sandbox Code Playgroud)

这段代码工作正常,但我想这样做;

String rootPathURI = "file:/media/storage/sdcard1/data/example.externalstorage/files/";

DocumentFile documentFile = DocumentFile.fromTreeUri(getApplicationContext(), Uri.parse(rootPathURI));
Run Code Online (Sandbox Code Playgroud)

但我得到这样的例外:

W/System.err( 5157): java.lang.IllegalArgumentException: Invalid     URI:"file:/media/storage/sdcard1/data/example.externalstorage/files/"
Run Code Online (Sandbox Code Playgroud)

java android documentfile

7
推荐指数
1
解决办法
6796
查看次数

如何使用Android 5.0的新SD卡访问API设置上次修改的文件属性?

背景:

使用:ACTION_OPEN_DOCUMENT_TREE + DocumentFile

更多信息: 如何使用针对Android 5.0(Lollipop)推出的新SD卡访问API?

我想问一下:

我找不到如何更改文件属性的方法.有没有?

特别是,我需要更改Last Modified属性 - 比如File Class方法:

public boolean setLastModified(long time); 
Run Code Online (Sandbox Code Playgroud)

我没有找到任何替补:

https://developer.android.com/reference/android/support/v4/provider/DocumentFile.html

或者在DocumentsContract之类的相关类中,...

新API的文档几乎没用,API函数运行速度极慢,新API的代码重写非常麻烦.我很抱歉这么强硬,但Kitkat"EACCESS(Permission denied)功能"花了我几年的生命而不是解决方案我会得到这个.

编辑:

似乎setLastModified(...)方法即使使用java.io.FileClass 也不起作用(至少从Android版本4.4开始):

https://code.google.com/p/android/issues/detail?id=18624

例如,如果您有一个归档应用程序,并且您想要真正的上次修改时间,而不是从归档中提取文件的时间 - 抱歉.很多同步工具变得无用......

编辑2:

Android 5.1(模拟器):setLastModified(long time)方法仍然不起作用.

编辑3:

Android 6.0(模拟器):setLastModified(long time)方法仍然无法正常工作.

android file sd-card android-5.0-lollipop documentfile

6
推荐指数
0
解决办法
2115
查看次数

Storage Access Framework获取正确的Uri路径删除/编辑/获取文件

TL:DR; 我解释了如何使用DocumentFile使用创建文件夹和子文件夹以及如何删除使用此类创建的文件。从onActvityResult()和documentFile.getUri.toString()返回的Uri不相同。我的问题是如何在不使用SAF UI的情况下获得有效的Uri来操作文件夹和文件,如果可能的话,不使用hack也不行。

让我分享到目前为止我学到的知识并提出我的问题。如果要获取文件夹的Uri并对其进行处理,则应使用Intentwith ACTION_OPEN_DOCUMENT_TREE获取一个Uri来访问文件夹并为该uri设置W / R权限。

通过以下方式授予 onActivityResult的持久权限

final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
Run Code Online (Sandbox Code Playgroud)

如果选择设备的主文件夹:

Uri treeUri = data.getData();
treeUri.toString()
Run Code Online (Sandbox Code Playgroud)

返回:content://com.android.externalstorage.documents/tree/primary:

File mediaStorageDir = new File(Environment.getExternalStorageDirectory(), "");
Run Code Online (Sandbox Code Playgroud)

返回值:存储/模拟/ 0

new File(treeUri.toString()).getAbsolutePath();
Run Code Online (Sandbox Code Playgroud)

返回值:内容:/com.android.externalstorage.documents/tree/primary:

如果使用DocumentFile类获取主文件夹的路径,则会得到

DocumentFile saveDir = null;
saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
String uriString = saveDir.getUri().toString();
Run Code Online (Sandbox Code Playgroud)

返回值:file:/// storage / emulated / 0

我的第一个问题是如何使用DocumentFile类获取带有内容的Uri。

我正在构建一个摄影应用程序,默认情况下,我想使用DocumentFile类为图像设置初始文件夹。

 @TargetApi(19)
protected DocumentFile getSaveDirMainMemory() {
    DocumentFile …
Run Code Online (Sandbox Code Playgroud)

android storage-access-framework documentfile

6
推荐指数
1
解决办法
1051
查看次数