במקרים רבים, האפליקציה יוצרת קבצים שאפליקציות אחרות לא צריכות לגשת אליהם, או שלא אמורות לגשת אליהם. המערכת מספקת את המיקומים הבאים לאחסון קבצים ספציפיים לאפליקציה:
ספריות של אחסון פנימי: הספריות האלה כוללות מיקום ייעודי לאחסון קבצים קבועים ומיקום אחר לאחסון נתונים במטמון. המערכת מונעת מאפליקציות אחרות לגשת למיקומים האלה, וב-Android 10 (רמת API 29) ומעלה, המיקומים האלה מוצפנים. המאפיינים האלה הופכים את המיקומים האלה למקום טוב לאחסון נתונים רגישים שרק האפליקציה עצמה יכולה לגשת אליהם.
ספריות אחסון חיצוניות: הספריות האלה כוללות מיקום ייעודי לאחסון קבצים קבועים ומיקום אחר לאחסון נתונים במטמון. אף על פי שאפליקציה אחרת יכולה לגשת לספריות האלה אם יש לה את ההרשאות המתאימות, הקבצים שמאוחסנים בספריות האלה מיועדים לשימוש רק באפליקציה שלכם. אם אתם רוצים ליצור קבצים שאפליקציות אחרות יוכלו לגשת אליהם, האפליקציה שלכם צריכה לאחסן את הקבצים האלה בחלק של האחסון המשותף באחסון החיצוני.
כשהמשתמש מסיר את האפליקציה, הקבצים שנשמרו באחסון הספציפי לאפליקציה מוסרים. בגלל ההתנהגות הזו, לא כדאי להשתמש באחסון הזה כדי לשמור פריטים שהמשתמשים מצפים שיישארו גם אם האפליקציה שלכם לא מותקנת. לדוגמה, אם האפליקציה מאפשרת למשתמשים לצלם תמונות, הם מצפים שיוכלו לגשת לתמונות האלה גם אחרי שהם מסירים את האפליקציה. לכן, במקום זאת, כדאי להשתמש באחסון משותף כדי לשמור את סוגי הקבצים האלה באוסף המדיה המתאים.
בקטעים הבאים מוסבר איך לאחסן קבצים בספריות ספציפיות לאפליקציות ולגשת אליהם.
גישה מאחסון פנימי
לכל אפליקציה, המערכת מספקת ספריות באחסון הפנימי שבהן האפליקציה יכולה לארגן את הקבצים שלה. תיקייה אחת מיועדת לקבצים קבועים של האפליקציה, ותיקייה אחרת מכילה קבצים במטמון של האפליקציה. האפליקציה לא דורשת הרשאות מערכת כדי לקרוא ולכתוב קבצים בספריות האלה.
לאפליקציות אחרות אין גישה לקבצים שמאוחסנים באחסון הפנימי. לכן, האחסון הפנימי הוא מקום טוב לנתוני אפליקציות שאפליקציות אחרות לא אמורות לגשת אליהם.
עם זאת, חשוב לזכור שהספריות האלה בדרך כלל קטנות. לפני שכותבים קבצים ספציפיים לאפליקציה באחסון הפנימי, האפליקציה צריכה לשאול לגבי השטח הפנוי במכשיר.
גישה לקבצים מתמשכים
הקבצים הרגילים והקבועים של האפליקציה נמצאים בספרייה שאפשר לגשת אליה באמצעות המאפיין filesDir
של אובייקט הקשר. המסגרת מספקת כמה שיטות שיעזרו לכם לגשת לקבצים בספרייה הזו ולאחסן אותם.
גישה לקבצים ואחסון שלהם
אפשר להשתמש ב-File
API כדי לגשת לקבצים ולאחסן אותם.
כדי לשמור על הביצועים של האפליקציה, לא מומלץ לפתוח ולסגור את אותו קובץ כמה פעמים.
קטע הקוד הבא מדגים איך להשתמש ב-File
API:
Kotlin
val file = File(context.filesDir, filename)
Java
File file = new File(context.getFilesDir(), filename);
אחסון קובץ באמצעות סטרימינג
במקום להשתמש ב-File
API, אפשר לקרוא ל-openFileOutput()
כדי לקבל FileOutputStream
שכותב לקובץ בספרייה filesDir
.
בקטע הקוד הבא מוצג איך לכתוב טקסט בקובץ:
Kotlin
val filename = "myfile" val fileContents = "Hello world!" context.openFileOutput(filename, Context.MODE_PRIVATE).use { it.write(fileContents.toByteArray()) }
Java
String filename = "myfile"; String fileContents = "Hello world!"; try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) { fos.write(fileContents.toByteArray()); }
כדי לאפשר לאפליקציות אחרות לגשת לקבצים שמאוחסנים בספרייה הזו באחסון הפנימי, צריך להשתמש ב-FileProvider
עם המאפיין FLAG_GRANT_READ_URI_PERMISSION
.
גישה לקובץ באמצעות סטרימינג
כדי לקרוא קובץ כסטרימינג, משתמשים בפקודה openFileInput()
:
Kotlin
context.openFileInput(filename).bufferedReader().useLines { lines -> lines.fold("") { some, text -> "$some\n$text" } }
Java
FileInputStream fis = context.openFileInput(filename); InputStreamReader inputStreamReader = new InputStreamReader(fis, StandardCharsets.UTF_8); StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(inputStreamReader)) { String line = reader.readLine(); while (line != null) { stringBuilder.append(line).append('\n'); line = reader.readLine(); } } catch (IOException e) { // Error occurred when opening raw file for reading. } finally { String contents = stringBuilder.toString(); }
הצגת רשימת הקבצים
כדי לקבל מערך שמכיל את השמות של כל הקבצים בספרייה filesDir
, אפשר לקרוא ל-fileList()
, כמו בקטע הקוד הבא:
Kotlin
var files: Array<String> = context.fileList()
Java
Array<String> files = context.fileList();
יצירת ספריות מקוננות
אפשר גם ליצור ספריות מקוננות או לפתוח ספרייה פנימית באמצעות קריאה ל-getDir()
בקוד מבוסס Kotlin, או באמצעות העברת ספריית הבסיס ושם של ספרייה חדשה לבונה File
בקוד מבוסס Java:
Kotlin
context.getDir(dirName, Context.MODE_PRIVATE)
Java
File directory = context.getFilesDir(); File file = new File(directory, filename);
יצירת קובצי מטמון
אם אתם צריכים לאחסן נתונים רגישים רק באופן זמני, אתם צריכים להשתמש בספריית המטמון הייעודית של האפליקציה באחסון הפנימי כדי לשמור את הנתונים. כמו בכל האחסון הספציפי לאפליקציה, הקבצים שמאוחסנים בספרייה הזו מוסרים כשהמשתמש מסיר את האפליקציה, אבל יכול להיות שהקבצים בספרייה הזו יוסרו מוקדם יותר.
כדי ליצור קובץ במטמון, קוראים ל-File.createTempFile()
:
Kotlin
File.createTempFile(filename, null, context.cacheDir)
Java
File.createTempFile(filename, null, context.getCacheDir());
האפליקציה ניגשת לקובץ בספרייה הזו באמצעות המאפיין cacheDir
של אובייקט context ו-API File
:
Kotlin
val cacheFile = File(context.cacheDir, filename)
Java
File cacheFile = new File(context.getCacheDir(), filename);
הסרת קבצים מהמטמון
למרות שמערכת Android מוחקת לפעמים קובצי מטמון בעצמה, לא כדאי להסתמך על המערכת שתנקה את הקבצים האלה בשבילכם. חשוב תמיד לשמור את קובצי המטמון של האפליקציה באחסון הפנימי.
כדי להסיר קובץ מספריית המטמון באחסון הפנימי, משתמשים באחת מהשיטות הבאות:
השיטה
delete()
באובייקטFile
שמייצג את הקובץ:Kotlin
cacheFile.delete()
Java
cacheFile.delete();
השיטה
deleteFile()
של הקשר של האפליקציה, עם העברת שם הקובץ:Kotlin
context.deleteFile(cacheFileName)
Java
context.deleteFile(cacheFileName);
גישה מאחסון חיצוני
אם האחסון הפנימי לא מספק מספיק מקום לאחסון קבצים ספציפיים לאפליקציה, כדאי להשתמש באחסון חיצוני במקום זאת. המערכת מספקת ספריות באחסון החיצוני שבהן אפליקציה יכולה לארגן קבצים שנותנים ערך למשתמש רק בתוך האפליקציה. ספרייה אחת מיועדת לקבצים מתמשכים של האפליקציה, וספרייה אחרת מכילה קבצים במטמון של האפליקציה.
ב-Android 4.4 (רמת API 19) ומעלה, האפליקציה לא צריכה לבקש הרשאות שקשורות לאחסון כדי לגשת לספריות ספציפיות לאפליקציה באחסון חיצוני. הקבצים שמאוחסנים בספריות האלה מוסרים כשמסירים את ההתקנה של האפליקציה.
במכשירים עם Android 9 (רמת API 28) או גרסה מוקדמת יותר, האפליקציה יכולה לגשת לקבצים ספציפיים לאפליקציה ששייכים לאפליקציות אחרות, בתנאי שיש לאפליקציה הרשאות אחסון מתאימות. כדי לתת למשתמשים יותר שליטה בקבצים שלהם ולהגביל את העומס בקבצים, אפליקציות שמיועדות ל-Android מגרסה 10 (רמת API 29) ומעלה מקבלות כברירת מחדל גישה מוגבלת לאחסון חיצוני, או אחסון מוגבל. כשמאפשרים אחסון בהיקף מוגבל, אפליקציות לא יכולות לגשת לספריות ספציפיות לאפליקציות ששייכות לאפליקציות אחרות.
אימות הזמינות של נפח האחסון
מכיוון שאחסון חיצוני נמצא בנפח פיזי שהמשתמש יכול להסיר, צריך לוודא שיש גישה לנפח לפני שמנסים לקרוא ממנו נתונים ספציפיים לאפליקציה או לכתוב בו נתונים ספציפיים לאפליקציה.
אפשר לשלוח שאילתה לגבי מצב אמצעי האחסון באמצעות הקריאה ל-Environment.getExternalStorageState()
.
אם המצב שמוחזר הוא MEDIA_MOUNTED
, אפשר לקרוא ולכתוב קבצים ספציפיים לאפליקציה באחסון החיצוני. אם זה MEDIA_MOUNTED_READ_ONLY
, אפשר רק לקרוא את הקבצים האלה.
לדוגמה, השיטות הבאות שימושיות כדי לקבוע את זמינות האחסון:
Kotlin
// Checks if a volume containing external storage is available // for read and write. fun isExternalStorageWritable(): Boolean { return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED } // Checks if a volume containing external storage is available to at least read. fun isExternalStorageReadable(): Boolean { return Environment.getExternalStorageState() in setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY) }
Java
// Checks if a volume containing external storage is available // for read and write. private boolean isExternalStorageWritable() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } // Checks if a volume containing external storage is available to at least read. private boolean isExternalStorageReadable() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) || Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY); }
במכשירים ללא אחסון חיצוני נשלף, אפשר להשתמש בפקודה הבאה כדי להפעיל נפח וירטואלי לבדיקת הלוגיקה של זמינות האחסון החיצוני:
adb shell sm set-virtual-disk true
בחירת מיקום פיזי לאחסון
לפעמים, במכשיר שמקצה מחיצה בזיכרון הפנימי שלו כאחסון חיצוני יש גם חריץ לכרטיס SD. כלומר, למכשיר יש כמה נפחים פיזיים שיכולים להכיל אחסון חיצוני, ולכן צריך לבחור באיזה מהם להשתמש לאחסון הייעודי לאפליקציה.
כדי לגשת למיקומים השונים, מתקשרים למספר ContextCompat.getExternalFilesDirs()
.
כפי שמוצג בקטע הקוד, הרכיב הראשון במערך המוחזר נחשב לנפח האחסון החיצוני הראשי. משתמשים בעוצמת הקול הזו אלא אם היא מלאה או לא זמינה.
Kotlin
val externalStorageVolumes: Array<out File> = ContextCompat.getExternalFilesDirs(applicationContext, null) val primaryExternalStorage = externalStorageVolumes[0]
Java
File[] externalStorageVolumes = ContextCompat.getExternalFilesDirs(getApplicationContext(), null); File primaryExternalStorage = externalStorageVolumes[0];
גישה לקבצים מתמשכים
כדי לגשת לקבצים ספציפיים לאפליקציה מאחסון חיצוני, צריך להתקשר אל
getExternalFilesDir()
.
כדי לשמור על הביצועים של האפליקציה, לא מומלץ לפתוח ולסגור את אותו קובץ כמה פעמים.
קטע הקוד הבא מדגים איך להפעיל את getExternalFilesDir()
:
Kotlin
val appSpecificExternalDir = File(context.getExternalFilesDir(null), filename)
Java
File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);
יצירת קובצי מטמון
כדי להוסיף קובץ ספציפי לאפליקציה למטמון באחסון החיצוני, צריך לקבל הפניה אל externalCacheDir
:
Kotlin
val externalCacheFile = File(context.externalCacheDir, filename)
Java
File externalCacheFile = new File(context.getExternalCacheDir(), filename);
הסרת קבצים מהמטמון
כדי להסיר קובץ מספריית המטמון החיצונית, משתמשים בשיטה delete()
באובייקט File
שמייצג את הקובץ:
Kotlin
externalCacheFile.delete()
Java
externalCacheFile.delete();
תוכן מדיה
אם האפליקציה שלכם פועלת עם קובצי מדיה שמספקים ערך למשתמש רק בתוך האפליקציה, מומלץ לאחסן אותם בספריות ספציפיות לאפליקציה באחסון חיצוני, כמו שמוצג בקטע הקוד הבא:
Kotlin
fun getAppSpecificAlbumStorageDir(context: Context, albumName: String): File? { // Get the pictures directory that's inside the app-specific directory on // external storage. val file = File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName) if (!file?.mkdirs()) { Log.e(LOG_TAG, "Directory not created") } return file }
Java
@Nullable File getAppSpecificAlbumStorageDir(Context context, String albumName) { // Get the pictures directory that's inside the app-specific directory on // external storage. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (file == null || !file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
חשוב להשתמש בשמות ספריות שסופקו על ידי קבועים של API כמו DIRECTORY_PICTURES
.
שמות הספריות האלה מבטיחים שהמערכת תטפל בקבצים בצורה נכונה.
אם אף אחד משמות תיקיות המשנה שהוגדרו מראש לא מתאים לקבצים שלכם, אתם יכולים להעביר את null
אל getExternalFilesDir()
. הפונקציה מחזירה את ספריית השורש הספציפית לאפליקציה באחסון החיצוני.
שאילתה לגבי נפח האחסון הפנוי
למשתמשים רבים אין הרבה נפח אחסון פנוי במכשירים שלהם, ולכן האפליקציה שלכם צריכה לצרוך נפח אחסון בצורה מושכלת.
אם אתם יודעים מראש כמה נתונים אתם מאחסנים, אתם יכולים לברר כמה מקום המכשיר יכול לספק לאפליקציה שלכם באמצעות הקריאה ל-getAllocatableBytes()
.
יכול להיות שהערך המוחזר של getAllocatableBytes()
יהיה גדול יותר מכמות המקום הפנוי הנוכחית במכשיר. הסיבה לכך היא שהמערכת זיהתה קבצים שאפשר להסיר מספריות המטמון של אפליקציות אחרות.
אם יש מספיק מקום לשמירת הנתונים של האפליקציה, צריך להתקשר אל allocateBytes()
.
אחרת, האפליקציה יכולה לבקש מהמשתמש להסיר קבצים מסוימים מהמכשיר או להסיר את כל קובצי המטמון מהמכשיר.
בקטע הקוד הבא מוצגת דוגמה לאופן שבו האפליקציה יכולה לשלוח שאילתה לגבי השטח הפנוי במכשיר:
Kotlin
// App needs 10 MB within internal storage. const val NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; val storageManager = applicationContext.getSystemService<StorageManager>()!! val appSpecificInternalDirUuid: UUID = storageManager.getUuidForPath(filesDir) val availableBytes: Long = storageManager.getAllocatableBytes(appSpecificInternalDirUuid) if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { storageManager.allocateBytes( appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP) } else { val storageIntent = Intent().apply { // To request that the user remove all app cache files instead, set // "action" to ACTION_CLEAR_APP_CACHE. action = ACTION_MANAGE_STORAGE } }
Java
// App needs 10 MB within internal storage. private static final long NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; StorageManager storageManager = getApplicationContext().getSystemService(StorageManager.class); UUID appSpecificInternalDirUuid = storageManager.getUuidForPath(getFilesDir()); long availableBytes = storageManager.getAllocatableBytes(appSpecificInternalDirUuid); if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { storageManager.allocateBytes( appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP); } else { // To request that the user remove all app cache files instead, set // "action" to ACTION_CLEAR_APP_CACHE. Intent storageIntent = new Intent(); storageIntent.setAction(ACTION_MANAGE_STORAGE); }
יצירת פעילות לניהול אחסון
האפליקציה יכולה להצהיר על פעילות מותאמת אישית וליצור אותה. כשמפעילים את הפעילות הזו, המשתמש יכול לנהל את הנתונים שהאפליקציה שומרת במכשיר שלו. מגדירים את הפעילות המותאמת אישית הזו, manage space, באמצעות המאפיין android:manageSpaceActivity
בקובץ המניפסט. אפליקציות לניהול קבצים יכולות להפעיל את הפעילות הזו גם אם האפליקציה שלכם לא מייצאת את הפעילות, כלומר אם הפעילות שלכם מוגדרת android:exported
לערך false
.
בקשה מהמשתמש להסיר קבצים מהמכשיר
כדי לבקש מהמשתמש לבחור קבצים במכשיר להסרה, מפעילים intent
שכולל את הפעולה
ACTION_MANAGE_STORAGE
. הכוונה הזו מציגה למשתמש הנחיה. אם רוצים, אפשר להציג בהודעה הזו את כמות המקום הפנוי במכשיר. כדי להציג את המידע הזה בצורה ידידותית למשתמש, צריך להשתמש בתוצאה של החישוב הבא:
StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()
בקשה מהמשתמש להסיר את כל קובצי המטמון
לחלופין, אפשר לבקש מהמשתמש לנקות את קובצי המטמון מכל האפליקציות במכשיר. כדי לעשות זאת, מפעילים intent שכולל את פעולת ה-intent ACTION_CLEAR_APP_CACHE
.
מקורות מידע נוספים
מידע נוסף על שמירת קבצים באחסון של המכשיר זמין במקורות המידע הבאים.