数据存储之SharedPreferences

来源:互联网 发布:淘宝平台服务协议 编辑:程序博客网 时间:2024/05/18 02:29

在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_CODECONTEXT_IGNORE_SECURITY两个选项。

CONTEXT_INCLUDE_CODE的意思是包括代码,也就是说可以执行这个包里面的代码。
CONTEXT_IGNORE_SECURITY的意思 是忽略安全警告,如果不加这个标志的话,有些功能是用不了的,会出现安全警告。

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


数据的存储位置和格式

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

存取复杂类型的数据

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

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

效果图

这里写图片描述

说明

本例将一个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的对话框。

效果图

这里写图片描述

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);    }}
1 0
原创粉丝点击