4

您好我正在尝试通过启动扫描来刷新图库。对于运行 Kitkat 和更高版本的设备,我正在使用 mediascannerconnection。但是 mediaScannerConnection 需要绝对路径才能进行扫描。我所拥有的是一个DocumentFile,它似乎没有任何方法来获取它的绝对路径。

以下是我正在使用的代码:

要使用新的棒棒糖 API 获得写入文件夹的权限——>将选定的 URI 保存到全局变量——>隐藏逻辑(创建/删除“.nomedia”文件)。

// Calling folder selector.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, 42);
..............
@SuppressLint("NewApi") @Override
    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
        if (resultCode == RESULT_OK && requestCode == 42) {
            Uri treeUri = resultData.getData();
            getContentResolver().takePersistableUriPermission(treeUri,
//                  Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
                    Intent.FLAG_GRANT_READ_URI_PERMISSION |             
                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            path = treeUri.getPath();
        }

 }
 ..........
 public void hide(boolean hide){
    DocumentFile pickedDir = DocumentFile.fromTreeUri(this,Uri.parse(path));
    if(hide){
            if(pickedDir.findFile(".nomedia")==null){
                pickedDir.createFile(null, ".nomedia");
                tellgallery(pickedDir.findFile(".nomedia").getUri().getPath());
            }
    }
    else{
            DocumentFile filetodelete = pickedDir.findFile(".nomedia");
            if(filetodelete!=null){
                filetodelete.delete();
                tellgallery(filetodelete.getPath());
            }
    }
 }

上面的代码完美运行,并在所需文件夹中创建/删除“.nomedia”文件。

现在,当我尝试告诉画廊该文件夹的文件已更改时。Gallery 无法选择更改。

下面是刷新/请求扫描的代码。

private void tellgallery(String path) {
    pathtonomedia = path;
    if(path!=null){
    if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
        mediaScannerConnection = new MediaScannerConnection(con,mediaScannerConnectionClient);
        mediaScannerConnection.connect();
    }
      else{
            this.sendBroadcast(new Intent(
            Intent.ACTION_MEDIA_MOUNTED,
            Uri.parse("file://" + path)));
      }
    }

}

MediaScanner 类如下:

private MediaScannerConnectionClient mediaScannerConnectionClient = 
        new MediaScannerConnectionClient() {

        @Override
        public void onMediaScannerConnected() {
            mediaScannerConnection.scanFile(pathtonomedia, null);              
        }

        @Override
        public void onScanCompleted(String path, Uri uri) {
            if(path.equals(pathtonomedia))
                mediaScannerConnection.disconnect();

        }
    };

但是代码不会刷新图库,我仍然可以在图库中看到该文件夹​​。然而,该文件夹会在 10 到 20 分钟后消失,这可能是系统刷新所致。

最后我想要的是

  • 无论如何我可以获得 DocumentFile 实例的路径吗?这样我就可以直接将它传递给 mediascanner 路径

  • 或者是否有任何其他方法可以请求扫描棒棒糖中的特定文件夹。特定文件夹位于 SDCARD 中,我只知道它的 URI 而不是实际路径。

非常感谢任何帮助。

4

2 回答 2

3

正如您所注意到的,删除不会触发媒体扫描,并且DocumentFileUri 不适用于MediaScanner,因此在删除文件之前,您必须查询MediaScanner有效路径。我所做的是遍历所有DocumentFile对象并调用.getLastPathSegment()它们。这将返回一个值,如:

E25A-3848:Pictures/appname/images/foo.jpg

在我的测试中,这个值总是有一个冒号,将一些设备特定的值与路径的尾部分开。如果用户选择内部存储而不是E25A-3838他们会得到类似的词primary,但这不相关。重要的是你想得到冒号后面的任何东西,所以我像这样遍历我需要删除的文档:

String temp = document.getUri().getLastPathSegment();
final int lastColon = temp.lastIndexOf(":");
if (lastColon > 0) {
    boolean deletionSuccess = document.delete();
    if (deletionSuccess) {
        Log.d(TAG, "Deleted " + document.getUri());
        segmentsToDelete.add(temp.substring(lastColon + 1));
    }
}

在这个循环之后,文档将消失,segmentsToDelete数组列表将包含所有需要从 MediaScanner 中清除的尾随路径。所以我做了另一个循环,要求 MediaScanner 删除所有以相同值结尾的条目:

final Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
for (String lastPathSegment : segmentsToDelete) {
    final int result = contentResolver.delete(uri,
        MediaStore.MediaColumns.DATA + " LIKE ? ESCAPE '\\'",
        new String[]{"%" + escapedLike(lastPathSegment)}
    );
    Log.d(TAG, "Media deletion for " + lastPathSegment + " was " + result);
}

.delete()调用将返回已删除的行数,因此您可以在其中放置一个断言以确保它始终返回 1。如果由于某种原因文档从未进入 mediascanner,则返回的可能会更少。该escapedLike()方法是一个自定义函数,用于确保类似 SQLITE 的模式匹配不匹配不正确的路径:

static String escapedLike(@NonNull String text)
{
    return text.replace("%", "\\%").replace("_", "\\_");
}

当然,如果你想删除整个目录,上面的方法也是一样的。在这种情况下,您不需要运行循环,您可以首先调用.delete()目录的 DocumentFile,然后在构建删除查询时使用%sqlLIKE参数的尾随,该参数应该返回大量删除(多达文件已在媒体扫描仪中注册)。

LIKE使用匹配而不是精确完整路径的原因是因为从 获得的先前值永远DocumentFile不会匹配完整路径。数据库E25A-3848:Pictures/appname/images/foo.jpg稍后会返回类似的东西,例如. 很遗憾,删除没有在内部调用 MediaScanner……</p> MediaScanner/storage/sdcard1/Pictures/appname/images/foo.jpg

于 2016-04-05T09:49:32.047 回答
0

你试过这个吗?这在许多情况下都有效。它在调用 MediaScannerConnection 后对我有用,这应该扫描整个 SD 卡。

    sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.parse("file://" + Environment.getExternalStorageDirectory())));
于 2015-10-31T16:42:46.057 回答