现在我想要实现一个从安卓上传文件到服务器的效果,具体流程是:用户点击上传按钮,就会激活ActivityResultContracts.GetContent,让用户选择SD卡上某个文件;被选中的文件会返回给我一个content uri;于是我将该uri保存下来,并用contentResolver.openInpustream来获取该文件的字节流,再写入到网络的outpustream去;若上传到一半手机重启了,那么下次再次运行程序时,我就重新用刚才保存下来的uri来打开字节流,继续上传……
听起来很美好,但问题在于,好像android不能持久保存content uri,我刚才保存那个uri是没有效果的,关机重启再用该uri打开字节流,会引发“java.lang.SecurityException: Permission Denial: opening provider com.android.externalstorage.ExternalStorageProvider from ProcessRecord{1771158d 10738:com.example.clouddisk/u0a186} (pid=10738, uid=10186) requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS”。
搜了很多解决办法,例如takePersistableUriPermissions()是没有效果的,它只作用于openDocument等操作获取的uri,不适用于getConten获取的urit(https://commonsware.com/blog/2016/08/10/uri-access-lifetime-shorter-than-you-might-think.html)。按照这篇文章的说法,我想要得到content uri的持久连接,唯一的办法就是把external storage里的内容copy到internal storage里去。
到了这里我就十分困惑,怎么会有这么反人类的设计呢?但更重要的是,这样一来我该如何实现上传的断点续传功能?总不能让用户再选一次吧?
最好的办法是获取到具体的路径,最简单
最后我自己解决了。。。
获取具体路径是不对的。content uri弄出来就是为了掩盖具体路径,从安卓只提供真实路径转content uri而不提供反向操作可见一斑(想了一下,大概还有一种解决办法,就是利用content uri来query出它的document_id,用它来替代content uri;使用的时候在特定目录去查表,遍历所有文件的id,对照之前保存的即可)
但最后我发现这问题很简单——openMutipleDocuments替代getContent即可。前者和后者都是可以弹出一个选择画面让用户自己选择文件,被选择的文件uri会出现在回调中。但前者可以持久化保存,或者不行,就这么简单。后者能达到的前者都能达到,甚至前者能实现的更多,很奇怪的事情。