在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文件中。
效果图
说明
本例将一个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); } }