Android Preference 初探

来源:互联网 发布:python金融大数据 pdf 编辑:程序博客网 时间:2024/05/21 06:43

前言

做了好几个Android 应用的开发,虽然每次都用到Preference来配置应用程序的参数,但是每次都是套用以前的模式来做的,没有深究。这次遇到一个项目,重新写的PreferenceActivity,发现一些概念自己还是没哟完全搞清楚,就仔细研究了一下Prefenece,PreferenceActivity, SharedPreferences以及Preference Layout XML文件等若干类或文件之间的关系,基本上有了个较为全面的认识,这里简单记录一下,以供今后查阅和回忆,谁让我的记性不好呢,吐舌头

1. Preference相关的类和函数介绍

PreferenceActivity :作为构建一个配置界面的基类,此类中包含一个ListView来管理各个配置项,也就是Preference,每个Preference都是ListView的一个Item。此listView从资源文件中的Preference Layout xml文件中经过inflate生成。
Preference Layout File :类似Activity的layout XML文件,不过里面的元素都是和Preference相关的,比如PreferenceScreen,PreferenceGroup,ListPreference等等。
Preference :此类对应PreferenceActvity中一个配置单位,相当于一个控件(view)。实际使用的都是此类的子类,比如ListPreference, CheckboxPreference。此类的一个实例,对应Preference 资源文件中的一个元素。
SharedPreferences :这是一个接口类,提供一个抽象的接口,供应用中来访问和修改应用的而配置文件。此类提供诸如getInt(String key, boolean defValue)的方法来获取各种类型的配置值,其中“key” 是键值,Preference Layout File中定义各个Preference中的android:key属性,就要和这里对应。当让,此类也提供修改配置文件的方法,必须先使用edit( ) 函数获得一个SharedPreferences.Editor 对象,再使用这个对象的方法进行修改,修改完后还需要提交修改(commit( ))。这样设计的目的,手册中这样描述 :
             All changes you make in an editor are batched, and not copied back to the original SharedPreferences until you call commit() or apply()

PreferenceManager :此类提供一个很有用的静态函数getDefaultSharedPreference (Context context),返回一个SharedPreferences 对象,用来操作整个应用范围内共享的、缺省的、唯一的preference文件。

Activity.getPreferences(int mode) :返回一个SharedPreferences 对象,用来操作此Activity私有的、唯一的preference文件。保存在系统中的xml形式的文件的名称为这个Activity的名字。

ContextWrapper.getSharePreferences(String name, int mode) :返回一个SharedPreferences 对象,用来操作整个应用范围内共享的、指定名称的preference文件。也就是说,可以通过这个函数建立多个在应用范围内共享的preference文件,文件名由第一个参数指定。

2. Preference Layout XML文件和Preferences 文件

我开始在这个地方就有点混淆,经过查阅资料才稍微清晰些。

2.1 Preference 文件的访问

Preferences文件,是Android一种保存持久数据的方法(其他方式还有文件,数据库等),Android数据持久化方法中最简单的一种,即使用Preferences的键值对存储方式。这种方式主要用来存储比较简单的一些数据,而且是标准的BooleanIntFloatLongString等类型。

和此Preference相关的 android.content.SharedPreferences 是一个接口,用来获取和修改持久化存储的数据。有三种获取系统中保存的持久化数据的方式:

1. public SharedPreferences getPreferences (int mode)

    通过Activity对象获取,获取的是本Activity私有的Preference,保存在系统中的xml形式的文件的名称为这个Activity的名字,因此一个Activity只能有一个,属于这个Activity

2. public SharedPreferences getSharedPreferences (String name, int mode)

    因为Activity继承了ContextWrapper,因此也是通过Activity对象获取,但是属于整个应用程序,可以有多个,以第一参数的name为文件名保存在系统中。

3. public static SharedPreferences getDefaultSharedPreferences (Context context)

    PreferenceManager的静态函数,保存PreferenceActivity中的设置,属于整个应用程序,但是只有一个,Android会根据包名和PreferenceActivity的布局文件来起一个名字保存。

    通过以上方式取得SharedPreferences后就可以对数据进行读取或者保存了。


2.2  Preference 文件的存放位置

这些文件是应用私有的,手机上安装程序后,一般放在/data/data/包名/shared_prefs/ 目录下。从前一章的分析可以看出,一个应用可能有多个preference文件,而且生成的方式各有不同:可能是缺省生成的,也可能是程序中制定文件名生成的。

例如,在Android 自带的Browser应用中,在从未运行的情况下,此目录下只有一个preference文件:com.android.browser_preferences.xml 

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><map><boolean name="enable_hardware_accel_skia" value="false" /><boolean name="debug_menu" value="false" /></map>
可以看出,此文件中记录了已经设定的配置项的name(key)/value pair,使用的是map概念。

在运行一次后,此目录下又增加了一个preference 文件:browser_recovery_prefs.xml

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><map><long name="last_recovered" value="0" /></map>
从名字看,是来记录recover信息的。

接下来,在启动一次Settings界面后,并进入“Advance” Fragments后,com.android.browser_preferences.xml 文件内容发生了变化:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><map><string name="default_text_encoding">Latin-1</string><boolean name="debug_menu" value="false" /><boolean name="open_in_background" value="true" /><boolean name="enable_javascript" value="true" /><boolean name="load_page" value="true" /><string name="plugin_state">ON</string><boolean name="autofit_pages" value="true" /><boolean name="block_popup_windows" value="true" /><string name="default_zoom">MEDIUM</string><boolean name="enable_hardware_accel_skia" value="false" /><string name="search_engine">google</string><boolean name="reset_default_preferences" value="false" /></map>

由此可以看出,com.android.browser_preferences.xml 文件算是Default 的preference 文件,在PreferenceActivity中加载Layout文件后,就会在这个文件里添加相应的items。而browser_recovery_prefs.xml可能就是程序运行时,代码建立的一个preference文件了。

2.3 Preference Layout XML 文件

Preference Layout XML文件是个资源文件,它的作用就是为PreferenceActivity提供layout;于此同时,在PreferenceActivity 加载这个资源文件之后,也会在应用的私有目录中建立一个与之对应的preference 文件,其命名方式是根据包名和Layout XML文件名来设定。当然也可以通过getPreferenceManager().setSharedPreferencesName("配置文件名")来设定。

3. 测试程序验证

为了进一步验证上面的分析,编写了一个简单的测试程序。

此程序有两个Activity。一个是主Activity,里面只有一个Button Widget,点击这个Button就会打开Prefence Activity。对应代码如下:

public class MainActivity extends Activity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                Button bt = (Button)findViewById(R.id.button1);                bt.setOnClickListener(new OnClickListener(){public void onClick(View v) {Intent intent = new Intent(cxt, PrefActivity.class);startActivity(intent);}                });    }    private Context cxt = this;}
另外一个是配置Activity,对应代码如下:

public class PrefActivity extends PreferenceActivity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);                addPreferencesFromResource(R.xml.demo_pref);    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.sh, menu);        return true;    }}
其中红色代码部分加载了资源文件中的Preference Layout 文件demo_pref.xml , 它的内容如下:

<?xml version="1.0" encoding="utf-8"?><PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >    <PreferenceCategory>        <ListPreference android:entries="@array/pref_add_list_choices" android:entryValues="@array/pref_add_list_values" android:dialogTitle="Select Address" android:title="Address" android:summary="Select Address" android:key="add" android:defaultValue="cn"/>    </PreferenceCategory>    </PreferenceScreen>

这里面只有一个ListPreference,用户如果点击的话,会弹出一个List Dialog,列出若干项供用户选择,截图如下:


我是在模拟器上测试的,可以童工DDMS工具看到此程序私有的文件目录。我查看了在不同情况下,preference 文件的出现时机和内容的变化情况。

1)当程序刚刚安装好后:

因为还未加载Preference Layout 文件,Android系统还未生成与之对应的default preference 文件。

2)当程序运行,但从未进入PreferenceActivty时:

因为还未加载Preference Layout 文件,Android系统还未生成与之对应的default preference 文件。

3)当程序首次进入PreferenceActivty, 尚未点击ListPreference 条目时:

因为已经加载了Preference Layout 文件,Android系统会在此应用的私有目录下生成一个shared_prefs目录,此目录下建立一个名为com.mutu.example.prefdemo_preferences.xml的default preference 文件。因为此程序的package名称为:com.mutu.example.prefdemo,所以猜测系统命名的方式就是简单地在包名后加上“_preferences.xml”。此时,文件内容如下:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><map><string name="add">cn</string></map>
此时只保存了一个name/value 条目,因为Preference Layout 文件中只有一个ListPreference。而且取值也和ListPreference设置的Default Value一致。

4)当点击点击ListPreference 条目,选择一个item,然后返回后:

选择一个非缺省的值,此时preference 文件内容变化:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><map><string name="add">au</string></map>
也就是在PreferenceActivity界面的造作,导致default preference文件内容发生变化。此后如果应用通过SharedPreferences接口访问这个文件,获得的“add”对应的值,就发生了变化。当让,在很多应用中,都是会监听此界面的修改,实时的改变程序运行的状态。

5)如果在程序中新建一个配置文件,分别利用Activity.getPreferences(int mode) 和ContextWrapper.getSharePreferences(String name, int mode) 来建立,相关代码如下:

        bt = (Button)findViewById(R.id.button2);        bt.setOnClickListener(new OnClickListener(){public void onClick(View v) {// activity private preference        SharedPreferences prefs = ctx.getPreferences(MODE_PRIVATE);        SharedPreferences.Editor ed = prefs.edit();        ed.putBoolean("student", true);        ed.putString("country", "cn");        ed.commit();}                });                bt = (Button)findViewById(R.id.button3);        bt.setOnClickListener(new OnClickListener(){public void onClick(View v) { // activity private preference        SharedPreferences prefs = ctx.getSharedPreferences("specfied_pref1",MODE_PRIVATE);        SharedPreferences.Editor ed = prefs.edit();        ed.putBoolean("man", true);        ed.putString("job", "sales");        ed.commit();    }                });


分别点击增加的两个Button, 会在对应在shared_prefs目录下生成两个preference文件,分别是MainActivity.xml :

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><map><boolean name="student" value="true" /><string name="country">cn</string></map>

和specified_pref1.xml:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><map><string name="job">sales</string><boolean name="man" value="true" /></map>
Activity私有的preference文件,使用它的类名+“.xml”来命名。在Android SDK文档中也有说明:

public SharedPreferences getPreferences (int mode)

Since: API Level 1

Retrieve a SharedPreferences object for accessing preferences that are private to this activity. This simply calls the underlying getSharedPreferences(String, int) method by passing in this activity's class name as the preferences name.

4. 总结

本文主要是从preference文件生成方法入手,通过一个简单的测试程序,粗浅的了解了Android系统处理Preference的生成对应文件的机制。

针对和应用相关的preference文件,总结以下几点:

  • 各种类型的Preference都对应着一个preference 文件
  • 具体应用根据需要,通过不同的方法,获取不同的SharePreferences接口实例,操作不同的preference文件
  • 概略上可以分为两种:一种从PreferenceActivity Layout 文件生成的preference;一种是通过Activity的preference相关函数建立。
时间有限,考虑的不是很全面,以后有机会再完善。