我正在尝试使用 Xamarin 将(apk)文件下载到面向 Android Q(API 29)的 Android 项目中 sdcard 根目录上的“下载”文件夹。
首先,我将文件下载到位于 sdcard/Android/data/com.my.app/files 的应用程序的私有存储空间
然后我使用 MediaStore API 将文件复制到公共“下载”文件夹。这是我的代码:
private void SaveTest(string appPath, string fileName)
{
var contentValues = new ContentValues();
contentValues.Put(MediaStore.MediaColumns.DisplayName, fileName);
contentValues.Put(MediaStore.MediaColumns.MimeType, "application/vnd.android.package-archive");
contentValues.Put(MediaStore.MediaColumns.RelativePath, Android.OS.Environment.DirectoryDownloads);
var context = (Context)this;
var resolver = context.ContentResolver;
Stream stream = null;
Android.Net.Uri uri = null;
try
{
var contentUri = MediaStore.Downloads.ExternalContentUri;
var cursor = resolver.Query(
contentUri,
new[] { MediaStore.MediaColumns.Id, MediaStore.MediaColumns.DisplayName, MediaStore.MediaColumns.MimeType, MediaStore.MediaColumns.RelativePath },
$"{MediaStore.MediaColumns.DisplayName} = ? AND {MediaStore.MediaColumns.MimeType} = ? AND {MediaStore.MediaColumns.RelativePath} = ?",
new[] { fileName, "application/vnd.android.package-archive", Android.OS.Environment.DirectoryDownloads },
null
);
if(cursor != null && cursor.Count >= 1)
{
cursor.MoveToFirst();
var id = cursor.GetLong(cursor.GetColumnIndexOrThrow(MediaStore.MediaColumns.Id));
var displayName = cursor.GetString(cursor.GetColumnIndexOrThrow(MediaStore.MediaColumns.DisplayName));
var relativePath = cursor.GetString(cursor.GetColumnIndexOrThrow(MediaStore.MediaColumns.MimeType));
var lastModifiedDate = cursor.GetString(cursor.GetColumnIndexOrThrow(MediaStore.MediaColumns.RelativePath));
uri = ContentUris.WithAppendedId(contentUri, id);
}
else
{
uri = resolver.Insert(contentUri, contentValues);
}
cursor?.Close();
if (uri == null)
{
throw new Exception("Retrieving of creating MediaStore record failed.");
}
byte[] buffer = new byte[1024];
stream = resolver.OpenOutputStream(uri);
var fullPathAndFileName = Path.Combine(appPath, fileName);
using (var inputstream = new FileStream(fullPathAndFileName, FileMode.Open))
{
CopyFile(inputstream, stream);
}
}
catch
{
if (uri != null)
{
// Don't leave an orphan entry in the MediaStore
resolver.Delete(uri, null, null);
}
throw;
}
finally
{
stream?.Close();
}
}
MediaStore 中的插入第一次有效,但是当我第二次尝试插入时,返回的 Uri 为空。因此,我添加了 ContentResolver 查询,以便检测现有记录,但此查询不返回任何结果。只有当我使用不同的文件名时,它才会再次起作用。
当我查看设备日志时,我可以看到以下错误:
时间设备名称类型 PID 标签消息 05-14 14:14:27.442 错误 14571 SQLiteDatabase android.database.sqlite.SQLiteConstraintException: UNIQUE 约束失败:files._data (code 2067 SQLITE_CONSTRAINT_UNIQUE) at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native方法)在 android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:879) 在 android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790) 在 android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java :88) 在 android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1599) 在 android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1468) 在 com.android.providers.media.MediaProvider。insertFile(MediaProvider.java:2776) 在 com.android.providers.media.MediaProvider.insertInternal(MediaProvider.java:3317) 在 com.android.providers.media.MediaProvider.insert(MediaProvider.java:2966) 在 android.content .ContentProvider$Transport.insert(ContentProvider.java:309) 位于 android.os.Binder 的 android.os.Binder.execTransactInternal(Binder.java:1021) 的 android.content.ContentProviderNative.onTransact(ContentProviderNative.java:154)。 execTransact(Binder.java:994) 05-14 14:14:27.442 错误 14571 SQLiteDatabase 错误插入 bucket_display_name=Download owner_package_name=com.my.app parent=8 volume_name=external_primary date_modified=1589458467 _display_name=XXX.apk mime_type=application/vnd .android.package-archive _data=/storage/emulated/0/Download/XXX.apk title=XXX.apk group_id=102105 is_download=true date_added=1589458467 primary_directory=下载bucket_id=540528482 media_type=0 relative_path=下载/
即使在没有任何过滤器的情况下执行 mediastore 查询,结果计数也是 0。如何获得正确的 Uri?