في كثير من الحالات، ينشئ تطبيقك ملفات لا تحتاج التطبيقات الأخرى إلى الوصول إليها أو لا يجب أن تصل إليها. يوفّر النظام المواقع التالية لتخزين الملفات الخاصة بالتطبيق:
أدلة وحدة التخزين الداخلية: تتضمّن هذه الأدلة موقعًا مخصّصًا لتخزين الملفات الثابتة وموقعًا آخر لتخزين البيانات المخزّنة مؤقتًا. يمنع النظام التطبيقات الأخرى من الوصول إلى هذه المواقع الجغرافية، ويتم تشفيرها على الإصدار 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 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، لا يحتاج تطبيقك إلى طلب أي أذونات متعلقة بالتخزين للوصول إلى الأدلة الخاصة بالتطبيق ضمن وحدة التخزين الخارجية. تتم إزالة الملفات المخزَّنة في هذه الأدلة عند إلغاء تثبيت تطبيقك.
على الأجهزة التي تعمل بالإصدار 9 من نظام التشغيل Android (المستوى 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
.
الطلب من المستخدم إزالة بعض ملفات الجهاز
لطلب أن يختار المستخدم الملفات التي يريد إزالتها على الجهاز، استخدِم intent يتضمّن الإجراء ACTION_MANAGE_STORAGE
. يعرض هذا الغرض إشعارًا للمستخدم. ويمكن أن يعرض هذا الطلب مقدار المساحة المتوفّرة على الجهاز، إذا أردت ذلك. لعرض هذه المعلومات السهلة الاستخدام، استخدِم نتيجة العملية الحسابية التالية:
StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()
طلب من المستخدم إزالة جميع ملفات ذاكرة التخزين المؤقت
بدلاً من ذلك، يمكنك أن تطلب من المستخدم محو ملفات ذاكرة التخزين المؤقت من جميع التطبيقات على الجهاز. لإجراء ذلك، استدعِ نية تتضمّن إجراء النية
ACTION_CLEAR_APP_CACHE
.
مراجع إضافية
لمزيد من المعلومات حول حفظ الملفات في مساحة تخزين الجهاز، يُرجى الرجوع إلى المراجع التالية.