Po skonfigurowaniu w aplikacji udostępniania plików za pomocą identyfikatorów URI treści możesz odpowiadać żądania dostępu do tych plików. Jednym ze sposobów odpowiedzi na te prośby jest wskazanie plików do wyboru z aplikacji serwera, który mogą wywoływać inne aplikacje. Takie podejście pozwala klientowi pozwala użytkownikom wybrać plik w aplikacji serwera i odebrać ten plik identyfikator URI treści.
Z tej lekcji dowiesz się, jak wybrać plik Activity
w Twojej aplikacji
która odpowiada na prośby o dostęp do plików.
Otrzymuj prośby o pliki
Aby otrzymywać żądania plików z aplikacji klienckich i odpowiadać za pomocą identyfikatora URI treści, aplikacja powinna
wybierz plik Activity
. Uruchamianie aplikacji klienckich
Activity
, wywołując metodę startActivityForResult()
za pomocą elementu Intent
zawierającego działanie
ACTION_PICK
Gdy aplikacja klienta zadzwoni
startActivityForResult()
, aplikacja może
zwraca wynik do aplikacji klienckiej w postaci identyfikatora URI treści dla wybranego przez użytkownika pliku.
Aby dowiedzieć się, jak wdrożyć żądanie dotyczące pliku w aplikacji klienckiej, zapoznaj się z tym wykładem. Wysyłanie prośby o udostępnienie pliku
Aktywność dotycząca tworzenia wyboru plików
Aby skonfigurować wybór pliku Activity
, zacznij od określenia
Activity
w pliku manifestu wraz z filtrem intencji
pasujące do działania ACTION_PICK
oraz
kategorie CATEGORY_DEFAULT
i
CATEGORY_OPENABLE
. Dodaj też filtry typów MIME
dotyczące plików, które Twoja aplikacja udostępnia innym aplikacjom. Fragment kodu poniżej pokazuje, jak określić
nowy element Activity
i filtr intencji:
<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>
Zdefiniuj aktywność związaną z wyborem pliku w kodzie
Następnie zdefiniuj podklasę Activity
, która będzie wyświetlać pliki dostępne z
jest znajdujący się w katalogu files/images/
aplikacji w pamięci wewnętrznej i umożliwia użytkownikowi wybranie
odpowiedni plik. Fragment kodu poniżej pokazuje, jak to definiować
Activity
i odpowiedz na wybór użytkownika:
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 */ ... } ... }
Reagowanie na wybór pliku
Gdy użytkownik wybierze udostępniony plik, aplikacja musi określić, który plik został wybrany.
a następnie wygeneruj identyfikator URI treści dla pliku. Ponieważ Activity
wyświetla
lista plików dostępnych w elemencie ListView
, gdy użytkownik kliknie nazwę pliku.
system wywołuje metodę onItemClick()
, w której można pobrać wybrany plik.
Gdy używasz intencji przesłania identyfikatora URI pliku z jednej aplikacji do drugiej:
musisz więc zadbać o to, aby uzyskać identyfikator URI
które aplikacje mogą odczytać. Dotyczy to urządzeń z Androidem 6.0 (poziom interfejsu API 23) lub nowszym
wymaga specjalnych
ze względu na zmiany w modelu uprawnień w tej wersji Androida,
READ_EXTERNAL_STORAGE
–
staje się
niebezpieczne uprawnienia, których może brakować aplikacja odbierająca.
Mając to na uwadze, zalecamy unikanie stosowania
Uri.fromFile()
, która
ma kilka wad. W ten sposób:
- Nie można udostępniać plików między profilami.
- Aplikacja musi mieć
WRITE_EXTERNAL_STORAGE
na urządzeniach z Androidem 4.4 (poziom interfejsu API 19) lub starszym. - Wymaga, aby aplikacje odbierające miały
READ_EXTERNAL_STORAGE
, które nie uda się zrealizować ważnych celów udostępniania, takich jak Gmail, które nie mają tych uprawnień.
Zamiast Uri.fromFile()
,
możesz używać uprawnień URI, aby przyznawać innym aplikacjom
dostęp do określonych identyfikatorów URI. Uprawnienia do identyfikatora URI nie działają w przypadku file://
identyfikatorów URI
wygenerowanych przez: Uri.fromFile()
.
działają na identyfikatorach URI powiązanych z dostawcami treści.
FileProvider
interfejs API może
ułatwiają tworzenie takich identyfikatorów URI. Ta metoda działa też w przypadku plików, które nie są plikami
w pamięci zewnętrznej, ale w pamięci lokalnej aplikacji wysyłającej intencję.
W aplikacji onItemClick()
uzyskasz:
File
obiektu o nazwie wybranego pliku i przekazać go jako argument w
getUriForFile()
oraz
organ nadzorczy określony w
<provider>
dla elementu FileProvider
.
Powstały identyfikator URI treści zawiera urząd, segment ścieżki odpowiadający atrybutowi
(jak określono w metadanych XML) oraz nazwę pliku wraz z jego nazwą
. Jak FileProvider
mapuje katalogi na ścieżkę
oparte na metadanych XML zostały opisane w sekcji
Określ katalogi do udostępniania.
Ten fragment kodu pozwala wykryć wybrany plik i uzyskać dla niego identyfikator URI treści:
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()); } ... } }); ... }
Pamiętaj, że identyfikatory URI treści możesz generować tylko w przypadku plików, które znajdują się w katalogu
określony w pliku metadanych zawierającym element <paths>
,
opisane w sekcji Wskazywanie katalogów do udostępniania. Jeśli dzwonisz
getUriForFile()
za
File
w nieokreślonej ścieżce, otrzymasz żądanie
IllegalArgumentException
Przyznaj uprawnienia do pliku
Teraz gdy masz już identyfikator URI treści pliku, który chcesz udostępnić innej aplikacji,
zezwól aplikacji klienckiej na dostęp do pliku. Aby zezwolić na dostęp, przyznaj uprawnienia aplikacji klienckiej przez
dodanie identyfikatora URI treści do Intent
, a potem ustawienie flag uprawnień na
Intent
. Przyznane uprawnienia są tymczasowe i wygasają
automatycznie po zakończeniu stosu zadań aplikacji odbierającej.
Ten fragment kodu pokazuje, jak ustawić uprawnienia do odczytu pliku:
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); } ... } ... }); ... }
Uwaga: połączenie pod numer setFlags()
to jedyna opcja
to sposób na bezpieczny dostęp do plików za pomocą tymczasowych uprawnień dostępu. Nie dzwoń
Metoda Context.grantUriPermission()
dla
identyfikatora URI zawartości pliku, ponieważ ta metoda przyznaje dostęp, który można unieważnić tylko
Dzwonię pod numer Context.revokeUriPermission()
.
Nie używaj Uri.fromFile()
. Wymusza odbieranie aplikacji
mieć uprawnienie READ_EXTERNAL_STORAGE
,
nie będzie w ogóle działać przy udostępnianiu
między różnymi użytkownikami i w różnych wersjach
Androida w wersji starszej niż 4.4 (poziom interfejsu API 19) wymagałoby
musi mieć WRITE_EXTERNAL_STORAGE
.
W przypadku bardzo ważnych celów udostępniania, takich jak aplikacja Gmail,
READ_EXTERNAL_STORAGE
, powodując
aby zakończyć się niepowodzeniem.
Zamiast tego możesz korzystać z uprawnień URI, aby przyznawać innym aplikacjom dostęp do określonych identyfikatorów URI.
Chociaż uprawnienia do identyfikatora URI nie działają w przypadku identyfikatorów URI file://, ponieważ są generowane przez
Uri.fromFile()
– tak
pracowaliśmy nad identyfikatorem URI powiązanym z dostawcami treści. Zamiast wdrażać własne rozwiązania,
możesz i powinien używać usługi FileProvider
jak wyjaśniliśmy w artykule Udostępnianie plików.
Udostępnij plik aplikacji, która wysłała prośbę
Aby udostępnić plik aplikacji, która go zażądała, przekaż Intent
zawierający identyfikator URI treści i uprawnienia do setResult()
. Gdy zdefiniowany właśnie Activity
dobiegnie końca,
system wysyła do aplikacji klienckiej identyfikator Intent
zawierający identyfikator URI treści.
Fragment kodu, który pokazuje, jak to zrobić:
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); } } });
Zapewnij użytkownikom możliwość natychmiastowego powrotu do aplikacji klienckiej po wybraniu pliku.
Możesz to zrobić na przykład przez zaznaczenie znacznika wyboru lub przycisku Gotowe. Powiąż metodę z
za pomocą przycisku
android:onClick
. W metodzie wywołaj
finish()
Na przykład:
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(); }
Dodatkowe informacje znajdziesz tutaj:
- Projektowanie identyfikatorów URI treści
- Wdrażanie uprawnień dostawcy treści
- Uprawnienia
- Filtry intencji i zamiarów