במכשירים עם 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
מאפשרת למשתמשים לבחור מסמך או קובץ ספציפיים לפתיח. - מתן גישה לתוכן של ספרייה
- פעולת הכוונה
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
.
בקטע הקוד הבא מוסבר איך יוצרים את הכוונה לפתוח מסמך 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) ואילך, אי אפשר להשתמש בפעולת הכוונה 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 לפתיח ספרייה ומפעילים אותו:
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) ואילך, אי אפשר להשתמש בפעולת הכוונה ACTION_OPEN_DOCUMENT_TREE
כדי לבקש גישה לספריות הבאות:
- ספריית הבסיס של נפח האחסון הפנימי.
- ספריית השורש של כל נפח אחסון בכרטיס SD שנחשב על ידי יצרן המכשיר כמהימנה, ללא קשר אם הכרטיס הוא משובט או נשלף. נפח אמין הוא נפח שאפליקציה יכולה לגשת אליו בהצלחה רוב הזמן.
- הספרייה
Download
.
בנוסף, ב-Android 11 (רמת API 30) ואילך, אי אפשר להשתמש בפעולת הכוונה 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)
קטע הקוד הבא מראה איך לפתוח קובץ 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; }
אחרי שפותחים את קובץ ה-bitmap, אפשר להציג אותו ב-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.