في العديد من الحالات، ينشئ تطبيقك ملفات لا تحتاج التطبيقات الأخرى إلى الوصول إليها أو لا يُفترَض أن تصل إليها. يقدّم النظام المواقع التالية لتخزين هذه الملفات الخاصة بالتطبيق:
أدلة مساحة التخزين الداخلية: تتضمّن هذه الأدلة كلاً من مكان مخصّص لتخزين الملفات الدائمة ومكان آخر لتخزين 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
.
مصادر إضافية
لمزيد من المعلومات عن حفظ الملفات في مساحة تخزين الجهاز، يُرجى الرجوع إلى الموارد التالية.