Scoped Storage Stories: DocumentFile
Android 10 is greatly restricting access to external storage via filesystem APIs. Instead, we need to use other APIs to work with content. This is the third post in a series where we will explore how to work with those alternatives, starting with the Storage Access Framework (SAF).
Whether you use ACTION_OPEN_DOCUMENT
or ACTION_CREATE_DOCUMENT
, you wind up
with a Uri
that points to the “document”. Your primary use of that Uri
is
with ContentResolver
and its openInputStream()
and openOutputStream()
methods,
so you can read and/or write the document. This is not that different from having
a File
and using the FileInputStream
and FileOutputStream
constructors to
be able to read and/or write the document.
However, File
has a lot of other useful methods, such as:
-
getName()
to get the filename -
exists()
to confirm that an actual file exists at the path wrapped by theFile
-
delete()
to delete the file -
and so on
The way to get similar functionality for your document Uri
is
via DocumentFile
.
DocumentFile
can wrap a File
or a document Uri
and give you an API that
resembles a subset of File
functionality, including the three methods that I listed
above.
If you have a document Uri
, you can wrap it in a DocumentFile
via
DocumentFile.fromSingleUri()
. You can then call getName()
, exists()
, delete()
,
and other methods, such as:
-
getType()
to get the MIME type associated with the content -
length()
to get the length of the content (i.e., how many bytes you could read in from theInputStream
) -
renameTo()
to rename the content -
and so on
However, there are some limitations:
-
The “name” for a piece of content is not necessarily a filename with an extension. It is a “display name”, and it should be something that the user might recognize. However, if you are expecting
SomethingLike-This.png
, you may be disappointed. The display name might be a classic filename, but it does not have to be. -
canWrite()
is somewhat broken, in that it may returntrue
for aUri
that you cannot write to
Note that you can also get a DocumentFile
for a File
via DocumentFile.fromFile()
.
This allows you to treat document Uri
values and files the same, so that portions
of your app can be written independently of where the data comes from.
The entire series of “Scoped Storage Stories” posts includes posts on:
- The basics of using the Storage Access Framework
- Getting durable access to the selected content
- Working with
DocumentFile
for individual documents - Working with document trees
- Working with
DocumentsContract
- Problems with the SAF API
- A specific problem with
listFiles()
onDocumentFile
- Storing content using
MediaStore
- Reading content from the
MediaStore
- Modifying
MediaStore
content from other apps - Limitations of
MediaStore.Downloads
- The undocumented
Documents
option - More on
RecoverableSecurityException
- How to modify more metadata in
MediaStore