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

一、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,代码如下:

@Override
public 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数据。如下图,

这里写图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值