0

我有一个应用程序,我可以在其中使用设备的相机拍照。我想做的是在不分配 EXTRA_OUTPUT 的情况下启动 ACTION_IMAGE_CAPTURE 意图,然后使用 file.renameTo 将在默认位置创建的文件移动到我自己的自定义位置。我的代码是这样的:

/* Start camera activity without EXTRA_OUTPUT */
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, _REQUESTCODE_ATTACH_CAMERA);

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == RESULT_OK) {
        switch(requestCode) {
            case _REQUESTCODE_ATTACH_CAMERA:
                /* Get path to most recently added image */
                final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA };
                final String imageOrderBy = MediaStore.Images.Media._ID + " DESC";
                Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, null, null, imageOrderBy);
                String fullPath = "";
                if(imageCursor.moveToFirst()){
                    fullPath = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
                    imageCursor.close();
                }

                File f = Environment.getExternalStorageDirectory();
                f = new File(f.getAbsolutePath() + File.separator + "DCIM" + File.separator + MY_APP_NAME;
                if(!f.exists()) {
                    f.mkdirs();
                }

                /* Create new file based on name of most recently created image */
                File oldFile = new File(fullPath);
                String newPath = f.getAbsolutePath() + File.separator + oldFile.getName() ;

                /* Move file with renameTo */
                oldFile.renameTo(new File(newPath));

                break;
            ...
        }
    }
}

所有这些工作都很好,但是发生了一件奇怪的事情。在我的应用程序中,我有另一个按钮,允许从手机的图库中选择现有图像。该代码如下所示:

Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
galleryIntent.setType("image/*");
activity.startActivityForResult(galleryIntent, _REQUESTCODE_ATTACH_GALLERY);

这也有效,但如果我使用上面发布的代码用相机拍照,然后尝试从图库中选择另一张图片,图库中将出现空白的“断开链接”类型的项目,其中不包含任何内容且无法选择. 这些似乎与使用 renameTo 拍摄和移动的照片相对应;如果我在 onActivityResult 中输入代码以将文件名发布到 LogCat,则记录的名称与它对应的先前移动文件的名称相同。尝试创建 File 对象或以任何方式访问该文件名,会导致 null 对象并强制关闭。

奇怪的是,Eclipse DDMS 中没有这些“断开的链接”文件的证据,如果我使用根浏览器,手机本身也没有证据,如果我重新安装 SD 卡,它们就会消失。

我在用相机拍摄图像后移动图像的全部原因是为了避免用不必要的图像填满手机的图库存储空间。虽然这些空的“断开链接”类型的文件似乎没有占用任何存储空间,但对于试图浏览其图库的最终用户来说,它们仍然非常烦人。有没有人对这里发生的事情或如何解决这个问题有任何想法?

编辑: 这是一张显示画廊外观的照片,其中显示了“断开的链接”类型的图像。使用我的应用程序拍摄的每张照片都会出现其中一张,如果我重新安装 SD 卡,它们都会消失。 屏幕截图显示

4

1 回答 1

2

部分感谢这个 SO 线程,我发现了一个解决方案。实际上,它会以这种方式运行是有道理的,因为为媒体内容保留了一个表格,因此在不告诉表格的情况下删除某些内容肯定会创建“断开链接”类型的场景。

最终的解决方案是使用 contentResolver.delete 在内容解析器中删除对文件的引用,但我发现有两种不同的方法可以工作。

/* Moving with renameTo */
//Use the same exact code as I had before (shortened for brevity) to move the file
oldFile.renameTo(newFile);
//Get URI from contentResolver using file Id from cursor
Uri oldUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media._ID)));
//Delete old file
getContentResolver().delete(oldUri, null, null);

以这种方式获取 URI 是必要的,因为它需要对 contentResolver 中的图像的引用,而不是其在存储中位置的路径。这种方式可能会让某些人觉得很脏,因为您正在移动一个文件,然后对该文件调用一个删除函数,以便欺骗内容解析器删除指向该文件的链接。如果您愿意,您可以不使用 renameTo 来执行此操作,这样对 delete(...) 的调用实际上会删除图像。

/* Moving with streams */
//Get streams
InputStream in = new FileInputStream(oldFile);
OutputStream out = new FileOutputStream(newFile);
byte[] buffer = new byte[1024];
int bytesRead = 0;
//Read old file into new file
while((bytesRead = in.read(buffer)) > 0) {
    out.write(buffer, 0, bytesRead);
}

//Get URI from contentResolver using file Id from cursor
Uri oldUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media._ID)));
//Delete old file
getContentResolver().delete(oldUri, null, null);

无论哪种方式,对 contentResolver.delete 的调用都是相同的,我只是想指出,如果图像已经被删除,它仍然可以工作。

在此期间,我发现了一个我什至没有意识到我有的问题的解决方案,我也会在这里发布,以防将来有同样问题的人遇到这个问题。为了使图像在新位置的设备库中保持可选状态,您需要让媒体扫描仪知道已经进行了更改。我发现有两种方法可以做到这一点:

/* This is the only way that I know of to handle multiple new files at once. I 
   really would use this sparingly, however, since it will rescan the entire 
   SD Card. Not only could this take a long time if the user has a lot of files 
   on their card, it will also show a notification so it is not exactly a 
   transparent operation. */
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));

/* You *could* do multiple files with this by passing in the path for each one 
   in the array of Strings, however an instance of this will get called for each 
   one rather than it doing them all at once. Likewise, your onScanCompleted 
   (if you choose to include one) will get called once for each file in the list. 
   So really, while this is much better for a small number of files, if you plan 
   on scanning a very large amount then the full rescan above would probably be 
   a better option. */
MediaScannerConnection.scanFile(context, new String[]{ newFilePathAsString }, null, 
    new MediaScannerConnection.OnScanCompletedListener() { 
        public void onScanCompleted(String path, Uri uri) {
            //This executes when scanning is completed
        }
    }
);
于 2012-11-20T16:58:41.407 回答