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