Android SharedPreferences数据存储原理解析及使用示例

来源:互联网 发布:郑州大学软件学院2017 编辑:程序博客网 时间:2024/05/24 00:51

一、SharedPreferences简介

    SharedPreferences是基础key-value(键值对)来存储一些轻量数据的存储方式,特别适用于保存软件配置参数。使用SharedPreferences 保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs。

SharedPreferences主要特点:
- 适合于少量数据的保存 ;
- 保存的数据只支持Java基本数据类型,不支持自定义数据类型;
- 使用简单。

二、使用方法

1)获取SharedPreferences实例

    SharedPreferences本身是一个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name,int mode)方法来获取SharedPreferences实例。例如:

// 获取只能被本应用程序读、写的SharedPreferences对象SharedPreferences preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);

    第一个参数是生成xxx.xml文件的文件名字,第二个参数是文件的访问权限以及文件数据写入方式。

简单说明一下几种方式:
Context.MODE_PRIVATE: //指定该SharedPreferences数据只能被本应用程序读写。
Context.MODE_WORLD_READABLE: //指定该SharedPreferences数据能被其他应用程序读,但不能写。
Context.MODE_WORLD_WRITEABLE: //指定该SharedPreferences数据能被其他应用程序读写。

注意:从Android4.2开始, Android不再推荐使用 MODE_WORLD_READABLE、MODE_WORLD_WRITEABLE这两种模式, 因为这两种模式允许其他应用程序来读或写本应用创建的数据, 因此容易导致安全漏洞 。 如果应用程序确实需要把内部数据暴露出来供其他应用访司, 则应该使用 ContentProvider。

2)写入数据

方法一:

//获取SharedPreferences实例SharedPreferences preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);//实例化SharedPreferences.Editor对象editor = preferences.edit();// 存入用户姓名editor.putString("name", "yourname");//存入用户年龄             editor.putInt("age",18);// 提交所有存入的数据editor.commit();

方法二:

SharedPreferences preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);preferences.edit().putString("name",  "yourname").putInt("age", 18).commit();

切记:不要写成下面的形式,会导致数据无法存储

SharedPreferences sp = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);sp.edit().putString("name", "yourname");sp.edit().putInt("age", 18);sp.edit().commit();

    为什么这种方式无法存储? 因为sp.edit()每次都会返回一个新的Editor对象,Editor的实现类EditorImpl里面会有一个缓存的Map,最后commit的时候先将缓存里面的Map写入内存中的Map,然后将内存中的Map写进XML文件中。使用上面的方式commit,由于sp.edit()又重新返回了一个新的Editor对象,缓存中的Map是空的,所以导致数据无法被存储。

3)读取数据

//获取SharedPreferences实例SharedPreferences preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);// 读取字符串数据String name = preferences.getString("name", "");// 读取int类型的数据int age = preferences.getInt("age", 0);

    getSharedPreferences的具体实现是frameworks/base/core/java/android/app/ContextImpl.java,代码如下:

@Overridepublic SharedPreferences getSharedPreferences(String name, int mode) {    SharedPreferencesImpl sp;    synchronized (ContextImpl.class) {        ......        final String packageName = getPackageName();        ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);        if (packagePrefs == null) {            packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();            sSharedPrefs.put(packageName, packagePrefs);        }        ......        sp = packagePrefs.get(name);        if (sp == null) {            File prefsFile = getSharedPrefsFile(name);            sp = new SharedPreferencesImpl(prefsFile, mode);            packagePrefs.put(name, sp);            return sp;        }    }    ......    return sp;}

    SharedPreferencesImpl是SharedPreferences接口的具体实现类,一个name对应一个SharedPreferencesImpl,一个应用程序中根据name的不同会有多个SharedPreferencesImpl。
SharedPreferencesImpl的具体实现是在frameworks/base/core/java/android/app/SharedPreferencesImpl.java,我们可以通过getSharedPreferences获得SharedPreferences的实例,当我们调用sp.getString等get方法取数据时,实际上是直接从内存中的Map里面去取,get方法传入的第一个参数正好是Map的key,第二个参数是当Map中没有这个key对应值的时候,返回的默认值。

4)监听数据变化

SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {            @Override            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {            }        };        preferences.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);//注册数据变化监听        preferences.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);//解注册数据变化监听

5)具体写入文件的存储结构

这里写图片描述

    存储在文件中就是这样的,纯明文存储没有一点安全性可言。即使设置了Activity.MODE_PRIVATE权限在ROOT权限面前也是不堪一击的。所以我们在使用SharedPreferences的时候尽量不要存储一些有关用户信息的数据,如果要存储那该怎么补救呢?我们可以把key和value的值采用加密算法加密一下。

三、使用示例

    简单设计一个应用,让用户输入姓名和年龄,然后将数据保存到SharedPreferences,之后读取SharedPreferences数据并在界面上显示出来。当用户修改数据重新保存以后,提示SharedPreferences数据发生变化。界面如下图:

这里写图片描述

首先是布局文件,

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingTop="50dp"    android:paddingLeft="100dp">    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:orientation="horizontal">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@string/name"            android:textAppearance="?android:attr/textAppearanceLarge"/>        <EditText            android:id="@+id/etWriteName"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:hint="@string/write_name"            android:textAppearance="?android:attr/textAppearanceLarge"/>    </LinearLayout>    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:orientation="horizontal">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@string/age"            android:textAppearance="?android:attr/textAppearanceLarge"/>        <EditText            android:id="@+id/etWriteAge"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:hint="@string/write_age"            android:textAppearance="?android:attr/textAppearanceLarge"/>    </LinearLayout>    <Button        android:id="@+id/write"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/write"        android:textAppearance="?android:attr/textAppearanceLarge"/>    <Button        android:id="@+id/read"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/read"        android:textAppearance="?android:attr/textAppearanceLarge"/>    <TextView        android:id="@+id/tvReadData"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:textAppearance="?android:attr/textAppearanceLarge"/></LinearLayout>

然后是主程序代码,

package cl.fjnu.edu.cn.sharedpreferencestest;import android.app.Activity;import android.content.Context;import android.content.SharedPreferences;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity{    SharedPreferences preferences;    SharedPreferences.Editor editor;    private EditText etWriteName;    private EditText etWriteAge;    private TextView tvRead;    private Button read;    private Button write;    @Override    public void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        etWriteName = (EditText) findViewById(R.id.etWriteName);        etWriteAge = (EditText) findViewById(R.id.etWriteAge);        tvRead = (TextView) findViewById(R.id.tvReadData);        write = (Button) findViewById(R.id.write);        read = (Button) findViewById(R.id.read);        //获取只能被本应用程序读写的SharedPreferences对象,        //保存的文件名为sharedPrefFile.xml        preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);        //实例化SharedPreferences.Editor对象        editor = preferences.edit();        //注册数据变化监听        preferences.registerOnSharedPreferenceChangeListener(      onSharedPreferenceChangeListener);        write.setOnClickListener(new OnClickListener()        {            @Override            public void onClick(View arg0)            {                // 存入用户姓名                editor.putString("name", etWriteName.getText().toString());                //存入用户年龄                editor.putInt("age",Integer.parseInt(etWriteAge.getText().toString()));                // 提交所有存入的数据                editor.commit();            }        });        read.setOnClickListener(new OnClickListener()        {            @Override            public void onClick(View arg0)            {                // 读取字符串数据                String name = preferences.getString("name", "");                // 读取int类型的数据                int age = preferences.getInt("age", 0);                //显示读取出来的数据                tvRead.setText("姓名: "+name+"\n年龄: "+age);            }        });    }    //数据变化监听器    SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener            = new SharedPreferences.OnSharedPreferenceChangeListener() {        @Override        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {            Toast.makeText(MainActivity.this,"sharedPrefTest文件数据发生了变化!",Toast.LENGTH_SHORT).show();        }    };}

运行效果:

    在姓名和年龄输入框分别输入数据,然后点击保存数据,再读取数据,可以看到下方显示出用户保存的SharedPreferences数据。如下图,

这里写图片描述

    修改输入数据,点击保存数据重新保存,则弹出Toast消息,提示SharedPreferences数据已发生变化。如下图所示,

这里写图片描述

    此时再点击读取数据,则会显示出更新过的SharedPreferences数据。如下图,

这里写图片描述

原创粉丝点击