الوصول إلى الملفات الخاصة بالتطبيقات

في العديد من الحالات، ينشئ تطبيقك ملفات لا تحتاج التطبيقات الأخرى إلى الوصول إليها أو لا يُفترَض أن تصل إليها. يقدّم النظام المواقع التالية لتخزين هذه الملفات الخاصة بالتطبيق:

  • أدلة مساحة التخزين الداخلية: تتضمّن هذه الأدلة كلاً من مكان مخصّص لتخزين الملفات الدائمة ومكان آخر لتخزين data المخزّنة مؤقتًا. ويمنع النظام التطبيقات الأخرى من الوصول إلى هذه المواقع الجغرافية، ويُشفَّر موقعها الجغرافي على الإصدار 10 من نظام Android (المستوى 29 من واجهة برمجة التطبيقات) والإصدارات الأحدث. تجعل هذه الخصائص من هذه المواقع مكانًا جيدًا لتخزين البيانات الحساسة التي يمكن لتطبيقك نفسه الوصول إليها فقط.

  • أدلة مساحة التخزين الخارجية: تتضمن هذه الأدلة موقعًا مخصَّصًا لتخزين الملفات الدائمة ومكانًا آخر لتخزين بيانات ذاكرة التخزين المؤقت. وعلى الرغم من أنّه بإمكان تطبيق آخر الوصول إلى هذه الأدلة إذا كان هذا التطبيق لديه الأذونات المناسبة، فإنّ الملفات المخزَّنة في هذه الأدلّة مصمَّمة للاستخدام من خلال تطبيقك فقط. إذا كنت تنوي إنشاء ملفات يجب منح التطبيقات الأخرى الإذن بالوصول إليها، يجب أن يخزِّن تطبيقك هذه الملفات في جزء مساحة التخزين المشتركة من وحدة التخزين الخارجية بدلاً من ذلك.

عندما يلغي المستخدم تثبيت تطبيقك، تتم إزالة الملفات المحفوظة في مساحة التخزين الخاصة بالتطبيق. وبسبب هذا السلوك، يجب ألّا تستخدم مساحة التخزين هذه لحفظ أي عنصر يتوقع المستخدم الاحتفاظ به بشكل مستقل عن تطبيقك. على سبيل المثال، إذا كان تطبيقك يسمح للمستخدمين بالتقاط الصور، يتوقّع المستخدم أن يتمكّنوا من الوصول إلى تلك الصور حتى بعد إلغاء تثبيت التطبيق. لذا عليك بدلاً من ذلك استخدام مساحة التخزين المشتركة لحفظ هذه الأنواع من الملفات في مجموعة الوسائط المناسبة.

توضّح الأقسام التالية كيفية تخزين الملفات والوصول إليها ضمن الأدلة الخاصة بالتطبيقات.

الوصول من وحدة التخزين الداخلية

يقدّم النظام لكل تطبيق أدلة ضمن مساحة التخزين الداخلية يمكنه تنظيم ملفاته فيها. تم تصميم أحد الأدلة لملفات تطبيقك الدائمة، بينما يحتوي دليل آخر على الملفات المخزَّنة مؤقتًا لتطبيقك. لا يتطلب تطبيقك أيًا من أذونات النظام لقراءة الملفات في هذه الأدلة وكتابتها.

لا يمكن للتطبيقات الأخرى الوصول إلى الملفات المخزّنة في وحدة التخزين الداخلية. وهذا يجعل وحدة التخزين الداخلية مكانًا جيدًا لبيانات التطبيقات التي لا ينبغي للتطبيقات الأخرى الوصول إليها.

يُرجى العلم أنّ هذه الأدلة عادةً ما تكون صغيرة. قبل كتابة ملفات خاصة بالتطبيق في وحدة التخزين الداخلية، يجب أن يستفسر التطبيق عن المساحة الخالية على الجهاز.

الوصول إلى الملفات الثابتة

تقع الملفات العادية والثابتة لتطبيقك في دليل يمكنك الوصول إليه باستخدام السمة filesDir لكائن السياق. يقدّم إطار العمل عدة طرق لمساعدتك في الوصول إلى الملفات وتخزينها في هذا الدليل.

الوصول إلى الملفات وتخزينها

يمكنك استخدام واجهة برمجة التطبيقات File للوصول إلى الملفات وتخزينها.

وللمساعدة في الحفاظ على مستوى أداء تطبيقك، لا تفتح الملف نفسه وتغلقه عدة مرات.

يوضِّح مقتطف الرمز البرمجي التالي كيفية استخدام واجهة برمجة التطبيقات File:

Kotlin

val file = File(context.filesDir, filename)

Java

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

تخزين ملف باستخدام ساحة مشاركات

كبديل لاستخدام واجهة برمجة تطبيقات 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 أداة الإنشاء في رمز مكتوب بلغة 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 لملف سياق وواجهة برمجة التطبيقات 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 (المستوى 19 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، لا يحتاج تطبيقك إلى طلب أي أذونات مرتبطة بالمساحة التخزينية للوصول إلى الدلائل الخاصة بالتطبيق ضمن مساحة التخزين الخارجية. تتم إزالة الملفات المخزّنة في هذه الأدلة عند إلغاء تثبيت تطبيقك.

على الأجهزة التي تعمل بنظام التشغيل Android 9 (المستوى 28 لواجهة برمجة التطبيقات) أو إصدار أقدم، يمكن لتطبيقك الوصول إلى الملفات الخاصة بالتطبيق والتي تنتمي إلى تطبيقات أخرى، شرط أن يكون لدى تطبيقك أذونات التخزين المناسبة. لمنح المستخدمين مزيدًا من التحكّم في ملفاتهم و للحدّ من فوضى الملفات، تحصل التطبيقات التي تستهدف الإصدار 10 من نظام التشغيل Android (المستوى 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;
}

من المهم استخدام أسماء الدلائل المقدَّمة من خلال ثوابت واجهة برمجة التطبيقات، مثل 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 .

مصادر إضافية

لمزيد من المعلومات حول حفظ الملفات في مساحة تخزين الجهاز، يُرجى الرجوع إلى الموارد التالية.

الفيديوهات