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

TypeCommens
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);
    }
}
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐