Setelah menyiapkan aplikasi untuk berbagi file menggunakan URI konten, Anda dapat merespons ke aplikasi lain terhadap file tersebut. Salah satu cara untuk merespons permintaan ini adalah dengan menyediakan pilihan file antarmuka dari aplikasi server yang dapat dipanggil oleh aplikasi lain. Pendekatan ini memungkinkan klien yang memungkinkan pengguna memilih file dari aplikasi server dan kemudian menerima permintaan akses URI konten.
Tutorial ini menunjukkan cara membuat Activity
pemilihan file di aplikasi Anda
yang merespons permintaan file.
Menerima permintaan file
Untuk menerima permintaan file dari aplikasi klien dan merespons dengan URI konten, aplikasi Anda harus
menyediakan Activity
pemilihan file. Aplikasi klien memulai proses ini
Activity
dengan memanggil startActivityForResult()
dengan Intent
yang berisi tindakan
ACTION_PICK
. Saat aplikasi klien memanggil
startActivityForResult()
, aplikasi Anda dapat
mengembalikan hasil ke aplikasi klien, dalam bentuk URI konten untuk file yang dipilih pengguna.
Untuk mempelajari cara mengimplementasikan permintaan file di aplikasi klien, lihat pelajaran ini Meminta file yang dibagikan.
Membuat Aktivitas pemilihan file
Untuk menyiapkan pemilihan file Activity
, mulailah dengan menentukan
Activity
dalam manifes Anda, bersama filter intent
yang cocok dengan tindakan ACTION_PICK
dan
kategori CATEGORY_DEFAULT
dan
CATEGORY_OPENABLE
. Juga tambahkan filter jenis MIME
untuk file yang ditayangkan
aplikasi Anda ke aplikasi lain. Cuplikan berikut menunjukkan cara menentukan
Activity
dan filter intent baru:
<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>
Menentukan Aktivitas pemilihan file dalam kode
Selanjutnya, tentukan subclass Activity
yang menampilkan file yang tersedia dari
direktori files/images/
aplikasi Anda di penyimpanan internal dan memungkinkan pengguna memilih
file yang diinginkan. Cuplikan berikut menunjukkan cara menentukan
Activity
dan merespons pilihan pengguna:
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 */ ... } ... }
Merespons pemilihan file
Setelah pengguna memilih file yang dibagikan, aplikasi Anda harus menentukan file yang dipilih dan
membuat URI konten untuk file tersebut. Karena Activity
menampilkan
daftar file yang tersedia di ListView
, saat pengguna mengklik nama file
sistem akan memanggil metode onItemClick()
, di mana Anda bisa mendapatkan file yang dipilih.
Saat menggunakan intent untuk mengirim URI file dari satu aplikasi ke aplikasi lainnya,
Anda harus berhati-hati untuk
mendapatkan URI yang
aplikasi dapat dibaca. Melakukannya pada perangkat yang menjalankan Android 6.0 (API level 23) dan yang lebih baru
memerlukan
karena perubahan model izin akses di versi Android tersebut, terutama
READ_EXTERNAL_STORAGE
menjadi
izin berbahaya, yang mungkin tidak dimiliki oleh aplikasi penerima.
Dengan memperhatikan pertimbangan ini, sebaiknya
Anda menghindari penggunaan
Uri.fromFile()
, yang
memiliki beberapa kelemahan. Metode ini:
- Tidak mengizinkan berbagi file di seluruh profil.
- Mengharuskan aplikasi memiliki
WRITE_EXTERNAL_STORAGE
di perangkat yang menjalankan Android 4.4 (API level 19) atau yang lebih rendah. - Mengharuskan aplikasi penerima memiliki
READ_EXTERNAL_STORAGE
, yang akan gagal pada target berbagi penting, seperti Gmail, yang tidak memiliki izin tersebut.
Sebagai ganti menggunakan Uri.fromFile()
,
Anda dapat menggunakan izin URI untuk memberikan aplikasi lain
akses ke URI tertentu. Meskipun izin URI tidak berfungsi di URI file://
yang dihasilkan oleh Uri.fromFile()
, model ini
pekerjaan pada URI yang terkait dengan Penyedia Konten. Tujuan
FileProvider
API dapat
membantu Anda membuat URI tersebut. Pendekatan ini juga berfungsi
dengan file yang tidak
di penyimpanan eksternal, tetapi di penyimpanan lokal aplikasi yang mengirimkan intent.
Di onItemClick()
, dapatkan
Objek File
untuk nama file dari file yang dipilih dan teruskan sebagai argumen ke
getUriForFile()
, beserta
otoritas yang Anda tentukan dalam
Elemen <provider>
untuk FileProvider
.
URI konten yang dihasilkan berisi otoritas, segmen jalur yang sesuai dengan
(seperti yang ditetapkan dalam meta-data XML), dan nama file termasuk
. Cara FileProvider
memetakan direktori ke jalur
segmen berdasarkan metadata XML dijelaskan dalam bagian
Menentukan direktori yang dapat dibagikan.
Cuplikan berikut menunjukkan cara mendeteksi file terpilih dan mendapatkan URI konten untuknya:
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()); } ... } }); ... }
Ingat bahwa Anda hanya dapat membuat URI konten untuk file yang berada di direktori
yang telah Anda tentukan di file meta-data yang berisi elemen <paths>
, seperti
dijelaskan di bagian Menentukan direktori yang dapat dibagikan. Jika Anda memanggil
getUriForFile()
untuk
File
di jalur yang belum Anda tentukan, Anda akan menerima
IllegalArgumentException
.
Memberikan izin untuk file
Setelah memiliki URI konten untuk file yang ingin dibagikan dengan aplikasi lain, Anda harus
izinkan aplikasi klien untuk mengakses file. Untuk mengizinkan akses, berikan izin ke aplikasi klien dengan
menambahkan URI konten ke Intent
, lalu menetapkan tanda izin di
Intent
. Izin yang Anda berikan bersifat sementara dan tidak berlaku lagi
secara otomatis saat tumpukan tugas aplikasi penerima selesai.
Cuplikan kode berikut menunjukkan cara menyetel izin baca untuk file:
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); } ... } ... }); ... }
Perhatian: Memanggil setFlags()
adalah satu-satunya
cara untuk memberikan akses ke file
Anda dengan aman menggunakan izin akses sementara. Hindari menelepon
Metode Context.grantUriPermission()
untuk
URI konten file, karena metode ini memberikan akses yang hanya dapat Anda cabut dengan
memanggil Context.revokeUriPermission()
.
Jangan gunakan Uri.fromFile()
. Metode ini memaksa aplikasi penerima
untuk memiliki izin READ_EXTERNAL_STORAGE
,
tidak akan berfungsi sama sekali jika Anda mencoba
membagikan seluruh pengguna, dan dalam versi
Android yang lebih rendah dari 4.4 (level API 19), akan memerlukan
aplikasi untuk memiliki WRITE_EXTERNAL_STORAGE
.
Dan target berbagi yang sangat penting, seperti aplikasi Gmail, tidak memiliki
READ_EXTERNAL_STORAGE
, yang menyebabkan
panggilan ini gagal.
Sebagai gantinya, Anda dapat menggunakan izin URI untuk memberi aplikasi lain akses ke URI tertentu.
Meskipun izin URI tidak berfungsi di URI file:// seperti yang dihasilkan oleh
Uri.fromFile()
, memang begitu
menangani URI yang terkait dengan Penyedia Konten. Daripada mengimplementasikannya
sendiri hanya untuk ini,
Anda dapat dan harus menggunakan FileProvider
seperti yang dijelaskan dalam Berbagi file.
Membagikan file dengan aplikasi yang meminta
Untuk membagikan file ke aplikasi yang memintanya, teruskan Intent
yang berisi URI konten dan izin untuk setResult()
. Saat Activity
yang baru saja Anda tentukan selesai,
sistem akan mengirimkan Intent
yang berisi URI konten ke aplikasi klien.
Cuplikan kode berikut menunjukkan cara melakukannya:
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); } } });
Sediakan cara agar pengguna dapat langsung kembali ke aplikasi klien setelah memilih file.
Salah satu cara untuk melakukan hal ini adalah dengan memberi tanda centang atau tombol Selesai. Mengaitkan metode dengan
tombol menggunakan
Atribut android:onClick
. Dalam metode ini, panggil
finish()
. Contoh:
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(); }
Untuk informasi terkait lainnya, lihat: