במקרים רבים, האפליקציה יוצרת קבצים שאפליקציות אחרות לא צריכות לגשת אליהם, או שלא אמורות לגשת אליהם. המערכת מספקת את המיקומים הבאים לאחסון קבצים ספציפיים לאפליקציה:
ספריות אחסון פנימיות: הספריות האלה כוללות מיקום ייעודי לאחסון קבצים קבועים, ומיקום אחר לאחסון נתונים במטמון. המערכת מונעת מאפליקציות אחרות לגשת למיקומים האלה, וב-Android 10 (רמת API 29) ומעלה, המיקומים האלה מוצפנים. המאפיינים האלה הופכים את המיקומים האלה למקום טוב לאחסון נתונים רגישים שרק לאפליקציה עצמה יש גישה אליהם.
ספריות אחסון חיצוניות: הספריות האלה כוללות מיקום ייעודי לאחסון קבצים קבועים ומיקום נוסף לאחסון נתונים במטמון. אפליקציות אחרות יכולות לגשת לספריות האלה אם יש להן את ההרשאות המתאימות, אבל הקבצים שמאוחסנים בספריות האלה מיועדים לשימוש רק על ידי האפליקציה שלכם. אם אתם מתכוונים ליצור קבצים שאפליקציות אחרות יוכלו לגשת אליהם, האפליקציה שלכם צריכה לאחסן את הקבצים האלה בחלק של נפח האחסון המשותף באחסון החיצוני.
כשהמשתמש מסיר את האפליקציה, הקבצים שנשמרו באחסון הספציפי לאפליקציה נמחקים. בגלל ההתנהגות הזו, אל תשתמשו באחסון הזה כדי לשמור דברים שהמשתמשים מצפים שיישארו גם בלי האפליקציה. לדוגמה, אם האפליקציה מאפשרת למשתמשים לצלם תמונות, המשתמשים מצפים שהם יוכלו לגשת לתמונות האלה גם אחרי שהם מסירים את האפליקציה. לכן, כדאי להשתמש בנפח אחסון משותף כדי לשמור את סוגי הקבצים האלה באוסף המדיה המתאים.
בקטעים הבאים מוסבר איך לאחסן קבצים בספריות ספציפיות לאפליקציה ואיך לגשת אליהם.
גישה מהאחסון הפנימי
לכל אפליקציה, המערכת מספקת ספריות בתוך האחסון הפנימי שבהן האפליקציה יכולה לארגן את הקבצים שלה. תיקייה אחת מיועדת לקבצים הקבועים של האפליקציה, ותיקייה אחרת מכילה את הקבצים שנשמרו במטמון של האפליקציה. האפליקציה לא צריכה הרשאות מערכת כדי לקרוא קבצים בספריות האלה ולכתוב בהן.
אפליקציות אחרות לא יכולות לגשת לקבצים שמאוחסנים באחסון הפנימי. לכן, האחסון הפנימי הוא מקום טוב לנתוני אפליקציות שאפליקציות אחרות לא צריכות לגשת אליהם.
עם זאת, חשוב לזכור שהמדריכים האלה בדרך כלל קטנים. לפני שכותבים קבצים ספציפיים לאפליקציה באחסון הפנימי, האפליקציה צריכה לבדוק את נפח האחסון הפנוי במכשיר.
גישה לקבצים קבועים
הקבצים הרגילים והקבועים של האפליקציה נמצאים בספרייה שאפשר לגשת אליה באמצעות המאפיין filesDir של אובייקט הקשר. ה-framework מספק כמה שיטות שעוזרות לגשת לקבצים בספרייה הזו ולאחסן אותם.
גישה לקבצים ואחסון שלהם
אתם יכולים להשתמש ב-API של File כדי לגשת לקבצים ולאחסן אותם.
כדי לשמור על ביצועי האפליקציה, אל תפתחו ותסגרו את אותו קובץ כמה פעמים.
בקטע הקוד הבא אפשר לראות איך משתמשים ב-API של File:
Kotlin
val file = File(context.filesDir, filename)
Java
File file = new File(context.getFilesDir(), filename);
אחסון קובץ באמצעות סטרימינג
במקום להשתמש ב-API של File, אתם יכולים להפעיל את 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, או על ידי העברת תיקיית השורש ושם תיקייה חדש ל-constructor של 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 של אובייקט הקשר ו-API של File:
Kotlin
val cacheFile = File(context.cacheDir, filename)
Java
File cacheFile = new File(context.getCacheDir(), filename);
הסרת קובצי מטמון
למרות שמערכת Android מוחקת לפעמים קובצי מטמון בעצמה, אל תסתמכו על המערכת שתנקה את הקבצים האלה בשבילכם. תמיד צריך לשמור את קובצי המטמון של האפליקציה באחסון הפנימי.
כדי להסיר קובץ מספריית המטמון באחסון הפנימי, משתמשים באחת מהשיטות הבאות:
ה-method
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);
הסרת קובצי מטמון
כדי להסיר קובץ מספריית המטמון החיצונית, משתמשים ב-method 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); }
יצירת פעילות לניהול האחסון
האפליקציה יכולה להצהיר על פעילות בהתאמה אישית וליצור אותה. כשהפעילות הזו מופעלת, המשתמש יכול לנהל את הנתונים שהאפליקציה שומרת במכשיר שלו. כדי להצהיר על הפעילות המותאמת אישית של "ניהול שטח האחסון", משתמשים במאפיין android:manageSpaceActivity בקובץ המניפסט. אפליקציות של מנהלי קבצים יכולות להפעיל את הפעילות הזו גם כשהאפליקציה לא מייצאת את הפעילות, כלומר כשהפעילות מגדירה את android:exported כ-false.
מבקשים מהמשתמש להסיר כמה קבצים מהמכשיר
כדי לבקש מהמשתמש לבחור קבצים במכשיר להסרה, צריך להפעיל Intent שכולל את הפעולה ACTION_MANAGE_STORAGE. ה-intent הזה מציג למשתמש בקשה. אם רוצים, אפשר להציג בהודעה הזו את כמות המקום הפנוי במכשיר. כדי להציג את המידע הזה בצורה ידידותית למשתמש, משתמשים בתוצאה של החישוב הבא:
StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()
מבקשים מהמשתמש להסיר את כל קובצי המטמון
לחלופין, אפשר לבקש מהמשתמש לנקות את קובצי המטמון מכל האפליקציות במכשיר. כדי לעשות את זה, מפעילים Intent שכולל את פעולת ה-Intent ACTION_CLEAR_APP_CACHE.
מקורות מידע נוספים
למידע נוסף על שמירת קבצים באחסון של המכשיר, עיינו במקורות המידע הבאים.