数据存储之SharedPreferences
在Android系统中提供了多张存储技术,这些存储技术可以讲数据保存在各种存储介质上。例如SharedPreferences可以将数据保存在应用软件的私有存储区,这些存储区中的数据只能被写入这些数据的软件读取。除此之外,Android系统还支持文件存储、SQLite数据库、OBB文件、云存储等SharedPreferences的基本用法SharedPreferences处理的就是一个key-val
在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_READABL
E和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文件中。
效果图
说明
本例将一个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);
}
}
更多推荐
所有评论(0)