我需要在本机C / C ++代码中的Android应用程序中按文件名打开文件。本机代码是我不希望修改的第三方库,但是它们通常需要文件名作为读取/写入文件的参数。借助Google的“范围存储” API并禁用对Android 10或更高版本中文件的本机访问,这确实是一个问题。
一种众所周知的解决方案是获取文件描述符并使用“ proc / self / fd / FD_NUMER”技巧,例如:
ParcelFileDescriptor mParcelFileDescriptor = null;
String getFileNameThatICanUseInNativeCode(Context context, DocumentFile doc) {
try {
Uri uri = doc.getUri();
mParcelFileDescriptor =
context.getContentResolver().openFileDescriptor(uri, "r");
if (mParcelFileDescriptor != null) {
int fd = mParcelFileDescriptor.getFd();
return "/proc/self/fd/" + fd;
}
}
catch (FileNotFoundException fne) {
return "";
}
}
// Don't forget to close mParcelFileDescriptor when done!
将此传递给本机C / C ++代码是可行的,但前提是该文件位于电话主存储器中。如果用户尝试打开插入到手机插槽中的外部SD卡上的文件,则该文件不起作用-这种方式打开的文件没有读取权限。我只能获取文件描述符int编号并使用fdopen(fd)。但这需要修改第三方库(开放源代码或许可的)的源代码,并且每当更新这些库的原始源时都会感到头疼。
有没有更好的解决方案来解决这个问题?不,我不想听到添加的解决方案
android:requestLegacyExternalStorage="true"
到AndroidManifest.xml应用程序部分-Google威胁要在2020年的下一版Android中禁用该功能,因此需要永久解决方案。另一个简单但愚蠢的解决方案是将用户尝试打开的整个文件(可能很大)复制到私有应用程序目录中。哑巴没用...
2020年5月19日更新:刚刚发现:READ_EXTERNAL_STORAGE权限使您可以在Android 11上运行并定位到API 30(或更高版本)时读取文件,但不列出目录内容。我将其作为错误提交,并得到了“这正在按预期工作”答复(https://issuetracker.google.com/issues/156660903)。如果有人在意,请在此处进行评论,也请在其“调查”中进行评论:https : //google.qualtrics.com/jfe/form/SV_9HOzzyeCIEw0ij3?Source=scoped-storage我不知道如何继续在Android上开发所有应用程序这些限制。
2020年5月17日更新:Google最终承认并允许在Android 11及更高版本中使用READ_EXTERNAL_STORAGE权限。在花了几个月的时间将我的应用程序转换为Storage Access Framework(SAF)之后,我现在需要一两个星期的时间才能将其转换回至少是读取文件的一部分,再转换为常规文件访问权限...谢谢Google!讽刺地感谢您所花费的时间和精力,并衷心感谢您至少部分地理解了我们的观点!
因此,不再需要下面列出的我以前的答案,请放心!
在Android“ Scoped Storage” BS上度过了我美好的一天之后,我找到了一种有效的解决方案,无需修改第三方本地库的源代码,只要该源代码具有并可以构建它们即可。
我的解决方案(非常不令人满意)是:在C / C ++编译命令中添加以下选项:
-include "[some/path/]idiocy_fopen_fd.h"
如您所见,idiocy_fopen_fd.h如下所示,对常规fopen()的每次调用都被idiocy_fopen_fd()代码替换,该代码检查文件名是否以“ / proc / self / fd /”开头,以及因此,提取文件描述符编号,然后调用fdopen()而不是fopen()...如果某人有更好的解决方案,最好是在您没有第三方库的源代码时也可以使用,请分享。
#ifndef fopen_fd
#include <stdio.h>
#include <string.h>
#include <unistd.h> // for dup()
#ifdef __cplusplus
extern "C" {
#endif
inline FILE* idiocy_fopen_fd(const char* fname, const char * mode) {
if (strstr(fname, "/proc/self/fd/") == fname) {
int fd = atoi(fname + 14);
if (fd != 0) {
// Why dup(fd) below: if we called fdopen() on the
// original fd value, and the native code closes
// and tries re-open that file, the second fdopen(fd)
// would fail, return NULL - after closing the
// original fd received from Android, it's no longer valid.
FILE *fp = fdopen(dup(fd), mode);
// Why rewind(fp): if the native code closes and
// opens again the file, the file read/write position
// would not change, because with dup(fd) it's still
// the same file...
rewind(fp);
return fp;
}
}
return fopen(fname, mode);
}
// Note that the above leaves the original file descriptor
// opened when finished - close parcelFileDescriptor in
// Java/Kotlin when your native code returns!
#ifdef __cplusplus
}
#endif
#define fopen idiocy_fopen_fd
#endif
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句