Datei teilen

Nachdem Sie Ihre App so eingerichtet haben, dass Dateien mithilfe von Inhalts-URIs geteilt werden, können Sie auf die Anfragen anderer Apps Anfragen für diese Dateien. Eine Möglichkeit, auf solche Anfragen zu reagieren, besteht darin, eine Dateiauswahl Schnittstelle der Serveranwendung, die andere Anwendungen aufrufen können. Bei diesem Ansatz kann ein Kunde -Anwendung, mit der Nutzer eine Datei aus der Server-App auswählen können und erhalten dann die Inhalts-URI.

In dieser Lektion erfahren Sie, wie Sie in Ihrer App eine Dateiauswahl Activity erstellen der auf Dateianfragen reagiert.

Dateianfragen erhalten

Um Anfragen für Dateien von Client-Apps zu erhalten und mit einem Inhalts-URI zu antworten, sollte Ihre App eine Dateiauswahl Activity. Client-Apps starten dies Activity durch Aufrufen von startActivityForResult() mit einer Intent, die die Aktion enthält ACTION_PICK. Wenn die Client-App aufruft startActivityForResult(), deine App kann ein Ergebnis in Form eines Inhalts-URI für die Datei, die der Nutzer ausgewählt hat, an die Client-App zurückgeben.

Wie Sie eine Anfrage für eine Datei in einer Client-App implementieren, erfahren Sie in der Lektion Eine freigegebene Datei anfordern

Dateiauswahl erstellen – Aktivität

Um die Dateiauswahl Activity einzurichten, geben Sie zuerst den Parameter Activity zusammen mit einem Intent-Filter in Ihrem Manifest. die mit der Aktion ACTION_PICK und der Kategorien CATEGORY_DEFAULT und CATEGORY_OPENABLE Fügen Sie auch MIME-Typ-Filter hinzu für die Dateien, die Ihre App anderen Apps bereitstellt. Im folgenden Snippet sehen Sie, wie das Tag Neuer Activity und Intent-Filter:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
        <application>
        ...
            <activity
                android:name=".FileSelectActivity"
                android:label="@File Selector" >
                <intent-filter>
                    <action
                        android:name="android.intent.action.PICK"/>
                    <category
                        android:name="android.intent.category.DEFAULT"/>
                    <category
                        android:name="android.intent.category.OPENABLE"/>
                    <data android:mimeType="text/plain"/>
                    <data android:mimeType="image/*"/>
                </intent-filter>
            </activity>

Dateiauswahlaktivität im Code definieren

Definieren Sie als Nächstes eine Activity-Unterklasse, die die Dateien anzeigt, die aus im internen Speicher der App auf das Verzeichnis files/images/. Hier kann der Nutzer auf die gewünschte Datei. Das folgende Snippet veranschaulicht, Activity und reagieren Sie auf die Auswahl des Nutzers:

Kotlin

class MainActivity : Activity() {

    // The path to the root of this app's internal storage
    private lateinit var privateRootDir: File
    // The path to the "images" subdirectory
    private lateinit var imagesDir: File
    // Array of files in the images subdirectory
    private lateinit var imageFiles: Array<File>
    // Array of filenames corresponding to imageFiles
    private lateinit var imageFilenames: Array<String>

    // Initialize the Activity
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Set up an Intent to send back to apps that request a file
        resultIntent = Intent("com.example.myapp.ACTION_RETURN_FILE")
        // Get the files/ subdirectory of internal storage
        privateRootDir = filesDir
        // Get the files/images subdirectory;
        imagesDir = File(privateRootDir, "images")
        // Get the files in the images subdirectory
        imageFiles = imagesDir.listFiles()
        // Set the Activity's result to null to begin with
        setResult(Activity.RESULT_CANCELED, null)
        /*
         * Display the file names in the ListView fileListView.
         * Back the ListView with the array imageFilenames, which
         * you can create by iterating through imageFiles and
         * calling File.getAbsolutePath() for each File
         */
        ...
    }
    ...
}

Java

public class MainActivity extends Activity {
    // The path to the root of this app's internal storage
    private File privateRootDir;
    // The path to the "images" subdirectory
    private File imagesDir;
    // Array of files in the images subdirectory
    File[] imageFiles;
    // Array of filenames corresponding to imageFiles
    String[] imageFilenames;
    // Initialize the Activity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Set up an Intent to send back to apps that request a file
        resultIntent =
                new Intent("com.example.myapp.ACTION_RETURN_FILE");
        // Get the files/ subdirectory of internal storage
        privateRootDir = getFilesDir();
        // Get the files/images subdirectory;
        imagesDir = new File(privateRootDir, "images");
        // Get the files in the images subdirectory
        imageFiles = imagesDir.listFiles();
        // Set the Activity's result to null to begin with
        setResult(Activity.RESULT_CANCELED, null);
        /*
         * Display the file names in the ListView fileListView.
         * Back the ListView with the array imageFilenames, which
         * you can create by iterating through imageFiles and
         * calling File.getAbsolutePath() for each File
         */
         ...
    }
    ...
}

Auf Dateiauswahl antworten

Nachdem ein Nutzer eine freigegebene Datei ausgewählt hat, muss in Ihrer Anwendung festgestellt werden, welche Datei ausgewählt wurde. Generieren Sie dann einen Inhalts-URI für die Datei. Da Activity das Feld Liste der verfügbaren Dateien in einer ListView, wenn der Nutzer auf einen Dateinamen klickt ruft das System die Methode onItemClick() auf, mit der Sie die ausgewählte Datei abrufen können.

Wenn Sie den URI einer Datei mit einem Intent von einer App an eine andere senden, müssen Sie darauf achten, einen URI abzurufen, der von anderen Apps lesen können. Auf Geräten mit Android 6.0 (API-Level 23) und höher erfordert spezielle insbesondere aufgrund von Änderungen am Berechtigungsmodell in dieser Android-Version READ_EXTERNAL_STORAGE ein werden gefährliche Berechtigung, die der empfangenden App möglicherweise fehlt.

Unter Berücksichtigung dieser Überlegungen empfehlen wir, die Verwendung von Uri.fromFile(), die hat mehrere Nachteile. Diese Methode:

  • Profilübergreifende Dateifreigabe ist nicht möglich.
  • Ihre App muss folgende Voraussetzungen erfüllen: WRITE_EXTERNAL_STORAGE auf Geräten mit Android 4.4 (API-Level 19) oder niedriger.
  • Hierfür müssen die empfangenden Apps die Berechtigung READ_EXTERNAL_STORAGE, die schlägt bei wichtigen Freigabezielen wie Gmail fehl, die diese Berechtigung nicht haben.

Anstelle von Uri.fromFile() Mit URI-Berechtigungen können Sie anderen Apps Zugriff auf bestimmte URIs haben. URI-Berechtigungen funktionieren nicht mit file:// URIs die von Uri.fromFile() generiert wurden, funktionieren mit URIs, die mit Contentanbietern verknüpft sind. Die FileProvider API kann beim Erstellen solcher URIs. Dieser Ansatz funktioniert auch bei Dateien, aber im lokalen Speicher der App, die den Intent sendet.

In onItemClick() erhalten Sie ein File-Objekt für den Dateinamen der ausgewählten Datei und übergeben Sie es als Argument an getUriForFile() zusammen mit dem die Sie in der <provider>-Element für FileProvider. Der resultierende Inhalts-URI enthält die Autorität, ein Pfadsegment, das dem (wie in den XML-Metadaten angegeben) sowie den Namen der Datei, einschließlich der . So ordnet FileProvider Verzeichnisse einem Pfad zu auf der Grundlage von XML-Metadaten wird im Abschnitt Geben Sie Verzeichnisse an, die freigegeben werden können.

Das folgende Snippet zeigt, wie Sie die ausgewählte Datei erkennen und einen Inhalts-URI dafür abrufen:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            /*
             * Get a File for the selected file name.
             * Assume that the file names are in the
             * imageFilename array.
             */
            val requestFile = File(imageFilenames[position])
            /*
             * Most file-related method calls need to be in
             * try-catch blocks.
             */
            // Use the FileProvider to get a content URI
            val fileUri: Uri? = try {
                FileProvider.getUriForFile(
                        this@MainActivity,
                        "com.example.myapp.fileprovider",
                        requestFile)
            } catch (e: IllegalArgumentException) {
                Log.e("File Selector",
                        "The selected file can't be shared: $requestFile")
                null
            }
            ...
        }
        ...
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            /*
             * When a filename in the ListView is clicked, get its
             * content URI and send it to the requesting app
             */
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                /*
                 * Get a File for the selected file name.
                 * Assume that the file names are in the
                 * imageFilename array.
                 */
                File requestFile = new File(imageFilename[position]);
                /*
                 * Most file-related method calls need to be in
                 * try-catch blocks.
                 */
                // Use the FileProvider to get a content URI
                try {
                    fileUri = FileProvider.getUriForFile(
                            MainActivity.this,
                            "com.example.myapp.fileprovider",
                            requestFile);
                } catch (IllegalArgumentException e) {
                    Log.e("File Selector",
                          "The selected file can't be shared: " + requestFile.toString());
                }
                ...
            }
        });
        ...
    }

Denken Sie daran, dass Sie nur Inhalts-URIs für Dateien generieren können, die sich in einem Verzeichnis befinden. die Sie in der Metadatendatei angegeben haben, die das <paths>-Element enthält, wie im Abschnitt freigegebene Verzeichnisse angeben beschrieben. Wenn Sie getUriForFile() für File in einem Pfad ist, den Sie nicht angegeben haben, erhalten Sie eine IllegalArgumentException

Berechtigungen für die Datei gewähren

Nachdem Sie nun einen Inhalts-URI für die Datei haben, die Sie für eine andere App freigeben möchten, müssen Sie Erlaubt der Client-App den Zugriff auf die Datei. Um den Zugriff zu ermöglichen, müssen Sie der Client-App Berechtigungen gewähren, indem Sie Hinzufügen des Inhalts-URI zu einem Intent und Festlegen von Berechtigungs-Flags für Intent. Die von Ihnen gewährten Berechtigungen sind temporär und laufen ab wenn der Aufgabenstapel der empfangenden App abgeschlossen ist.

Das folgende Code-Snippet zeigt, wie Sie eine Leseberechtigung für die Datei festlegen:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            ...
            if (fileUri != null) {
                // Grant temporary read permission to the content URI
                resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                ...
            }
            ...
        }
        ...
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    // Grant temporary read permission to the content URI
                    resultIntent.addFlags(
                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }
                ...
             }
             ...
        });
    ...
    }

Achtung:Das Aufrufen von setFlags() ist die einzige können Sie mithilfe temporärer Zugriffsberechtigungen sicher Zugriff auf Ihre Dateien gewähren. Anrufe vermeiden Context.grantUriPermission()-Methode für ein den Inhalts-URI der Datei, da diese Methode Zugriff gewährt, der nur durch Context.revokeUriPermission() wird angerufen.

Uri.fromFile() darf nicht verwendet werden. Der Empfang von Apps wird erzwungen. um die Berechtigung READ_EXTERNAL_STORAGE zu erhalten, funktioniert überhaupt nicht, wenn Sie Dateien für mehrere Nutzer und Versionen freigeben möchten. Android-Versionen niedriger als 4.4 (API-Level 19) benötigen, App mit WRITE_EXTERNAL_STORAGE. Wichtige Ziele zum Teilen, wie die Gmail App, müssen keine READ_EXTERNAL_STORAGE, was dazu führt, dass dieser Aufruf fehlschlägt. Stattdessen können Sie mithilfe von URI-Berechtigungen anderen Apps Zugriff auf bestimmte URIs gewähren. URI-Berechtigungen funktionieren nicht mit file:// URIs, die von Die/der Uri.fromFile() tut das an URIs arbeiten, die mit Contentanbietern verknüpft sind. Anstatt Ihre eigenen speziellen Lösungen zu implementieren, können und sollten Sie FileProvider verwenden. enthalten, wie unter Dateifreigabe beschrieben.

Datei für die anfragende App freigeben

Wenn du die Datei für die App freigeben möchtest, die sie angefordert hat, übergib den Intent mit dem Inhalts-URI und den Berechtigungen für setResult(). Wenn die gerade definierte Activity abgeschlossen ist, wird der System die Intent mit dem Inhalts-URI an die Client-App sendet. Das folgende Code-Snippet zeigt, wie das geht:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            ...
            if (fileUri != null) {
                ...
                // Put the Uri and MIME type in the result Intent
                resultIntent.setDataAndType(fileUri, contentResolver.getType(fileUri))
                // Set the result
                setResult(Activity.RESULT_OK, resultIntent)
            } else {
                resultIntent.setDataAndType(null, "")
                setResult(RESULT_CANCELED, resultIntent)
            }
        }
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    ...
                    // Put the Uri and MIME type in the result Intent
                    resultIntent.setDataAndType(
                            fileUri,
                            getContentResolver().getType(fileUri));
                    // Set the result
                    MainActivity.this.setResult(Activity.RESULT_OK,
                            resultIntent);
                    } else {
                        resultIntent.setDataAndType(null, "");
                        MainActivity.this.setResult(RESULT_CANCELED,
                                resultIntent);
                    }
                }
        });

Geben Sie den Nutzern die Möglichkeit, sofort zur Client-App zurückzukehren, nachdem sie eine Datei ausgewählt haben. Dazu können Sie z. B. ein Häkchen oder die Schaltfläche Fertig setzen. Methode verknüpfen mit die Schaltfläche mit der Attribut „android:onClick“. Rufen Sie in der Methode finish() Beispiel:

Kotlin

    fun onDoneClick(v: View) {
        // Associate a method with the Done button
        finish()
    }

Java

    public void onDoneClick(View v) {
        // Associate a method with the Done button
        finish();
    }

Weitere Informationen finden Sie hier: