Once Again, Uri Access Lifetime Is Shorter Than You Might Think
I first wrote about this subject nine years agoand updated the post five years ago.But, as I was pondering an entry in Jorge Castillo���s Bluesky ���Android dev secret��� roster,it dawned on me that this post needs yet another refresh, as this is still an ongoingsource of trouble for newcomers to Android app development.
Let���s say that your app gets a Uri from some outside source. Perhaps you usedActivityResultContracts.GetContent or ActivityResultContracts.OpenDocumentto get a Uri to someuser-selected content. Or, perhaps you have an activity with an set up to respond to ACTION_VIEW or ACTION_SEND or something, andyou got a Uri that way. Or, maybe you get one from another app via the clipboard,drag-and-drop, etc.
How long can you use that Uri to access the content that it points to?
Some developers think that you have indefinite access to it, and thatthe Uri value can be saved safely to persistent storage. Alas, that is notthe case.
For a file:Uri, that might almost work, though the file could always be movedor deleted. However, the file scheme was all but banned nearly a decade ago,so it is unlikely that you will encounter one. Most likely, you will getone with a content scheme.
However, you do not have long-term access to the contentidentified by a content: Uri. It is best to think of a content: Urias being akin to an HTTPS URL to some content, where that content canonly be accessed if the session is authenticated (e.g., via a sessioncookie). Just because you can download content from that URL today doesnot mean that you can download content from that URL tomorrow, as thesession may well have timed out.
You might then think that you have access as long as your process is running,and that access rights lapse at that point. That���s what I originally thought���and that���s not right either.
Instead, only the component instance that received the Uri can access the content.And, for the Intent actions that I listed, that means the Activitythat receives the result can access the content��� but not other activity instanceswithin your app, nor any services, etc.
For short-term use, you might hand the Uri off to a repository.The repository can load the content and deliver it to various componentsof your app using a reactive API. Or you can consume the content directlyin the receiving activity in other ways, such as handing a Uri to an imageto your favorite image-loading library (e.g., Coil). So long as you are usingthe Activity that received the content as your Context, or are using the Applicationsingleton as your Context, you should be fine.
Nowadays, many apps are single-activity apps, with different screens being implementedby fragments or composables. This reduces the impact ofshort-lived content access. However, if you implemented more than one activity,and if you need to get the Uri to another activity in your app:
Attach it to the Intent you use to start that other activity via setData()
Add Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSIONto the Intent based on what the other activity needs to do
This extends the permission that your first activity has to the second activity, so ittoo can access the content. The same basic approach would hold true for getting theUri to a Service.
That approach can allow you to use the Uri within all components of your runningprocess. It does not help you use that Uri in future processes, though.
If you need durable access ��� such as being able to access the contenttomorrow ��� you have two main options that I know of:
If you used ActivityResultContracts.OpenDocument, ActivityResultContracts.CreateDocument, or similarStorage Access Framework actions, you can try using takePersistableUriPermissions()on ContentResolver to get long-term access to the content.
Otherwise, before your component is destroyed, make a local copy of the content in your app���s portion of internal storage (e.g., getCacheDir()).This approach duplicates the content, and changes to the original edition of thecontent will not be reflected in the copy. Use appropriate UI terms(e.g., ���import���) to help the user understand that this is what is goingon.


