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

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

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

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

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

StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()

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

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

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

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

סרטונים