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:
- Inhalts-URIs entwerfen
- Berechtigungen für Contentanbieter implementieren
- Berechtigungen
- Intents und Intent-Filter