مشخصات مقاله
روش های دیگر ذخیره سازی داده (preferences and files)
این آموزش تمرکز خود را بر شرح نحوه ی ذخیره سازی جفت های کلید-مقدار در فایل های اندروید (File based persistence) با فراخوانی preference API قرار می دهد. سپس چگونگی خواندن/نوشتن فایل در اندروید را تشریح می کند.
آموزش File based persistence (ذخیره داده ها در سیستم فایل)
آموزش روش های ذخیره ی ماندگار داده ها به صورت محلی (local data persistence)
اندروید این امکان را فراهم می آورد تا داده های اپلیکیشن را در سیستم فایل ذخیره نمایید. در واقع سیستم اندروید به ازای هر اپلیکیشن یک پوشه ی data/data/[application package] ایجاد می کند.
اندروید برای ذخیره ی داده ها در سیستم فایل محلی روش های زیر را پشتیبانی می کند:
- Files – می توان داده ها را در فایل ذخیره کرده و بروز رسانی نمود (روشی قدیمی).
- Preferences – اندروید به شما اجازه می دهد جفت های کلید-مقدار از نوع داده ای اولیه (primitive data type) را به صورت دائمی ذخیره نمایید (به شما امکان می دهد اطلاعات مربوط به تنظیمات انتخابی کاربر را با استفاده از آبجکت sharedpreferences به صورت کلید-مقدار ذخیره کنید).
- SQLite database – می توان نمونه یا نسخه های موجود از دیتابیس SQLite حامل داده های اپلیکیشن را در سیستم فایل و به صورت محلی ذخیره کرد (ذخیره ی داده های اپلیکیشن در سیستم مدیریت متحدالشکل دیتابیس که اندروید توسط SQLite پشتیبانی می کند).
فایل ها در پوشه ی files و تنظیمات اپلیکیشن در قالب فایل های XML داخل پوشه ی shared-prefs ذخیره و نگهداری می شوند.
اگر اپلیکیشن شما یک دیتابیس SQLite ایجاد کند، اندروید به صورت پیش فرض این دیتابیس را در دایرکتوری اصلی اپلیکیشن و تحت پوشه ی databases ذخیره می نماید.
تصویر زیر یک سیستم فایل را به نمایش می گذارد که فایل های ساده، فایل های کش شده (cache file) و اطلاعات مربوط به وضعیت تنظیمات انتخابی کاربر (preferences) را شامل می شود.

تنها اپلیکیشن است که اجازه ی درج داده و نوشتن در پوشه ی اصلی اپلیکیش (app-directory) را دارد. اپلیکیشن همچنین این قابلیت را دارد که زیرپوشه های (sub-directory) لازم را در پوشه ی اصلی (application directory) ایجاد نماید. جهت ایجاد این زیرپوشه ها، اپلیکیشن مورد نظر می تواند مجوزهای لازم در سطح خواندن و نوشتن را به دیگر برنامه ها اعطا کند.
آموزش ذخیره سازی internal داده در مقایسه با ذخیره ی داده ها به صورت external
اندروید دارای دو حافظه ی داخل و خارجی است و داده ها را به صورت internal و external ذخیره می کند. حافظه ی خارجی دارای سطح دسترسی شخصی نبوده (private نیست) و همیشه در دسترس نیست. به عنوان مثال، زمانی که دستگاه اندروید از طریق USB به کامپیوتر وصل می شود، این حافظه ی خارجی به طور موقت از دسترس اپلیکیشن های اندروید خارج می گردد.
آموزش جایگذاری اپلیکیشن در حافظه ی خارجی
از ویرایش 8 مجموعه ابزار ساخت و توسعه ی اپلیکیشن های اندروید (SDK level 8) می توان در فایل تنظیمات (manifest) مشخص نمود که اپلیکیشن مورد نظر امکان نصب بر روی حافظه ی خارجی را داشته باشد یا اینکه اپلیکیشن مزبور بایستی بر روی حافظه ی خارجی جایگذاری شود. برای نیل به این هدف کافی است مقدار android:installLocation را در فایل تنظیمات برابر preferExternal یا auto قرار دهید.
در این شرایط، ممکن است برخی از کامپوننت های نرم افزاری و تشکیل دهنده اپلیکیشن بر روی mount point یا محل ذخیره سازی رمزنگاری شده (encrypted) خارجی جایگذاری شوند. دیتابیس و سایر داده های private (با سطح دسترسی شخصی) همچنان فقط بر روی حافظه ی داخلی سیستم (storage system) ذخیره می شوند.
آموزش Preferences (ذخیره و بازگردانی اطلاعات مربوط به تنظیمات کاربر)
آموزش ذخیره جفت های کلید-مقدار از نوع داده ای اولیه
اندروید با ارائه و پشتیبانی از کلاس SharedPreferences این امکان را مهیا می کند تا جفت های کلید-مقدار از جنس داده ای اولیه را در سیستم فایل اندروید ذخیره نمایید (تنظیمات انتخابی کاربر همچون اندازه ی فونت و رنگ متن که داده های کوچک هستند را به صورت key-value ذخیره نمایید).
Preferences و تنظیمات انتخابی کاربر را می توان به راحتی داخل فایل های منبع XML تعریف نمود.
کلاس PreferenceManager با ارائه ی توابعی به توسعه دهنده این امکان را می دهد تا اطلاعات مربوط به تنظیمات کاربر، مستقر در یک فایل XML را بازیابی کند. کد زیر نحوه ی دسترسی به تنظیمات ذخیره شده در یک فایل XML را به نمایش می گذارد.
1 2 3 | # getting preferences from a specified file SharedPreferences settings = getSharedPreferences( "Test" , Context.MODE_PRIVATE); <button></button> |
Preferences می بایست به صورت private ویژه ی اپلیکیشن مورد نظر ایجاد شود. تمامی کامپوننت ها و اجزا تشکیل دهنده ی اپلیکیشن می توانند به اطلاعات preferences دسترسی داشته باشند. به اشتراک گذاری اطلاعات با یک اپلیکیشن دیگر که دارای فایل preference سراسری با دسترسی در سطح خواندن و نوشتن (world readable/writable) می باشد، امری است که به ندرت اتفاق می افتد. چرا که در این صورت کامپوننت خارجی می بایست از اسم و محل قرار گیری دقیق فایل مطلع باشد.
Preferences پیش فرض به راحتی با فراخوانی متد PreferenceManager.getDefaultSharedPreferences(this) در اختیار دیگر کامپوننت های اپلیکیشن قرار می گیرد. برای دسترسی به مقدار preference کافی است کلید مربوطه و آبجکتی از کلاس SharedPreferences را مانند زیر مورد استفاده قرار دهید.
1 2 3 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getActivity(); String url = settings.getString( "url" , "n/a" ); <button></button> |
به منظور ایجاد و ویرایش مقادیر preferences لازم است متد edit() را بر روی آبجکت SharedPreferences فراخوانی نمایید. پس از ویرایش مقدار، بایستی متد apply() را فراخوانی کنید تا مقادیر جدید به صورت ناهمزمان در سیستم فایل اعمال شوند. لازم به توضیح است که متد commit() تغییرات را به صورت همزمان به فایل سیستم اعمال می کند، از این جهت استفاده از متد مذکور در این سناریو توصیه نمی شود.
1 2 3 4 | Editor edit = preferences.edit(); edit.putString( "username" , "new_value_for_user" ); edit.apply(); <button></button> |
آموزش گوش فراخوانی به تغییرات در تنظیمات کاربر به وسیله ی preference listener
می توانید با فراخوانی متد registerOnSharedPreferenceChangeListener() بر روی آبجکت SharedPreference به تغییرات در preferences گوش داده و از آن ها مطلع شوید.
1 2 3 4 5 6 7 8 9 10 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( this ); // Instance field for listener listener = new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // Your Implementation } }; prefs.registerOnSharedPreferenceChangeListener(listener); <button></button> |
توجه داشته باشید که SharedPreferences تمامی listener یا گوش فراخوان ها را داخل WeakHashMap نگه می دارد، به همین علت چنانچه کد شما اشاره گری به آن تعریف نکرده باشد (آن را داخل متغیر نگه ندارد)، listener ها بازیافت می شوند و اطلاعات مورد نظر شما از دست خواهد رفت.
آموزش: آنچه لازم است
پروژه ی این بخش مبتنی بر نمونه اپلیکیشن "de.vogella.android.socialapp" از آموزش action bar در مباحث قبلی می باشد.
تمرین: آنچه لازم استبرای تمرین زیر لازم است یک پروژه ی اندرویدی به نام com.example.android.rssfeed همراه با یک المان Settings در action bar ایجاد نمایید.
تمرین: ذخیره ی تنظیمات کاربر برای RSSfeedدر تمرین جاری، برنامه را گونه ای می نویسید که یک activity دیگر باز شده و به کاربر این امکان را بدهد تا URL دلخواه خود برای RSS feed را وارد نماید. این activity ظاهر و UI خود را از یک فایل xml که حاوی تنظیمات انتخابی کاربر است (xml preference file)، می خواند.
آموزش ایجاد فایل preference
یک فایل XML به نام mypreferences.xml در پوشه ی XML ایجاد نمایید. المان های زیر را به آن اضافه نمایید.
1 2 3 4 5 6 7 8 | <? xml version = "1.0" encoding = "utf-8" ?> < edittextpreference android:key = "url" android:title = "Rss feed URL" android:inputtype = "textUri" /> < checkboxpreference android:title = "Aktiv" android:key = "active" /> </ preferencescreen > < button ></ button > |
آموزش ایجاد activity جهت دریافت ورودی از کاربر
ابتدا کلاس SettingsActivity که از PreferenceActivity ارث بری می کند را ایجاد نمایید. این activity فایل preference را بارگذاری نموده و به کاربر اجازه می دهد تا مقادیر را ویرایش کند.
1 2 3 4 5 6 7 8 9 10 11 | package com.example.android.rssreader; import android.os.Bundle; import android.preference.PreferenceActivity; public class SettingsActivity extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); addPreferencesFromResource(R.xml.mypreferences); } } <button></button> |
این کلاس را به عنوان یک activity در فایل تنظیمات اپلیکیشن AndroidManifest.xml ثبت و معرفی نمایید.
آموزش متصل کردن activity مربوط به تنظیمات و دریافت ورودی کاربر
Activity مربوط به preference و دریافت تنظیمات دلخواه کاربر را با فراخوانی متد onOptionsItemSelected() باز نمایید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.mainmenu, menu); return true ; } // This method is called once the menu is selected @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.preferences: // Launch settings activity Intent i = new Intent( this , SettingsActivity. class ); startActivity(i); break ; // more code... } return true ; } <button></button> |
آموزش بارگذاری RSS feed با استفاده از مقدار preference
تکه کد زیر نشان می دهد چگونه می توان از یک متد به مقدار preference دسترسی پیدا کرد.
1 2 3 4 5 | // if you use this in a service or activity you can use this // if you use this in a fragment use getActivity() or getContent() as parameter SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences( this ); <button></button> |
آموزش تست اپلیکیشن
اپلیکیشن خود را اجرا نمایید. آیتم (action) Settings را از action bar انتخاب کنید. بایستی یک activity دیگر به نمایش در آمده و شما بتوانید آدرس URL دلخواه را وارد کنید. پس از کلیک بر روی دکمه ی بازگشت (back) و بروز رسانی (refresh)، با بررسی activity اطمینان حاصل کنید که مقدار url در activity بکار برده شده است.
تمرین اضافی: نمایش مقدار جاری در settingsتکه کد زیر نشان می دهد چگونه می توان مقدار جاری را در صفحه ی preference نمایش داد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | package com.example.android.rssreader; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Bundle; import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener { @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); addPreferencesFromResource(R.xml.mypreferences); // show the current value in the settings screen for ( int i = 0 ; i < getPreferenceScreen().getPreferenceCount(); i++) { initSummary(getPreferenceScreen().getPreference(i)); } } @Override protected void onResume() { super .onResume(); getPreferenceScreen().getSharedPreferences() .registerOnSharedPreferenceChangeListener( this ); } @Override protected void onPause() { super .onPause(); getPreferenceScreen().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener( this ); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { updatePreferences(findPreference(key)); } private void initSummary(Preference p) { if (p instanceof PreferenceCategory) { PreferenceCategory cat = (PreferenceCategory) p; for ( int i = 0 ; i < cat.getPreferenceCount(); i++) { initSummary(cat.getPreference(i)); } } else { updatePreferences(p); } } private void updatePreferences(Preference p) { if (p instanceof EditTextPreference) { EditTextPreference editTextPref = (EditTextPreference) p; p.setSummary(editTextPref.getText()); } } } <button></button> |

آموزش File API
آموزش استفاده از File API
برای دسترسی به سیستم فایل می توانید از طریق کلاس های java.io اقدام نمایید. اندروید کلاس های کمکی (helper) در اختیار توسعه دهنده قرار می دهد که ایجاد و دسترسی به فایل ها و پوشه های جدید را آسان می سازد. برای مثال، getDir(String, int) پوشه ی مورد نیاز را ایجاد نموده و دسترسی به آن را فراهم می سازد. متد openFileInput(String s) یک فایل برای ورودی باز کرده و متد openFileOutput(String s, Context.MODE_PRIVATE) نیز یک فایل جدید ایجاد می نماید.
اندروید از فایل های خواندنی و نوشتنی سراسری (world readable/writable) پشتیبانی می کند. اما به طور کلی توصیه می شود دسترسی به فایل ها را فقط به خود اپلیکیشن محدود نمایید (فایل های اپلیکیشن را private نگه دارید) و در صورت نیاز به اشتراک گذاری داده ها بین اپلیکیشن ها از کامپوننت نرم افزاری content provider استفاده کنید.
مثال زیر استفاده از API مورد نظر را به نمایش می گذارد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class Util { public static void writeConfiguration(Context ctx ) { try (FileOutputStream openFileOutput = ctx.openFileOutput( "config.txt" , Context.MODE_PRIVATE);) { openFileOutput.write( "This is a test1." .getBytes()); openFileOutput.write( "This is a test2." .getBytes()); } catch (Exception e) { // not handled } } } public void readFileFromInternalStorage(String fileName) { String eol = System.getProperty( "line.separator" ); try (BufferedReader input = new BufferedReader( new InputStreamReader( openFileInput(fileName))); ){ String line; StringBuffer buffer = new StringBuffer(); while ((line = input.readLine()) != null ) { buffer.append(line + eol); } } catch (Exception e) { // we do not care } } <button></button> |
آموزش حافظه و محل ذخیره سازی خارجی (external storage)
اندروید از حافظه ی خارجی مانند SD و دسترسی به آن نیز پشتیبانی می کند. تمامی فایل ها و پوشه های مستقر در حافظه ی خارجی برای کلیه ی اپلیکیشن هایی که دارای مجوز لازم هستند، قابل دسترسی می باشد.
جهت خواندن داده از حافظه ی خارجی، اپلیکیشن شما می بایست از مجوز android.permission.READ_EXTERNAL_STORAGE برخوردار باشد.
به منظور درج و نوشتن داده در حافظه ی خارجی، اپلیکیشن شما می بایست از مجوز android.permission.WRITE_EXTERNAL_STORAGE برخوردار باشد. برای دسترسی به حافظه ی خارجی، کافی است متد Environement.getExternalStorageDirectory() را فراخوانی نمایید.
با فراخوانی متد زیر شما می توانید وضعیت حافظه ی خارجی را بررسی کنید. چنانچه دستگاه اندروید از طریق USB به کامپیوتر متصل شده است، در آن صورت کارت SD که به عنوان حافظه خارجی مورد استفاده قرار گرفته است، از دسترس خارج می شود.
1 2 | Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) <button></button> |
تکه کد زیر مثالی از خواندن اطلاعات از حافظه ی خارجی را به نمایش می گذارد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | private void readFileFromSDCard() { File directory = Environment.getExternalStorageDirectory(); // assumes that a file article.rss is available on the SD card File file = new File(directory + "/article.rss" ); if (!file.exists()) { throw new RuntimeException( "File not found" ); } Log.e( "Testing" , "Starting to read" ); BufferedReader reader = null ; try { reader = new BufferedReader( new FileReader(file)); StringBuilder builder = new StringBuilder(); String line; while ((line = reader.readLine()) != null ) { builder.append(line); } } catch (Exception e) { e.printStackTrace(); } finally { if (reader != null ) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } <button></button> |