Uri Access Lifetime: Shorter Than You Might Think
UPDATE 2020-08-08: Four years later, I have published an updated version of this post.
Your app gets a Uri
from some outside source. Perhaps you used
ACTION_GET_CONTENT
or ACTION_OPEN_DOCUMENT
to get a Uri
to some
user-selected content. Perhaps you have an activity with an <intent-filter>
set up to respond to ACTION_VIEW
or ACTION_SEND
or something, and
you got a Uri
that way.
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 that
the Uri
value can be saved safely to persistent storage. For a file:
Uri
, that might almost work, though the file could always be moved
or deleted.
However, you do not have long-term access to the content
identified by a content:
Uri
. It is best to think of a content:
Uri
as being akin to an HTTPS URL to some content, where that content can
only be accessed if the session is authenticated (e.g., via a session
cookie). Just because you can download content from that URL today does
not mean that you can download content from that URL tomorrow, as the
session may well have timed out.
So, how long do we have for content:
Uri
values?
I always thought that access was scoped to the lifetime of your process. It turns out that I was wrong, and that it’s much shorter than I had been thinking.
Ian Lake pointed out
that access is tied to the component that received the Uri
. Once
that component is destroyed, access to the content identified by
the Uri
lapses.
For example, suppose that you use ACTION_OPEN_DOCUMENT
to allow the
user to pick a document via the Storage Access Framework. That has
to be done by an activity, using startActivityForResult()
, where you
get the Uri
in onActivityResult()
. You can use that Uri
successfully
from within that activity instance. Once that activity is destroyed,
though, you can no longer access the content identified by the Uri
.
If you need incrementally longer access — say, long enough for you to process the content — the recipe is:
-
Use a service for doing that processing work
-
Pass the
Uri
to the service via the data facet of theIntent
(i.e.,setData()
) -
Add
FLAG_GRANT_READ_URI_PERMISSION
and/orFLAG_GRANT_WRITE_URI_PERMISSION
to the flags for theIntent
Now the service will have access to the content identified by the Uri
until the service is destroyed (or until the service uses similar techniques
to pass access along to some other component).
(passing the Uri
via an extra should also work on Android 5.0+, though
I have not tried that with this specific scenario)
If you need durable access — such as being able to access the content tomorrow — you have two main options that I know of:
-
If you used
ACTION_OPEN_DOCUMENT
,ACTION_CREATE_DOCUMENT
, or similar Storage Access Framework mechanisms, you can try usingtakePersistableUriPermissions()
onContentResolver
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. This approach sucks, as it duplicates the content, and changes to the original edition of the content will not be reflected in the copy. Use appropriate UI terms (e.g., “import”) to help the user understand that this is what is going on.