Über freigegebenen Speicher auf Dokumente und andere Dateien zugreifen

Auf Geräten mit Android 4.4 (API-Level 19) und höher kann Ihre App über das Storage Access Framework mit einem Dokumentanbieter interagieren, einschließlich externer Speicher-Volumes und cloudbasiertem Speicher. Dieses Framework ermöglicht Nutzern, mit einer Systemauswahl zu interagieren, um einen Dokumentanbieter auszuwählen sowie bestimmte Dokumente und andere Dateien auszuwählen, die Ihre Anwendung erstellen, öffnen oder ändern soll.

Da der Nutzer an der Auswahl der Dateien oder Verzeichnisse beteiligt ist, auf die Ihre Anwendung zugreifen kann, benötigt dieser Mechanismus keine Systemberechtigungen. Dadurch werden die Nutzersteuerung und der Datenschutz verbessert. Darüber hinaus bleiben diese Dateien, die außerhalb eines App-spezifischen Verzeichnisses und außerhalb des Medienspeichers gespeichert sind, nach der Deinstallation der App auf dem Gerät erhalten.

Die Verwendung des Frameworks umfasst die folgenden Schritte:

  1. Eine Anwendung ruft einen Intent auf, der eine speicherbezogene Aktion enthält. Diese Aktion entspricht einem bestimmten Anwendungsfall, den das Framework zur Verfügung stellt.
  2. Der Nutzer sieht eine Systemauswahl, mit der er einen Dokumentanbieter durchsuchen und einen Speicherort oder ein Dokument auswählen kann, an dem die speicherbezogene Aktion ausgeführt wird.
  3. Die Anwendung erhält Lese- und Schreibzugriff auf einen URI, der den vom Nutzer ausgewählten Standort oder das vom Nutzer ausgewählte Dokument darstellt. Mit diesem URI kann die Anwendung Vorgänge am ausgewählten Standort ausführen.

Damit der Zugriff auf Mediendateien auf Geräten mit Android 9 (API-Level 28) oder niedriger unterstützt wird, deklarieren Sie die Berechtigung READ_EXTERNAL_STORAGE und setzen Sie maxSdkVersion auf 28.

In diesem Leitfaden werden die verschiedenen Anwendungsfälle erläutert, die das Framework für die Arbeit mit Dateien und anderen Dokumenten unterstützt. Außerdem wird erläutert, wie Vorgänge für den vom Nutzer ausgewählten Standort ausgeführt werden.

Anwendungsfälle für den Zugriff auf Dokumente und andere Dateien

Das Storage Access Framework unterstützt die folgenden Anwendungsfälle für den Zugriff auf Dateien und andere Dokumente.

Neue Dateien erstellen
Mit der Intent-Aktion ACTION_CREATE_DOCUMENT können Nutzer eine Datei an einem bestimmten Speicherort speichern.
Dokumente oder Dateien öffnen
Mit der Intent-Aktion ACTION_OPEN_DOCUMENT können Nutzer ein bestimmtes Dokument oder eine bestimmte Datei zum Öffnen auswählen.
Zugriff auf den Inhalt eines Verzeichnisses gewähren
Mit der Intent-Aktion ACTION_OPEN_DOCUMENT_TREE, die unter Android 5.0 (API-Level 21) und höher verfügbar ist, können Nutzer ein bestimmtes Verzeichnis auswählen und deiner App Zugriff auf alle Dateien und Unterverzeichnisse in diesem Verzeichnis gewähren.

In den folgenden Abschnitten finden Sie Anleitungen zur Konfiguration der einzelnen Anwendungsfälle.

Neue Dateien erstellen

Verwenden Sie die Intent-Aktion ACTION_CREATE_DOCUMENT, um die Systemdateiauswahl zu laden und dem Nutzer die Auswahl eines Speicherorts für den Inhalt einer Datei zu ermöglichen. Dieser Vorgang ähnelt demjenigen, der in den Dialogfeldern „Speichern unter“ verwendet wird, die von anderen Betriebssystemen verwendet werden.

Hinweis : ACTION_CREATE_DOCUMENT kann eine vorhandene Datei nicht überschreiben. Wenn Ihre App versucht, eine Datei mit demselben Namen zu speichern, hängt das System eine Zahl in Klammern an das Ende des Dateinamens an.

Wenn Ihre Anwendung beispielsweise versucht, eine Datei namens confirmation.pdf in einem Verzeichnis zu speichern, in dem es bereits eine Datei mit diesem Namen gibt, speichert das System die neue Datei unter dem Namen confirmation(1).pdf.

Geben Sie beim Konfigurieren des Intents den Dateinamen und den MIME-Typ sowie optional den URI der Datei oder des Verzeichnisses an, den die Dateiauswahl beim ersten Laden anzeigen soll. Verwenden Sie dazu den Intent EXTRA_INITIAL_URI.

Das folgende Code-Snippet zeigt, wie der Intent zum Erstellen einer Datei erstellt und aufgerufen wird:

Kotlin

// Request code for creating a PDF document.
const val CREATE_FILE = 1

private fun createFile(pickerInitialUri: Uri) {
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "application/pdf"
        putExtra(Intent.EXTRA_TITLE, "invoice.pdf")

        // Optionally, specify a URI for the directory that should be opened in
        // the system file picker before your app creates the document.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }
    startActivityForResult(intent, CREATE_FILE)
}

Java

// Request code for creating a PDF document.
private static final int CREATE_FILE = 1;

private void createFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");
    intent.putExtra(Intent.EXTRA_TITLE, "invoice.pdf");

    // Optionally, specify a URI for the directory that should be opened in
    // the system file picker when your app creates the document.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);

    startActivityForResult(intent, CREATE_FILE);
}

Datei öffnen

Ihre Anwendung kann Dokumente als Speichereinheit verwenden, in die Nutzer Daten eingeben, die sie mit Peers teilen oder in andere Dokumente importieren möchten. Einige Beispiele sind, wie ein Nutzer ein Produktivitätsdokument öffnet oder ein Buch öffnet, das als EPUB-Datei gespeichert ist.

In diesen Fällen kann der Nutzer die zu öffnende Datei auswählen, indem er den Intent ACTION_OPEN_DOCUMENT aufruft. Dadurch wird die Dateiauswahl-App des Systems geöffnet. Geben Sie einen MIME-Typ an, damit nur die Dateitypen angezeigt werden, die von Ihrer App unterstützt werden. Außerdem können Sie optional den URI der Datei angeben, den die Dateiauswahl beim ersten Laden anzeigen soll. Dazu verwenden Sie den zusätzlichen Intent EXTRA_INITIAL_URI.

Das folgende Code-Snippet zeigt, wie der Intent zum Öffnen eines PDF-Dokuments erstellt und aufgerufen wird:

Kotlin

// Request code for selecting a PDF document.
const val PICK_PDF_FILE = 2

fun openFile(pickerInitialUri: Uri) {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "application/pdf"

        // Optionally, specify a URI for the file that should appear in the
        // system file picker when it loads.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }

    startActivityForResult(intent, PICK_PDF_FILE)
}

Java

// Request code for selecting a PDF document.
private static final int PICK_PDF_FILE = 2;

private void openFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");

    // Optionally, specify a URI for the file that should appear in the
    // system file picker when it loads.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);

    startActivityForResult(intent, PICK_PDF_FILE);
}

Zugriffsbeschränkungen

Unter Android 11 (API-Level 30) und höher können Sie die Intent-Aktion ACTION_OPEN_DOCUMENT nicht verwenden, um den Nutzer dazu aufzufordern, einzelne Dateien aus den folgenden Verzeichnissen auszuwählen:

  • Das Verzeichnis Android/data/ und alle Unterverzeichnisse.
  • Das Verzeichnis Android/obb/ und alle Unterverzeichnisse.

Zugriff auf den Inhalt eines Verzeichnisses gewähren

Anwendungen zur Dateiverwaltung und zum Erstellen von Medien verwalten Dateigruppen normalerweise in einer Verzeichnishierarchie. Verwenden Sie die Intent-Aktion ACTION_OPEN_DOCUMENT_TREE, um diese Funktion in Ihrer App bereitzustellen. Damit können Nutzer Zugriff auf eine gesamte Verzeichnisstruktur gewähren. Einige Ausnahmen sind ab Android 11 (API-Level 30) möglich. Ihre Anwendung kann dann auf jede Datei im ausgewählten Verzeichnis und in seinen Unterverzeichnissen zugreifen.

Bei Verwendung von ACTION_OPEN_DOCUMENT_TREE erhält Ihre Anwendung nur Zugriff auf die Dateien im Verzeichnis, das der Nutzer auswählt. Sie haben keinen Zugriff auf Dateien anderer Anwendungen, die sich außerhalb dieses vom Nutzer ausgewählten Verzeichnisses befinden. Durch diesen nutzergesteuerten Zugriff können Nutzer genau auswählen, welche Inhalte sie mit Ihrer Anwendung teilen möchten.

Optional können Sie mit dem zusätzlichen Intent EXTRA_INITIAL_URI den URI des Verzeichnisses angeben, das in der Dateiauswahl beim ersten Laden angezeigt werden soll.

Das folgende Code-Snippet zeigt, wie der Intent zum Öffnen eines Verzeichnisses erstellt und aufgerufen wird:

Kotlin

fun openDirectory(pickerInitialUri: Uri) {
    // Choose a directory using the system's file picker.
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
        // Optionally, specify a URI for the directory that should be opened in
        // the system file picker when it loads.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }

    startActivityForResult(intent, your-request-code)
}

Java

public void openDirectory(Uri uriToLoad) {
    // Choose a directory using the system's file picker.
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);

    // Optionally, specify a URI for the directory that should be opened in
    // the system file picker when it loads.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uriToLoad);

    startActivityForResult(intent, your-request-code);
}

Zugriffsbeschränkungen

Unter Android 11 (API-Level 30) und höher können Sie mit der Intent-Aktion ACTION_OPEN_DOCUMENT_TREE keinen Zugriff auf die folgenden Verzeichnisse anfordern:

  • Das Stammverzeichnis des internen Speicher-Volumes.
  • Das Stammverzeichnis jedes SD-Karten-Volumes, das der Gerätehersteller als zuverlässig erachtet, unabhängig davon, ob die Karte emuliert oder entfernt werden kann. Ein zuverlässiges Volume ist ein Volume, auf das eine App die meiste Zeit erfolgreich zugreifen kann.
  • Das Verzeichnis Download.

Außerdem können Sie unter Android 11 (API-Level 30) und höher die Intent-Aktion ACTION_OPEN_DOCUMENT_TREE nicht verwenden, um den Nutzer zur Auswahl einzelner Dateien aus den folgenden Verzeichnissen aufzufordern:

  • Das Verzeichnis Android/data/ und alle Unterverzeichnisse.
  • Das Verzeichnis Android/obb/ und alle Unterverzeichnisse.

Vorgänge für ausgewählten Standort ausführen

Nachdem der Nutzer eine Datei oder ein Verzeichnis mit der Dateiauswahl des Systems ausgewählt hat, können Sie den URI des ausgewählten Elements mit dem folgenden Code in onActivityResult() abrufen:

Kotlin

override fun onActivityResult(
        requestCode: Int, resultCode: Int, resultData: Intent?) {
    if (requestCode == your-request-code
            && resultCode == Activity.RESULT_OK) {
        // The result data contains a URI for the document or directory that
        // the user selected.
        resultData?.data?.also { uri ->
            // Perform operations on the document using its URI.
        }
    }
}

Java

@Override
public void onActivityResult(int requestCode, int resultCode,
        Intent resultData) {
    if (requestCode == your-request-code
            && resultCode == Activity.RESULT_OK) {
        // The result data contains a URI for the document or directory that
        // the user selected.
        Uri uri = null;
        if (resultData != null) {
            uri = resultData.getData();
            // Perform operations on the document using its URI.
        }
    }
}

Wenn Sie einen Verweis auf den URI des ausgewählten Elements abrufen, kann Ihre App mehrere Vorgänge für das Element ausführen. Beispielsweise können Sie auf die Metadaten eines Elements zugreifen, es bearbeiten und löschen.

In den folgenden Abschnitten wird gezeigt, wie Sie Aktionen für die vom Nutzer ausgewählten Dateien ausführen.

Vorgänge ermitteln, die ein Anbieter unterstützt

Verschiedene Contentanbieter ermöglichen verschiedene Vorgänge für Dokumente, z. B. das Kopieren des Dokuments oder das Anzeigen der Miniaturansicht eines Dokuments. Prüfen Sie den Wert von Document.COLUMN_FLAGS, um festzustellen, welche Vorgänge von einem bestimmten Anbieter unterstützt werden. Auf der Benutzeroberfläche Ihrer App können dann nur die Optionen angezeigt werden, die der Anbieter unterstützt.

Berechtigungen beibehalten

Wenn Ihre App eine Datei zum Lesen oder Schreiben öffnet, erteilt das System Ihrer App eine URI-Berechtigung für diese Datei. Diese gilt, bis das Gerät des Nutzers neu gestartet wird. Angenommen, Ihre App ist eine Bildbearbeitungs-App und Sie möchten, dass Nutzer direkt von Ihrer App aus auf die fünf Bilder zugreifen können, die sie zuletzt bearbeitet haben. Wenn das Gerät des Nutzers neu gestartet wurde, müssen Sie den Nutzer zurück zur Systemauswahl senden, um die Dateien zu finden.

Ihre App kann die vom System gewährte persistente URI-Berechtigung verwenden, um den Zugriff auf Dateien auch bei Geräteneustarts beizubehalten und die Nutzerfreundlichkeit zu verbessern. Dies wird im folgenden Code-Snippet gezeigt:

Kotlin

val contentResolver = applicationContext.contentResolver

val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
        Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)

Java

final int takeFlags = intent.getFlags()
            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);

Dokumentmetadaten untersuchen

Wenn Sie den URI für ein Dokument haben, erhalten Sie Zugriff auf dessen Metadaten. Dieses Snippet erfasst die Metadaten für ein durch den URI angegebenes Dokument und protokolliert sie:

Kotlin

val contentResolver = applicationContext.contentResolver

fun dumpImageMetaData(uri: Uri) {

    // The query, because it only applies to a single document, returns only
    // one row. There's no need to filter, sort, or select fields,
    // because we want all fields for one document.
    val cursor: Cursor? = contentResolver.query(
            uri, null, null, null, null, null)

    cursor?.use {
        // moveToFirst() returns false if the cursor has 0 rows. Very handy for
        // "if there's anything to look at, look at it" conditionals.
        if (it.moveToFirst()) {

            // Note it's called "Display Name". This is
            // provider-specific, and might not necessarily be the file name.
            val displayName: String =
                    it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
            Log.i(TAG, "Display Name: $displayName")

            val sizeIndex: Int = it.getColumnIndex(OpenableColumns.SIZE)
            // If the size is unknown, the value stored is null. But because an
            // int can't be null, the behavior is implementation-specific,
            // and unpredictable. So as
            // a rule, check if it's null before assigning to an int. This will
            // happen often: The storage API allows for remote files, whose
            // size might not be locally known.
            val size: String = if (!it.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                it.getString(sizeIndex)
            } else {
                "Unknown"
            }
            Log.i(TAG, "Size: $size")
        }
    }
}

Java

public void dumpImageMetaData(Uri uri) {

    // The query, because it only applies to a single document, returns only
    // one row. There's no need to filter, sort, or select fields,
    // because we want all fields for one document.
    Cursor cursor = getActivity().getContentResolver()
            .query(uri, null, null, null, null, null);

    try {
        // moveToFirst() returns false if the cursor has 0 rows. Very handy for
        // "if there's anything to look at, look at it" conditionals.
        if (cursor != null && cursor.moveToFirst()) {

            // Note it's called "Display Name". This is
            // provider-specific, and might not necessarily be the file name.
            String displayName = cursor.getString(
                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.i(TAG, "Display Name: " + displayName);

            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
            // If the size is unknown, the value stored is null. But because an
            // int can't be null, the behavior is implementation-specific,
            // and unpredictable. So as
            // a rule, check if it's null before assigning to an int. This will
            // happen often: The storage API allows for remote files, whose
            // size might not be locally known.
            String size = null;
            if (!cursor.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                size = cursor.getString(sizeIndex);
            } else {
                size = "Unknown";
            }
            Log.i(TAG, "Size: " + size);
        }
    } finally {
        cursor.close();
    }
}

Dokument öffnen

Wenn Sie einen Verweis auf den URI eines Dokuments haben, können Sie ein Dokument zur weiteren Verarbeitung öffnen. Dieser Abschnitt enthält Beispiele zum Öffnen einer Bitmap und eines Eingabestreams.

Bitmap

Im folgenden Code-Snippet sehen Sie, wie eine Bitmap-Datei anhand ihres URI geöffnet wird:

Kotlin

val contentResolver = applicationContext.contentResolver

@Throws(IOException::class)
private fun getBitmapFromUri(uri: Uri): Bitmap {
    val parcelFileDescriptor: ParcelFileDescriptor =
            contentResolver.openFileDescriptor(uri, "r")
    val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor
    val image: Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor)
    parcelFileDescriptor.close()
    return image
}

Java

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor =
            getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return image;
}

Nachdem du die Bitmap geöffnet hast, kannst du sie in einem ImageView darstellen.

Eingabestream

Das folgende Code-Snippet zeigt, wie ein InputStream-Objekt anhand seines URI geöffnet wird. In diesem Snippet werden die Zeilen der Datei zu einem String eingelesen:

Kotlin

val contentResolver = applicationContext.contentResolver

@Throws(IOException::class)
private fun readTextFromUri(uri: Uri): String {
    val stringBuilder = StringBuilder()
    contentResolver.openInputStream(uri)?.use { inputStream ->
        BufferedReader(InputStreamReader(inputStream)).use { reader ->
            var line: String? = reader.readLine()
            while (line != null) {
                stringBuilder.append(line)
                line = reader.readLine()
            }
        }
    }
    return stringBuilder.toString()
}

Java

private String readTextFromUri(Uri uri) throws IOException {
    StringBuilder stringBuilder = new StringBuilder();
    try (InputStream inputStream =
            getContentResolver().openInputStream(uri);
            BufferedReader reader = new BufferedReader(
            new InputStreamReader(Objects.requireNonNull(inputStream)))) {
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
    }
    return stringBuilder.toString();
}

Dokumente bearbeiten

Sie können das Storage Access Framework verwenden, um ein Textdokument zu bearbeiten.

Das folgende Code-Snippet überschreibt den Inhalt des Dokuments, das durch den angegebenen URI dargestellt wird:

Kotlin

val contentResolver = applicationContext.contentResolver

private fun alterDocument(uri: Uri) {
    try {
        contentResolver.openFileDescriptor(uri, "w")?.use {
            FileOutputStream(it.fileDescriptor).use {
                it.write(
                    ("Overwritten at ${System.currentTimeMillis()}\n")
                        .toByteArray()
                )
            }
        }
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

Java

private void alterDocument(Uri uri) {
    try {
        ParcelFileDescriptor pfd = getActivity().getContentResolver().
                openFileDescriptor(uri, "w");
        FileOutputStream fileOutputStream =
                new FileOutputStream(pfd.getFileDescriptor());
        fileOutputStream.write(("Overwritten at " + System.currentTimeMillis() +
                "\n").getBytes());
        // Let the document provider know you're done by closing the stream.
        fileOutputStream.close();
        pfd.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Dokument löschen

Wenn Sie den URI für ein Dokument haben und der Document.COLUMN_FLAGS des Dokuments SUPPORTS_DELETE enthält, können Sie das Dokument löschen. Beispiel:

Kotlin

DocumentsContract.deleteDocument(applicationContext.contentResolver, uri)

Java

DocumentsContract.deleteDocument(applicationContext.contentResolver, uri);

Äquivalenten Medien-URI abrufen

Die Methode getMediaUri() stellt einen Medienspeicher-URI bereit, der dem angegebenen URI des Dokumentanbieters entspricht. Die beiden URIs beziehen sich auf dasselbe zugrunde liegende Element. Mit dem Mediastore-URI können Sie über den freigegebenen Speicher einfacher auf Mediendateien zugreifen.

Die Methode getMediaUri() unterstützt ExternalStorageProvider-URIs. Unter Android 12 (API-Level 31) und höher unterstützt die Methode auch MediaDocumentsProvider-URIs.

Virtuelle Datei öffnen

Unter Android 7.0 (API-Level 25) und höher kann Ihre App virtuelle Dateien verwenden, die über das Storage Access Framework zur Verfügung gestellt werden. Auch wenn virtuelle Dateien keine binäre Darstellung haben, kann die App deren Inhalte öffnen, indem sie sie in einen anderen Dateityp umwandeln oder diese Dateien mit der Intent-Aktion ACTION_VIEW aufrufen.

Zum Öffnen virtueller Dateien muss die Clientanwendung eine spezielle Logik für deren Verarbeitung enthalten. Wenn Sie eine Byte-Darstellung der Datei abrufen möchten, z. B. um sich eine Vorschau der Datei anzeigen zu lassen, müssen Sie beim Dokumentanbieter einen alternativen MIME-Typ anfordern.

Nachdem der Nutzer eine Auswahl getroffen hat, verwenden Sie den URI in den Ergebnisdaten, um festzustellen, ob die Datei virtuell ist, wie im folgenden Code-Snippet gezeigt:

Kotlin

private fun isVirtualFile(uri: Uri): Boolean {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false
    }

    val cursor: Cursor? = contentResolver.query(
            uri,
            arrayOf(DocumentsContract.Document.COLUMN_FLAGS),
            null,
            null,
            null
    )

    val flags: Int = cursor?.use {
        if (cursor.moveToFirst()) {
            cursor.getInt(0)
        } else {
            0
        }
    } ?: 0

    return flags and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0
}

Java

private boolean isVirtualFile(Uri uri) {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false;
    }

    Cursor cursor = getContentResolver().query(
        uri,
        new String[] { DocumentsContract.Document.COLUMN_FLAGS },
        null, null, null);

    int flags = 0;
    if (cursor.moveToFirst()) {
        flags = cursor.getInt(0);
    }
    cursor.close();

    return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}

Nachdem Sie geprüft haben, ob das Dokument eine virtuelle Datei ist, können Sie die Datei in einen alternativen MIME-Typ wie "image/png" umwandeln. Das folgende Code-Snippet zeigt, wie geprüft wird, ob eine virtuelle Datei als Bild dargestellt werden kann. Wenn ja, wird ein Eingabestream aus der virtuellen Datei abgerufen:

Kotlin

@Throws(IOException::class)
private fun getInputStreamForVirtualFile(
        uri: Uri, mimeTypeFilter: String): InputStream {

    val openableMimeTypes: Array<String>? =
            contentResolver.getStreamTypes(uri, mimeTypeFilter)

    return if (openableMimeTypes?.isNotEmpty() == true) {
        contentResolver
                .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
                .createInputStream()
    } else {
        throw FileNotFoundException()
    }
}

Java

private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
    throws IOException {

    ContentResolver resolver = getContentResolver();

    String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);

    if (openableMimeTypes == null ||
        openableMimeTypes.length < 1) {
        throw new FileNotFoundException();
    }

    return resolver
        .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
        .createInputStream();
}

Zusätzliche Ressourcen

Weitere Informationen zum Speichern und Aufrufen von Dokumenten und anderen Dateien finden Sie in den folgenden Ressourcen.

Produktproben

Videos