参考《Professional Android 4 Development》
持久化:Files, Saving State and Preferences
Android中的数据持久化
1. Shared Preferences: 以键值对的形式存储,是一种轻量级的持久化方法。
2. Saved UI application state: Activities和Fragment的生命周期事件中会获得savedInstanceState参数,包含UI的内容。
3. Files。
使用Shared Preferences
SharedPreferences mySharedPreferences = getSharedPreferences(MY_PREFS, Activity.MODE_PRIVATE);
MODE_PRIVATE模式的Shared Preferences存储在应用程序的sandbox里面,其他应用程序无法访问。
修改Shared Preferences
SharedPreferences.Editor editor = mySharedPreferences.edit(); // Store new primitive types in the shared preferences object. editor.putBoolean(“isTrue”, true); editor.putFloat(“lastFloat”, 1f); editor.putInt(“wholeNumber”, 2); editor.putLong(“aNumber”, 3l); editor.putString(“textEntryValue”, “Not Empty”); // Commit the changes. editor.apply()
读取Shared Preferences
可以获取单个属性的值:
// Retrieve the saved values. boolean isTrue = mySharedPreferences.getBoolean(“isTrue”, false); float lastFloat = mySharedPreferences.getFloat(“lastFloat”, 0f); int wholeNumber = mySharedPreferences.getInt(“wholeNumber”, 1); long aNumber = mySharedPreferences.getLong(“aNumber”, 0); String stringPreference = mySharedPreferences.getString(“textEntryValue”, “”);
也可以将所有的键值对读到一个Map中:
Map<String, ?> allPreferences = mySharedPreferences.getAll();
检测Shared Preferences中是否含有某个属性的值:
boolean containsLastFloat = mySharedPreferences.contains(“lastFloat”);
Preference Framework
Android中的Preference Framework是基于XML的,其外观由主题决定,这样既可以使程序和系统保持一致的风格,又可以方便地继承其他程序的Preference。
Preference Framework包含四个部分:
1. Preference Screen Layout:用于定义Preference的显示,为XML文件。
2. Preference Activity和Preference Fragment:用于host Preference Screen,在Android3.0之前,使用Preference Activity;之后使用Preference Fragment。
3. Preference Header Definition:XML文件,定义Preference Fragment和显示他们的hierarchy。
4. Shared Preference Change Listener: OnSharedPreferenceChangeListener接口的实现类,监听Shared Preference的Change事件。
使用XML定义Preference Layout
定义Preference Layout的XML文件存放在res/xml文件夹下,下面是一个示例:
<?xml version=”1.0” encoding=”utf-8”?> <PreferenceScreen xmlns:android=”http://schemas.android.com/apk/res/android”> <PreferenceCategory android:title=”My Preference Category”> <CheckBoxPreference android:key=”PREF_CHECK_BOX” android:title=”Check Box Preference” android:summary=”Check Box Preference Description” android:defaultValue=”true”/> </PreferenceCategory> </PreferenceScreen>
效果图:
原生的Preference控件:
1. CheckBoxPreference
2. EditTextPreference
3. ListPreference
4. MultiSelectListPreference
5. RingtonePreference
使用Intent导入系统Preference
<?xml version=”1.0” encoding=”utf-8”?> <PreferenceScreen xmlns:android=”http://schemas.android.com/apk/res/android” android:title=”Intent preference” android:summary=”System preference imported using an intent”> <intent android:action=”android.settings.DISPLAY_SETTINGS “/> </PreferenceScreen>
Preference Fragment简介
public class MyPreferenceFragment extends PreferenceFragment
重写onCreate()并调用addPreferencesFromResource方法,可以Inflate Preference Fragment。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.userpreferences); }
使用Preference Header定义Preference Fragment
Preference Header的XML文件位于res/xml目录下,每个Header的Resource ID是它的文件名(不含后缀)。
<preference-headers xmlns:android=”http://schemas.android.com/apk/res/android”> <header android:fragment=”com.paad.preferences.MyPreferenceFragment” android:icon=”@drawable/preference_icon” android:title=”My Preferences” android:summary=”Description of these preferences” /> </preference-headers>
和<PreferenceScreen>类似,<preference-headers>中也可以包含Intent:
<header android:icon=”@drawable/ic_settings_display” android:title=”Intent” android:summary=”Launches an Intent.”> <intent android:action=”android.settings.DISPLAY_SETTINGS “/> </header>
Preference Activity简介
创建Preference Activity:
public class MyFragmentPreferenceActivity extends PreferenceActivity
加载Preference Headers:
public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.userpreferenceheaders, target); }
对于早于3.0版本的系统,可以这样:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.userpreferences); }
onSharedPreferenceChangeListener简介
实现onSharedPreferenceChangeListener接口,可以获得Preference的添加,修改和删除事件的callback。
public class MyActivity extends Activity implements OnSharedPreferenceChangeListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Register this OnSharedPreferenceChangeListener SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs.registerOnSharedPreferenceChangeListener(this); } public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // TODO Check the shared preference and key parameters and change UI or behavior as appropriate. } }
Application Instance State持久化
使用Shared Preferences保存Activity State
// Create or retrieve the activity preference object. SharedPreferences activityPreferences = getPreferences(Activity.MODE_PRIVATE); // Retrieve an editor to modify the shared preferences. SharedPreferences.Editor editor = activityPreferences.edit(); // Retrieve the View TextView myTextView = (TextView)findViewById(R.id.myTextView); // Store new primitive types in the shared preferences object. editor.putString(“currentTextValue”, myTextView.getText().toString()); // Commit changes. editor.apply();
借助生命周期事件保存和恢复Activity Instance State
Activity提供了onSaveInstanceState handler来保存用户UI state等信息,其设计目标是当Activity被runtime terminate后,仍可以保存UI state信息。但如果Activity是被用户关闭或通过调用finish()方法关闭,则无法保存UI state信息。
private static final String TEXTVIEW_STATE_KEY = “TEXTVIEW_STATE_KEY”; @Override public void onSaveInstanceState(Bundle saveInstanceState) { // Retrieve the View TextView myTextView = (TextView)findViewById(R.id.myTextView); // Save its state saveInstanceState.putString(TEXTVIEW_STATE_KEY, myTextView.getText().toString()); super.onSaveInstanceState(saveInstanceState); }
这个Handler提供了在用户session里面保存UI state的方法,跨用户session还是建议使用Shared Preference。
借助生命周期事件保存和恢复Fragment Instance State
Fragment的onCreate,onCreateView和onActivityCreated Handler会获得instance state bundle参数。Activity被销毁和重建时Fragment仍可以通过调用setRetainInstance方法保存自己。当Fragment调用setRetainInstance时,它将不会经历onDestroy和onCreate阶段。这样可以节省很多系统资源。
public class MyFragment extends Fragment { private static String USER_SELECTION = “USER_SELECTION”; private int userSelection = 0; private TextView tv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); if (savedInstanceState != null) userSelection = savedInstanceState.getInt(USER_SELECTION); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.mainfragment, container, false); tv = (TextView)v.findViewById(R.id.text); setSelection(userSelection); Button b1 = (Button)v.findViewById(R.id.button1); Button b2 = (Button)v.findViewById(R.id.button2); Button b3 = (Button)v.findViewById(R.id.button3); b1.setOnClickListener(new OnClickListener() { public void onClick(View arg0) { setSelection(1); } }); b2.setOnClickListener(new OnClickListener() { public void onClick(View arg0) { setSelection(2); } }); b3.setOnClickListener(new OnClickListener() { public void onClick(View arg0) { setSelection(3); } }); return v; } private void setSelection(int selection) { userSelection = selection; tv.setText(“Selected: “ + selection); } @Override public void onSaveInstanceState(Bundle outState) { outState.putInt(USER_SELECTION, userSelection); super.onSaveInstanceState(outState); } }
读写静态文件
静态文件可以放在res/raw目录下,作为资源使用。
Resources myResources = getResources();
InputStream myFile = myResources.openRawResource(R.raw.myfilename);
使用应用程序自己的文件夹存储文件
许多应用程序需要在自己的目录或其他目录中下载或创建文件。相应地,android提供了两个方法:getDir和getExternalFilesDir,分别用于在应用程序文件夹内部创建文件和在文件夹外部创建文件。
创建Private Application Files
String FILE_NAME = “tempfile.tmp”; // Create a new output file stream that’s private to this application. FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); // Create a new file input stream. FileInputStream fis = openFileInput(FILE_NAME);
上面代码默认的文件写模式为override,若要改为append,需将模式设为Context.MODE_APPEND。
若要将文件设为其他应用可写,可以使用Context.MODE_WORLD_WRITEABLE模式:
String OUTPUT_FILE = “publicCopy.txt”;
FileOutputStream fos = openFileOutput(OUTPUT_FILE, Context.MODE_WORLD_WRITEABLE);
获得应用的sandbox目录:
File file = getFilesDir();
Log.d(“OUTPUT_PATH_”, file.getAbsolutePath());
文件缓存
和普通文件一样,文件缓存也分为两种,内部缓存和外部缓存,分别使用getCacheDir和getExternalCacheDir获得。
常用的文件目录
- DIRECTORY_ALARMS
- DIRECTORY_DCIM
- DIRECTORY_DOWNLOADS
- DIRECTORY_MOVIES
- DIRECTORY_MUSIC
- DIRECTORY_NOTIFICATIONS
- DIRECTORY_PICTURES
- DIRECTORY_PODCASTS
- DIRECTORY_RINGTONES
如果这些默认值所对应的文件夹不存在,你可以自己去创建:
String FILE_NAME = “MyMusic.mp3”; File path = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_MUSIC); File file = new File(path, FILE_NAME); try { path.mkdirs(); [... Write Files ...] } catch (IOException e) { Log.d(TAG, “Error writing “ + FILE_NAME, e); }
}