数据存储之SharedPreferences

简介: 数据存储之SharedPreferences

在Android系统中提供了多张存储技术,这些存储技术可以讲数据保存在各种存储介质上。例如SharedPreferences可以将数据保存在应用软件的私有存储区,这些存储区中的数据只能被写入这些数据的软件读取。除此之外,Android系统还支持文件存储、SQLite数据库、OBB文件、云存储等


SharedPreferences的基本用法


SharedPreferences处理的就是一个key-value对。

它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数 据,其背后是用xml文件存放数据,使用简易的键值对存储。

//文件命名为demo,私有模式
SharedPreferences sharedPreferences = getSharedPreferences("demo", Context.MODE_PRIVATE);
Editor editor = sharedPreferences.edit();//获取编辑器
editor.putString("name", "hello");
editor.putInt("age", 6);
editor.commit();//提交修改

这样,我们就把信息存储到了/data/data/<packagename>/shared_prefs/demo.xml文件里面,现在你打开这个文件,就可以看到内容已经被存储。如下:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="name">hello</string>
<int name="age" value="6" />
</map>


getSharedPreferences(name,mode) 方法的第一个参数用于指定该文件的名称,最好定义为一个静态字符串,另外,名称如上面所示,不用带后缀名,后缀名会由系统自动加上。方法的第二个参数指定 文件的操作模式,共有四种操作模式,这四种模式想必大家都有一定的了解。这里简单说一下:


Type Commens
MODE_PRIVATE 默认模式,在创建的文件只能该应用能够使用(或所有的应用程序共享同一个用户标识号)。
MODE_APPEND 如果文件已经存在,在文件内容后面添加。
MODE_WORLD_READABLE 允许其他应用读该应用创建的文件。
MODE_WORLD_WRITEABLE 允许其他应用写该应用创建的文件。


所以,如果你希望SharedPreferences背后使用的xml文件能被其他应用读和写,可以指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。


另外Activity还提供了另一个getPreferences(mode)方法操作SharedPreferences,这个方法默认使用当前类不带包名的类名作为文件的名称。


如果我们的模式设置为Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限,我们其他的应用是可以访问的,下面是其他应用访问的代码(假如上面代码的包名为cn.test.demo):

try{
 Context context = createPackageContext("cn.test.demo",  Context.CONTEXT_IGNORE_SECURITY);
} catch (NameNotFoundException e){
 e.printStackTrace();
}

CreatePackageContext这个方法有两个参数:

1.packageName 包名,要得到Context的包名

2.flags 标志位,有CONTEXT_INCLUDE_CODE和CONTEXT_IGNORE_SECURITY两个选项。


CONTEXT_INCLUDE_CODE的意思是包括代码,也就是说可以执行这个包里面的代码。

CONTEXT_IGNORE_SECURITY的意思 是忽略安全警告,如果不加这个标志的话,有些功能是用不了的,会出现安全警告。


CreatePackageContext方法在找不到包名的时候会报NameNotFoundException异常,所以我们要捕获它。


数据的存储位置和格式


文件存放在/data/data/<packagename> /shared_prefs目录下


存取复杂类型的数据


不建议使用,但是确实可以存储复杂类型的数据。


如果想用SharedPreferences存取更加复杂的数据类型(对象图像等),就需要对这些数据进行编码,通常会将复杂类型的数据转换成Base64格式的编码,然后将转换后的数据以字符串的形式保存在xml文件中。


效果图

20160308225239912.gif


说明

本例将一个Product对象和一个图像保存在xml文件中,并在程序重新运行后从xml文件装载Product和图像。

Code

Product.java

import java.io.Serializable;
/**
 * MyApp
 *
 * @author Mr.Yang on 2016-02-21  17:08.
 * @version 1.0
 * @desc  必须可序列化,需要实现Serializable
 */
public class Product implements Serializable
{
    public String name;
    public int price;
}

我们使用了存放在res/drawable中的图片,下面的代码将该图像保存在base64.xml文件中。

将该图像保存在base64.xml文件中。

 try {
            SharedPreferences sharedPreferences = getSharedPreferences("base64", Activity.MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            // 读取和压缩R.drawable.item10,并将其压缩结果保存在ByteArrayOutputStream中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            BitmapFactory.decodeResource(getResources(), R.drawable.item10).compress(Bitmap.CompressFormat.JPEG, 50, baos);
            // 对压缩后的字节进行Base6编码
            String imageBase64 = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
            // 保存转换后的Base64格式字符串
            editor.putString("image", imageBase64);
            editor.commit();
            // 关闭输出流
            baos.close();
            Toast.makeText(this, "onClick_Save_Image 成功", Toast.LENGTH_LONG).show();
        } catch (Exception e) {
        }


读取并显示在ImageView上

 try {
            SharedPreferences sharedPreferences = getSharedPreferences("base64", Activity.MODE_PRIVATE);
            // 读取Base64格式的图片数据
            String imageBase64 = sharedPreferences.getString("image", "");
            // 对Base64格式的字符串进行解码,还原成字节数组
            byte[] imageBytes = Base64.decode(imageBase64.getBytes(), Base64.DEFAULT);
            ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
            // 在控件上显示图像
            ImageView imageView = (ImageView) findViewById(R.id.image);
            imageView.setImageDrawable(Drawable.createFromStream(bais, "image"));
            // 关闭输入流
            bais.close();
            Toast.makeText(this, "onClick_Read_Image 成功", Toast.LENGTH_LONG).show();
        } catch (Exception e) {
        }


将Product保存到base64.xml中

  try {
            Product product = new Product();
            product.name = "如来神掌";
            product.price = 1500;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            // 将Product对象保存到ObjectOutputStream中
            oos.writeObject(product);
            SharedPreferences sharedPreferences = getSharedPreferences("base64", Activity.MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            // 将Product对象转换成byte数组,并将其进行Base64编码
            String productBase64 = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
            // 将编码后的字符串保存到base64.xml中
            editor.putString("product", productBase64);
            editor.commit();
            // 输出流关闭
            oos.close();
            Toast.makeText(this, "onClick_Save_Serializable_Object 成功", Toast.LENGTH_LONG).show();
        } catch (Exception e) {
        }


从base64.xml中读取Product对象

 try {
            SharedPreferences sharedPreferences = getSharedPreferences("base64", Activity.MODE_PRIVATE);
            // 读取Product对象的Base64格式的字符串
            String base64Product = sharedPreferences.getString("product", "");
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //将base64格式字符串还原成byte数组
            byte[] productBytes = Base64.decode(base64Product.getBytes(), Base64.DEFAULT);
            ByteArrayInputStream bais = new ByteArrayInputStream(productBytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            // 将byte数组转换成Product对象
            Product product = (Product) ois.readObject();
            Toast.makeText(this,
                    "name:" + product.name + "\nprice:" + product.price,
                    Toast.LENGTH_LONG).show();
            // 关闭输入流
            ois.close();
        } catch (Exception e) {
        }


设置数据文件的访问权限

 /**
     * MODE_WORLD_READABLE  MODE_WORLD_READABLE
     * "This constant was deprecated in API level 17.
     * Creating world-readable files is very dangerous,
     * and likely to cause security holes in applications.
     * It is strongly discouraged; instead,
     * applications should use more formal mechanism for interactions
     * such as ContentProvider, BroadcastReceiver, and Service.
     * There are no guarantees that this access mode will remain on a file,
     * such as when it goes through a backup and restore.
     * File creation mode: allow all other applications to have read access to the created file."
     */
    private void createDiffPermissionSpFile() {
        int[] modes = new int[]{Activity.MODE_PRIVATE, Activity.MODE_APPEND,
                Activity.MODE_WORLD_READABLE,
                Activity.MODE_WORLD_READABLE};
        for (int i = 0; i < modes.length; i++) {
            SharedPreferences sharedPreferences = getSharedPreferences("data" + String.valueOf(i + 1), modes[i]);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putString("name", "不同的访问权限");
            editor.commit();
        }
    }


观察生成的文件的权限,同linux文件系统


可以保存设置的Activity:PreferenceActivity


概述

Android SDK提供了更加方便的方法来实现配置界面,并且可以透明的保存配置信息,这就是PreferenceActivity.


PreferenceActivity是Activity的子类,该类封装了SharedPreference,因此PreferenceActivity的所有子类都会拥有保存key-value的能力。


通过PreferenceActivity生成的XML文件在data/data/<工程名>/shared_prefs/下,名字为“<工程名>_Preference.xml。


在PreferenceActivity中,所有的修改都会自动更新该XML文件,不需要我们手动去设置大量的监听。


PreferenceActivity提供了一些常用的控件,可以满足大多数配置界面的要求, PreferenceActivity既可以从xml文件中创建,也可以通过代码的方式创建。


比较常用的控件有3个:


CheckBoxPreference:对应<CheckBoxPreference>标签,相当于CheckBox

EditTextPreference:对应<EditTextPreference>标签。单击该控件会弹出一个带有EditText的对话框

ListPreference:对应<ListPreference>标签,单击该控件会弹出一个带ListView的对话框。


效果图

20160308230339605.gif

PreferenceActivity在API11之后就废弃了,建议使用PreferenceFragment。


res目录下创建一个xml子目录,建立

preference_setting.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
    <PreferenceCategory android:title="我的位置源" >
        <CheckBoxPreference
            android:key="wireless_network"
            android:summary="使用无线网络查看应用程序(例如Google地图)中的位置"
            android:title="使用无线网络" />
        <CheckBoxPreference
            android:key="gps_satellite_setting"
            android:summary="定位时,精确到街道级别(取消选择可节约电量)"
            android:title="启用GPS卫星设置" />
    </PreferenceCategory>
    <PreferenceCategory android:title="个人信息设置" >
        <CheckBoxPreference
            android:key="yesno_save_individual_info"
            android:title="是否保存个人信息" />
        <EditTextPreference
            android:key="individual_name"
            android:summary="请输入真实姓名"
            android:title="姓名" />
        <PreferenceScreen
            android:key="other_individual_msg"
            android:summary="是否工作、手机"
            android:title="其他个人信息" >
            <CheckBoxPreference
                android:key="is_an_employee"
                android:title="是否工作" />
            <EditTextPreference
                android:key="mobile"
                android:summary="请输入真实的手机号"
                android:title="手机" />
        </PreferenceScreen>
    </PreferenceCategory>
</PreferenceScreen>


PreferenceActivityDemo

package com.turing.base.activity.dataStore.sharedPreference;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import com.turing.base.R;
/**
 *   3.0版本以后就需要使用PreferenceFragment,这里用过时的也没关系
 */
public class PreferenceActivityDemo extends PreferenceActivity implements Preference.OnPreferenceChangeListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 改变保存数据的xml使用的名称,默认是<package_name>_preferences.xml
        getPreferenceManager().setSharedPreferencesName("setting");
        // 从xml文件中加载布局
        addPreferencesFromResource(R.xml.preference_setting);
        // 获取"姓名"列表项对应的Preference对象
        Preference individualNamePreference = findPreference("individual_name");
        // 获得指向setting.xml文件的SharedPreferences对象
        SharedPreferences sharedPreferences = individualNamePreference.getSharedPreferences();
        // 设置列表项的Summary
        individualNamePreference.setSummary(sharedPreferences.getString("individual_name", ""));
        //设置"姓名"列表项是否可用
        if (sharedPreferences.getBoolean("yesno_save_individual_info", false))
            individualNamePreference.setEnabled(true);
        else
            individualNamePreference.setEnabled(false);
        // 设置包含onPreferenceChange事件的对象实例
        individualNamePreference.setOnPreferenceChangeListener(this);
        //mobile
        // 获取"mobile"列表项对应的Preference对象
        Preference mobilePreference = findPreference("mobile");
        // 获得指向setting.xml文件的SharedPreferences对象
        SharedPreferences mobileSP = mobilePreference.getSharedPreferences();
        // 设置列表项的Summary
        mobilePreference.setSummary(mobileSP.getString("mobile", ""));
        // 设置包含onPreferenceChange事件的对象实例
        mobilePreference.setOnPreferenceChangeListener(this);
    }
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        // 设置"姓名"列表项中Summary的值
        preference.setSummary(String.valueOf(newValue));
        // 必须返回为true,否则无法保存设置的值
        return true;
    }
    /**
     * 状态改变后的自动文件存储 需要继承PreferenceActivity的类和实现OnPreferenceChangeListener接口,
     * 重写onPreferenceTreeClick方法进行业务逻辑处理
     * @param preferenceScreen
     * @param preference
     * @return
     */
    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,Preference preference) {
        // 判断选中的是否为"是否保存个人信息"列表项的复选框
        if ("yesno_save_individual_info".equals(preference.getKey())) {
            // 设置姓名为可选或者不可选
            findPreference("individual_name").setEnabled(!findPreference("individual_name").isEnabled());
        }
        return super.onPreferenceTreeClick(preferenceScreen, preference);
    }
}


相关文章
|
6月前
|
存储 API 文件存储
47. 【Android教程】SharedPreferences 存储
47. 【Android教程】SharedPreferences 存储
74 2
|
4月前
|
存储 安全 API
Android经典实战之存储方案对比:SharedPreferences vs MMKV vs DataStore
本文介绍了 Android 开发中常用的键值对存储方案,包括 SharedPreferences、MMKV 和 DataStore,并对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。
143 1
|
7月前
|
存储 文件存储 数据库
Flutter的持久化存储:使用SharedPreferences等进行数据存储的技术解析
【4月更文挑战第26天】 Flutter应用持久化存储主要使用SharedPreferences,适合存储简单数据类型。通过shared_preferences插件,可轻松进行数据读写。注意异步操作、数据类型限制和安全性,对于复杂需求,可选择SQLite或文件存储。
|
SQL 存储 Java
数据存储之-SQLite数据库一
数据存储之-SQLite数据库一
168 0
|
API Android开发
安卓开发之使用SharedPreferences保存键值对数据
如果您有想要保存的相对较小键值对集合,则应使用 SharedPreferences API。SharedPreferences 对象指向包含键值对的文件,并提供读写这些键值对的简单方法。每个 SharedPreferences 文件均由框架进行管理,可以是私有文件,也可以是共享文件。
204 1
安卓开发之使用SharedPreferences保存键值对数据
|
存储 XML API
【Android】6.0SharedPreferences简单数据存储
如果您有想要保存的相对较小键值对集合,则应使用 SharedPreferences API。SharedPreferences 对象指向包含键值对的文件,并提供读写这些键值对的简单方法。每个 SharedPreferences 文件均由框架进行管理,可以是私有文件,也可以是共享文件。 例如:我们登录qq并不是每次都会要输入账号密码,而是会记住密码和用户的登录状态,这些轻量的数据可以通过SharedPreferences以键值对的方式存储在本地
82 0
|
存储 XML Linux
深入剖析Android中最简单的数据存储方式:SharedPreferences
深入剖析Android中最简单的数据存储方式:SharedPreferences
654 0
|
存储 文件存储 数据库
Android SharedPreferences存储数据与读取
SharedPreferences为Android五种数据存储的方式之一,其他四种数据存储分别为:
225 0
|
存储 文件存储 数据库
文件存储技术-数据持久化-Android
文件存储技术-数据持久化-Android
|
存储 安全 测试技术
DataStore —— SharedPreferences 的替代者 ?
DataStore —— SharedPreferences 的替代者 ?