如何调用 Android 媒体扫描器来扫描供应商提供的外部 SD 卡,而不是通过 getExternalStorage() 获得的 SD 卡?

3
我的应用程序允许用户除了getExternalStorage()路径外,将文件保存在外部SD卡上。我知道Android本身没有外置SD卡的概念,但我们知道许多厂商会为平板电脑/手机提供一个额外的SD卡槽。而且指向这个特定sdcard的路径可能取决于设备制造商。
我的应用程序为用户提供了一个偏好设置,用户可以在其中提供与getExternalStorage()返回的路径不同的供应商路径到SD卡。
以前,我使用以下代码来调用媒体扫描器:
  sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
  + Environment.getExternalStorageDirectory())));

但是现在我在想以下代码是否有效:
  sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
  + "/someotherpath/blah/");

这个方案可行吗?我没有一个有额外SD卡槽的设备来测试它,您的意见对我很有用。

2个回答

1

我查看了Android开源代码(Android 4.1)

有一个名为/packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerReceiver.java的文件。

它包含以下代码:

 @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Uri uri = intent.getData();
        if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
            // scan internal storage
            scan(context, MediaProvider.INTERNAL_VOLUME);
        } else {
            if (uri.getScheme().equals("file")) {
                // handle intents related to external storage
                String path = uri.getPath();
                String externalStoragePath = Environment.getExternalStorageDirectory().getPath();

                Log.d(TAG, "action: " + action + " path: " + path);
                if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
                    // scan whenever any volume is mounted
                    scan(context, MediaProvider.EXTERNAL_VOLUME);
                } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&
                        path != null && path.startsWith(externalStoragePath + "/")) {
                    scanFile(context, path);
                }
            }
        }
    }

正如您所看到的,它将检查ACTION_MEDIA_MOUNT(您使用的)并调用scan()。但是,它将使用硬编码的MediaProvier.EXTERNAL_VOLUME(而不是传递的文件URI)。
回答您的问题,更改代码对您来说没有意义。任何带有文件模式的URI都将起同样的作用。
但是,有可能供应商会修改此代码。
还有一件事。Android 4.2引入了多用户概念,每个用户都有自己的外部存储。基于此,显示的代码可能已更改。
更新1
很有趣。最初,我只是浏览了MediaScannerReceiver的一部分,并且认为它只会扫描一个外部卷。然而,在您告诉我您已经查看了代码并问它是否有效之后,我进一步调查发现它将搜索所有可安装的卷(正如您所说的)。
据我理解,它通过以下执行路径(以伪Java代码形式呈现,以忽略所有实例化等等):
  • MediaScannerReceiver.onReceive调用scan(context, MediaProvider.EXTERNAL_VOLUME);
  • MediaScannerReceiver.scan调用context.startService(new Intent(context, MediaScannerService.class).putExtras(args));其中args包含键/值对"volume"=MediaProvider.EXTERNAL_VOLUME)
  • MediaScannerService.onStartCommand调用mServicehandler.sendMessage
  • MediaScannerService.ServiceHandler.handleMessage接收消息并调用相当于scan(StorageManager.getVolumePaths(),MediaProvider.EXTERNAL_VOLUME)
  • MediaScannerService.scan调用MediaScanner.scanDirectories
  • MediaScanner逐个遍历每个目录

考虑到"StorageManager.getVolumePaths()"应该返回所有可挂载的卷,我认为您的当前代码应该没问题(它将扫描所有卷)。


谢谢Victor,那么可以安全地假设调用scan(context, MediaProvider.EXTERNAL_VOLUME);将扫描所有SD卡吗?我深入研究了Android代码,似乎从MountService类中的com.android.internal.R.xml.storage_list获取扫描目录。 - Ahmed
2Ahmed:我进行了更深入的调查。请看我的回答中的Update1。 - Victor Ronin
谢谢Victor,经过代码演练,我得出了类似的结论,希望在生产环境中能够奏效 :) 谢谢。 - Ahmed
为什么这个详细的问题没有任何点赞?好答案,谢谢。 - schlenger

0

对于 Api 8 及以上版本,您可以使用以下内容

 MediaScannerConnection.scanFile(this,
      new String[] { file.toString() }, null,
      new MediaScannerConnection.OnScanCompletedListener() {
  public void onScanCompleted(String path, Uri uri) {
      Log.i("ExternalStorage", "Scanned " + path + ":");
      Log.i("ExternalStorage", "-> uri=" + uri);
  }
 });

这只扫描单个文件,您可以在此处提供任何路径,包括替代的外部存储。


谢谢,但这段代码不适用于我的情况。我必须扫描整个sdcard路径,而不是已记录的单个文件。 - Ahmed
您还可以在文件路径中传递目录。这将仅触发对该文件夹的扫描。 - Arveen
这意味着它也适用于类似 "/mnt/sdcard2" 这样的东西。 - Arveen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接