背景
直到Android Q,如果我们想获取有关APK文件的信息,可以使用WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE来访问存储,然后在文件路径上使用PackageManager.getPackageArchiveInfo函数。
存在类似情况,例如在压缩文件上使用ZipFile class,可能还有无数框架API和第三方库。
问题
谷歌最近宣布了对Android Q的大量限制。
其中之一称为Scoped Storage,当访问设备上的所有文件时,该文件会破坏存储权限。它使您可以处理媒体文件,也可以使用受限制的Storage-Access-Framework(SAF),后者不允许应用程序使用File API和文件路径访问和使用文件。
Android Q Beta 2发行时,由于它而中断了很多应用程序,包括Google。原因是默认情况下已将其打开,从而影响所有应用程序,无论它们是否针对AndroidQ。
原因是许多应用程序,SDK和Android框架本身-都经常使用File API。在许多情况下,它们也不支持InputStream或SAF相关的解决方案。一个例子就是我写过的APK解析例子(PackageManager.getPackageArchiveInfo)。
但是,在Q beta 3上,情况有所变化,因此目标Q的应用程序将具有作用域存储,并且有一个标记将其禁用,并且仍然照常使用常规存储权限和File API。遗憾的是,该标志只是临时的(读取here),因此它延迟了不可避免的。
我尝试过的
我已经尝试并发现了以下内容:
使用存储许可确实不允许我读取不是媒体文件的任何文件(我想查找APK文件)。好像文件不存在。
使用SAF,我可以找到APK文件,并通过一些变通办法来找到其真实路径(链接here),我注意到File API可以告诉我该文件确实存在,但无法获取其大小,并且框架无法通过getPackageArchiveInfo
使用其路径。写关于这个here
我试图建立到文件的符号链接(链接here),然后从符号链接中读取。它没有帮助。
对于解析APK文件的情况,我尝试搜索其他解决方案。我发现有2个使用File类(here和here)处理APK的github存储库,还有一个使用InputStream代替的仓库(here)。遗憾的是,使用InputStream的那个版本很旧,缺少各种功能(例如获取应用程序的名称和图标),并且不会很快更新。此外,拥有一个库需要维护以跟上未来版本的Android,否则将来可能会出现问题,甚至崩溃。
问题
通常,有没有一种方法可以在使用SAF时仍然使用File API?我不是在谈论根解决方案,也不是在将文件复制到其他地方。我说的是更可靠的解决方案。
对于APK解析的情况,是否有一种方法可以解决此问题,即框架仅提供文件路径作为参数?任何解决方法或使用InputStream的方法?
最佳答案
当我只能处理文件或文件路径时,如何处理SAF?即使您只能将Java File对象或路径字符串发送到无法修改的库函数,也有可能:
首先,获取需要处理的文件的Uri(以String形式,类似于“ content:// ...”),然后:
try {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r"); // may get FileNotFoundException here
// Obtain file descriptor:
int fd = parcelFileDescriptor.getFd(); // or detachFd() if we want to close file in native code
String linkFileName = "/proc/self/fd/" + fd;
// Call library function with path/file string:
someFunc(/*file name*/ linkFileName);
// or with File parameter
otherFunc(new File(linkFileName));
// Finally, if you did not call detachFd() to obtain the file descriptor, call:
parcelFileDescriptor.close();
// Otherwise your library function should close file/stream...
} catch (FileNotFoundException fnf) {
fnf.printStackTrace(); // or whatever
}