גישה לקבצים ספציפיים לאפליקציה

במקרים רבים, האפליקציה יוצרת קבצים שאפליקציות אחרות לא צריכות גישה אליהם, או שאין להן גישה אליהם. המערכת מספקת את המיקומים הבאים לאחסון קובצי אפליקציה ספציפיים:

  • ספריות אחסון פנימי: הספריות האלה כוללות מיקום ייעודי לאחסון קבצים קבועים, ומיקום אחר לאחסון נתוני מטמון. המערכת מונעת מאפליקציות אחרות לגשת למיקומים האלה, וב-Android 10 (רמת API 29) ואילך המיקומים האלה מוצפנים. המאפיינים האלה הופכים את המיקומים האלה למקום טוב לאחסון מידע רגיש שרק לאפליקציה עצמה יש גישה אליו.

  • ספריות אחסון חיצוניות: הספריות האלה כוללות גם מיקום ייעודי לאחסון קבצים קבועים וגם מיקום אחר לאחסון נתוני מטמון. אמנם אפליקציה אחרת יכולה לגשת לספריות האלה אם יש לה את ההרשאות המתאימות, אבל הקבצים שמאוחסנים בספריות האלה מיועדים לשימוש רק באפליקציה שלכם. אם אתם מתכוונים ליצור קבצים שיהיו לאפליקציות אחרות גישה אליהם, עליכם לאחסן את הקבצים האלה בחלק של אחסון משותף באחסון החיצוני.

כשהמשתמש מסיר את האפליקציה, הקבצים שנשמרו באחסון הייעודי לאפליקציה יוסרו. בגלל ההתנהגות הזו, לא מומלץ להשתמש באחסון הזה כדי לשמור פריטים שהמשתמשים מצפים שיישמרו בנפרד מהאפליקציה. לדוגמה, אם האפליקציה מאפשרת למשתמשים לצלם תמונות, הם מצפים שתהיה להם גישה לתמונות האלה גם אחרי שהם מסירים את האפליקציה. לכן, במקום זאת, כדאי להשתמש באחסון המשותף כדי לשמור את סוגי הקבצים האלה באוסף המדיה המתאים.

בקטעים הבאים מוסבר איך לאחסן קבצים בספריות ספציפיות לאפליקציה ולגשת אליהם.

גישה מהאחסון הפנימי

לכל אפליקציה, המערכת מספקת ספריות באחסון הפנימי שבהן האפליקציה יכולה לארגן את הקבצים שלה. תיקייה אחת מיועדת לקבצים הקבועים של האפליקציה, והשנייה מכילה את הקבצים ששמורים במטמון של האפליקציה. לאפליקציה לא נדרשות הרשאות מערכת כדי לקרוא ולכתוב בקבצים בתיקיות האלה.

לאפליקציות אחרות אין גישה לקבצים שמאוחסנים באחסון הפנימי. לכן, האחסון הפנימי הוא מקום טוב לשמירת נתוני אפליקציות שאפליקציות אחרות לא צריכות לגשת אליהם.

עם זאת, חשוב לזכור שהספריות האלה בדרך כלל קטנות. לפני שמזינים קבצים ספציפיים לאפליקציה באחסון הפנימי, האפליקציה צריכה לשלוח שאילתה לגבי נפח האחסון הפנוי במכשיר.

גישה לקבצים קבועים

הקבצים הקבועים הרגילים של האפליקציה נמצאים בספרייה שאפשר לגשת אליה באמצעות המאפיין filesDir של אובייקט ההקשר. המסגרת מספקת כמה שיטות שיעזרו לכם לגשת לקבצים בספרייה הזו ולאחסן אותם.

גישה לאחסון קבצים

אפשר להשתמש ב-API של File כדי לגשת לקבצים ולאחסן אותם.

כדי לשמור על ביצועי האפליקציה, אל תפתחו ותפתחו את אותו הקובץ כמה פעמים.

קטע הקוד הבא מדגים איך משתמשים ב-API של File:

Kotlin

val file = File(context.filesDir, filename)

Java

File file = new File(context.getFilesDir(), filename);

אחסון קובץ באמצעות סטרימינג

לחלופין, אפשר להפעיל את openFileOutput() כדי לקבל FileOutputStream שכותב לקובץ בספרייה filesDir.File

בקטע הקוד הבא מוצג איך לכתוב טקסט בקובץ:

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 מוחקת קובצי מטמון בעצמה, אבל אל תסמכו עליה שתנקה את הקבצים האלה בשבילכם. תמיד צריך לשמור את קובצי המטמון של האפליקציה באחסון הפנימי.

כדי להסיר קובץ מהספרייה של המטמון באחסון הפנימי, משתמשים באחת מהשיטות הבאות:

  • השיטה 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);
}

יצירת פעילות לניהול אחסון

האפליקציה יכולה להצהיר על פעילות מותאמת אישית וליצור אותה. כשהפעילות הזו מופעלת, היא מאפשרת למשתמש לנהל את הנתונים שהאפליקציה שמרה במכשיר של המשתמש. מגדירים את הפעילות בהתאמה אישית 'ניהול מרחב' באמצעות המאפיין android:manageSpaceActivity בקובץ המניפסט. אפליקציות ניהול קבצים יכולות להפעיל את הפעילות הזו גם אם האפליקציה לא מייצאת את הפעילות. כלומר, אם הפעילות מגדירה את הערך של android:exported לערך false.

לבקש מהמשתמש להסיר קבצים מסוימים מהמכשיר

כדי לבקש מהמשתמש לבחור קבצים במכשיר להסרה, מפעילים כוונה שכוללת את הפעולה ACTION_MANAGE_STORAGE. כאשר המשתמש מבטא את הכוונה הזו, מוצגת לו הנחיה. אם רוצים, אפשר להציג בהודעה הזו את נפח האחסון הפנוי במכשיר. כדי להציג את המידע הזה בצורה ידידותית למשתמש, משתמשים בתוצאה של החישוב הבא:

StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()

לבקש מהמשתמש להסיר את כל קובצי המטמון

לחלופין, אפשר לבקש מהמשתמש לנקות את קובצי המטמון מכל האפליקציות במכשיר. כדי לעשות זאת, מפעילים כוונה שכוללת את פעולת הכוונה ACTION_CLEAR_APP_CACHE.

מקורות מידע נוספים

למידע נוסף על שמירת קבצים באחסון של המכשיר, תוכלו לעיין במקורות המידע הבאים.

סרטונים