במכשירים שמותקנת בהם גרסת Android 4.4 (API ברמה 19) ומעלה, האפליקציה יכולה ליצור אינטראקציה עם ספק מסמכים, כולל נפחי אחסון חיצוניים ואחסון מבוסס-ענן, באמצעות Storage Access Framework (מסגרת לגישה לאחסון). המסגרת הזו מאפשרת למשתמשים ליצור אינטראקציה עם כלי לבחירת מערכת כדי לבחור ספק מסמכים ולבחור מסמכים ספציפיים וקבצים אחרים שהאפליקציה שלכם יכולה ליצור, לפתוח או לשנות.
המנגנון הזה לא דורש הרשאות מערכת, כי המשתמש מעורב בבחירת הקבצים או הספריות שהאפליקציה יכולה לגשת אליהם, והוא משפר את השליטה של המשתמש ואת הפרטיות שלו. בנוסף, הקבצים האלה, שמאוחסנים מחוץ לספרייה ספציפית לאפליקציה ומחוץ למאגר המדיה, נשארים במכשיר גם אחרי שהאפליקציה מוסרת.
השימוש במסגרת כולל את השלבים הבאים:
- אפליקציה מפעילה Intent שמכיל פעולה שקשורה לאחסון. הפעולה הזו תואמת לתרחיש שימוש ספציפי שהמסגרת מספקת.
- המשתמש רואה כלי לבחירת קבצים במערכת, שמאפשר לו לעיין בספק מסמכים ולבחור מיקום או מסמך שבהם תתבצע הפעולה שקשורה לאחסון.
- האפליקציה מקבלת גישת קריאה וכתיבה ל-URI שמייצג את המיקום או המסמך שהמשתמש בחר. באמצעות ה-URI הזה, האפליקציה יכולה לבצע פעולות במיקום שנבחר.
כדי לתמוך בגישה לקובצי מדיה במכשירים עם Android 9 (רמת API 28) או גרסאות ישנות יותר, צריך להצהיר על ההרשאה READ_EXTERNAL_STORAGE
ולהגדיר את maxSdkVersion
ל-28
.
במדריך הזה מוסבר על תרחישי השימוש השונים שהמסגרת תומכת בהם לעבודה עם קבצים ומסמכים אחרים. היא גם מסבירה איך לבצע פעולות במיקום שנבחר על ידי המשתמש.
תרחישי שימוש לגישה למסמכים ולקבצים אחרים
ה-Storage Access Framework תומך בתרחישי השימוש הבאים לגישה לקבצים ולמסמכים אחרים.
- איך יוצרים קובץ חדש
- פעולת הכוונה
ACTION_CREATE_DOCUMENT
מאפשרת למשתמשים לשמור קובץ במיקום ספציפי. - פתיחת מסמך או קובץ
- פעולת היעד
ACTION_OPEN_DOCUMENT
מאפשרת למשתמשים לבחור מסמך או קובץ ספציפיים לפתיחה. - מתן גישה לתוכן של ספרייה
- פעולת ה-intent
ACTION_OPEN_DOCUMENT_TREE
, שזמינה ב-Android 5.0 (רמת API 21) ומעלה, מאפשרת למשתמשים לבחור ספרייה ספציפית, וכך להעניק לאפליקציה שלכם גישה לכל הקבצים ולכל ספריות המשנה בתוך הספרייה הזו.
בקטעים הבאים מפורט תהליך ההגדרה, בהתאם לתרחיש לדוגמה הרלוונטי.
יצירת קובץ חדש
משתמשים בפעולת הכוונה
ACTION_CREATE_DOCUMENT
כדי לטעון את בורר הקבצים של המערכת ולאפשר למשתמש לבחור מיקום לכתיבת התוכן של קובץ. התהליך הזה דומה לזה שמשמש בתיבות הדו-שיח 'שמירה בשם' במערכות הפעלה אחרות.
הערה: אי אפשר להשתמש ב-ACTION_CREATE_DOCUMENT
כדי להחליף קובץ קיים. אם האפליקציה מנסה לשמור קובץ עם אותו שם, המערכת מוסיפה מספר בסוגריים בסוף שם הקובץ.
לדוגמה, אם האפליקציה מנסה לשמור קובץ בשם confirmation.pdf
בספרייה שכבר יש בה קובץ בשם הזה, המערכת שומרת את הקובץ החדש בשם confirmation(1).pdf
.
כשמגדירים את הכוונה, מציינים את השם ואת סוג ה-MIME של הקובץ, ואפשר גם לציין את ה-URI של הקובץ או של הספרייה שיופיעו בבוחר הקבצים כשהוא נטען לראשונה באמצעות התוסף EXTRA_INITIAL_URI
של הכוונה.
בקטע הקוד הבא מוצג איך ליצור את הכוונה ליצירת קובץ ולהפעיל אותה:
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); }
פתיחת קובץ
יכול להיות שהאפליקציה שלכם משתמשת במסמכים כיחידת אחסון שבה המשתמשים מזינים נתונים שהם רוצים לשתף עם עמיתים או לייבא למסמכים אחרים. לדוגמה, משתמש פותח מסמך פרודוקטיביות או ספר שנשמר כקובץ EPUB.
במקרים כאלה, צריך לאפשר למשתמש לבחור את הקובץ לפתיחה באמצעות הפעלת ה-intent ACTION_OPEN_DOCUMENT
, שפותח את אפליקציית בחירת הקבצים של המערכת. כדי להציג רק את סוגי הקבצים שהאפליקציה תומכת בהם, צריך לציין סוג MIME. בנוסף, אפשר לציין את ה-URI של הקובץ שיוצג בבוחר הקבצים כשהוא נטען בפעם הראשונה באמצעות התוסף EXTRA_INITIAL_URI
של Intent.
בקטע הקוד הבא מוצג אופן היצירה של Intent לפתיחת מסמך PDF והפעלתו:
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); }
הגבלות גישה
ב-Android 11 (רמת API 30) ומעלה, אי אפשר להשתמש בפעולת intent ACTION_OPEN_DOCUMENT
כדי לבקש מהמשתמש לבחור קבצים בודדים מהספריות הבאות:
- הספרייה
Android/data/
וכל ספריות המשנה שלה. - הספרייה
Android/obb/
וכל ספריות המשנה שלה.
מתן גישה לתוכן של ספרייה
אפליקציות לניהול קבצים וליצירת מדיה בדרך כלל מנהלות קבוצות של קבצים בהיררכיית ספריות. כדי לספק את היכולת הזו באפליקציה, צריך להשתמש בפעולת ה-Intent ACTION_OPEN_DOCUMENT_TREE
, שמאפשרת למשתמש להעניק גישה לעץ שלם של ספריות, עם כמה חריגים החל מ-Android 11 (רמת API 30). לאחר מכן, האפליקציה יכולה לגשת לכל קובץ בספרייה שנבחרה ולכל אחת מספריות המשנה שלה.
כשמשתמשים ב-ACTION_OPEN_DOCUMENT_TREE
, האפליקציה מקבלת גישה רק לקבצים בספרייה שהמשתמש בוחר. אין לכם גישה לקבצים של אפליקציות אחרות שנמצאים מחוץ לספרייה הזו שנבחרה על ידי המשתמש. הגישה הזו בשליטת המשתמש מאפשרת למשתמשים לבחור בדיוק איזה תוכן הם רוצים לשתף עם האפליקציה.
אפשר גם לציין את ה-URI של הספרייה שצריכה להופיע בבוחר הקבצים כשהוא נטען בפעם הראשונה באמצעות התוסף EXTRA_INITIAL_URI
של Intent.
בקטע הקוד הבא מוצג אופן היצירה של Intent לפתיחת ספרייה והפעלתו:
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); }
הגבלות גישה
ב-Android 11 (רמת API 30) ומעלה, אי אפשר להשתמש בפעולת ה-intent ACTION_OPEN_DOCUMENT_TREE
כדי לבקש גישה לספריות הבאות:
- ספריית הבסיס של נפח האחסון הפנימי.
- ספריית הבסיס של כל נפח בכרטיס ה-SD שהיצרן של המכשיר מגדיר כמהימן, בלי קשר לשאלה אם הכרטיס וירטואלי או נשלף. נפח אמין הוא נפח שאפליקציה יכולה לגשת אליו בהצלחה ברוב המקרים.
- הספרייה
Download
.
בנוסף, ב-Android 11 (רמת API 30) ומעלה, אי אפשר להשתמש בפעולת ה-intent ACTION_OPEN_DOCUMENT_TREE
כדי לבקש מהמשתמש לבחור קבצים בודדים מהספריות הבאות:
- הספרייה
Android/data/
וכל ספריות המשנה שלה. - הספרייה
Android/obb/
וכל ספריות המשנה שלה.
ביצוע פעולות במיקום שנבחר
אחרי שהמשתמש בוחר קובץ או תיקייה באמצעות הכלי לבחירת קבצים של המערכת,
אפשר לאחזר את ה-URI של הפריט שנבחר באמצעות הקוד הבא ב-onActivityResult()
:
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. } } }
באמצעות קבלת הפניה ל-URI של הפריט שנבחר, האפליקציה יכולה לבצע מספר פעולות על הפריט. לדוגמה, אתם יכולים לגשת למטא-נתונים של הפריט, לערוך את הפריט במקום ולמחוק אותו.
בקטעים הבאים מוסבר איך לבצע פעולות בקבצים שהמשתמש בוחר.
איך קובעים אילו פעולות ספק תומך בהן
ספקי תוכן שונים מאפשרים לבצע פעולות שונות במסמכים, כמו העתקת המסמך או הצגת התמונה הממוזערת של המסמך. כדי לדעת אילו פעולות ספק מסוים תומך בהן, בודקים את הערך של Document.COLUMN_FLAGS
.
לאחר מכן, בממשק המשתמש של האפליקציה יוצגו רק האפשרויות שהספק תומך בהן.
שמירת הרשאות
כשהאפליקציה פותחת קובץ לקריאה או לכתיבה, המערכת מעניקה לאפליקציה הרשאת URI לקובץ הזה, שתוקפה נמשך עד להפעלה מחדש של המכשיר של המשתמש. אבל נניח שהאפליקציה שלכם היא אפליקציה לעריכת תמונות, ואתם רוצים שהמשתמשים יוכלו לגשת ישירות מהאפליקציה ל-5 התמונות שהם ערכו לאחרונה. אם המכשיר של המשתמש הופעל מחדש, תצטרכו להפנות את המשתמש בחזרה לכלי לבחירת תמונות כדי למצוא את הקבצים.
כדי לשמור על הגישה לקבצים גם אחרי הפעלה מחדש של המכשיר וליצור חוויית משתמש טובה יותר, האפליקציה יכולה לקבל את מענק ההרשאה ל-URI שניתן לשמירה שהמערכת מציעה, כמו שמוצג בקטע הקוד הבא:
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);
בדיקת המטא-נתונים של המסמך
כשמקבלים את ה-URI של מסמך, מקבלים גישה למטא-נתונים שלו. בקטע הקוד הזה, המטא-נתונים של מסמך שצוין על ידי ה-URI נלקחים ונרשמים ביומן:
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(); } }
פתיחת מסמך
אם יש לכם הפניה ל-URI של מסמך, אתם יכולים לפתוח את המסמך כדי לבצע בו עיבוד נוסף. בקטע הזה מוצגות דוגמאות לפתיחה של מפת סיביות ושל זרם קלט.
מפת סיביות (bitmap)
בקטע הקוד הבא מוצג איך לפתוח קובץ Bitmap
לפי ה-URI שלו:
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; }
אחרי שפותחים את מפת הביטים, אפשר להציג אותה ב-ImageView
.
שידור קלט
בקטע הקוד הבא מוצג איך לפתוח אובייקט InputStream בהינתן ה-URI שלו. בקטע הקוד הזה, השורות של הקובץ נקראות לתוך מחרוזת:
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(); }
עריכת מסמך
אתם יכולים להשתמש ב-Storage Access Framework כדי לערוך מסמך טקסט במקום.
קטע הקוד הבא מחליף את התוכן של המסמך שמיוצג על ידי ה-URI שצוין:
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(); } }
מחיקת מסמך
אם יש לכם את ה-URI של מסמך והמסמך Document.COLUMN_FLAGS
מכיל SUPPORTS_DELETE
, אתם יכולים למחוק את המסמך. לדוגמה:
Kotlin
DocumentsContract.deleteDocument(applicationContext.contentResolver, uri)
Java
DocumentsContract.deleteDocument(applicationContext.contentResolver, uri);
אחזור URI מקביל של מדיה
השיטה
getMediaUri()
מספקת URI של חנות מדיה ששווה ל-URI של ספק המסמכים שצוין. שני מזהי ה-URI מפנים לאותו פריט בסיסי. באמצעות ה-URI של אחסון קובצי המדיה, קל יותר לגשת לקובצי מדיה מנפח אחסון משותף.
השיטה getMediaUri()
תומכת בכתובות URI מסוג ExternalStorageProvider
. ב-Android 12 (רמת API 31) ואילך, השיטה תומכת גם בכתובות URI של MediaDocumentsProvider
.
פתיחת קובץ וירטואלי
ב-Android 7.0 (API ברמה 25) ומעלה, האפליקציה יכולה להשתמש בקבצים וירטואליים שזמינים באמצעות Storage Access Framework. למרות שלקבצים וירטואליים אין ייצוג בינארי, האפליקציה יכולה לפתוח את התוכן שלהם על ידי המרה שלהם לסוג קובץ אחר או על ידי הצגת הקבצים האלה באמצעות פעולת הכוונה ACTION_VIEW
.
כדי לפתוח קבצים וירטואליים, אפליקציית הלקוח צריכה לכלול לוגיקה מיוחדת לטיפול בהם. אם רוצים לקבל ייצוג של הקובץ בבייט – כדי להציג את הקובץ בתצוגה מקדימה, למשל – צריך לבקש מספק המסמכים סוג MIME חלופי.
אחרי שהמשתמש בוחר, משתמשים ב-URI בנתוני התוצאות כדי לקבוע אם הקובץ וירטואלי, כמו שמוצג בקטע הקוד הבא:
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; }
אחרי שמוודאים שהמסמך הוא קובץ וירטואלי, אפשר להמיר את הקובץ לסוג MIME חלופי, כמו "image/png"
. קטע הקוד הבא מראה איך לבדוק אם אפשר לייצג קובץ וירטואלי כתמונה, ואם כן, איך לקבל זרם קלט מהקובץ הווירטואלי:
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(); }
מקורות מידע נוספים
למידע נוסף על אחסון מסמכים וקבצים אחרים ועל גישה אליהם, אפשר לעיין במקורות המידע הבאים.
טעימות
- ActionOpenDocument, זמין ב-GitHub.
- ActionOpenDocumentTree, שזמין ב-GitHub.