Android 数据存储详解(SharedPreferences, 文件, Sqlite, ContentProvider)

文章大部分内容来自 < < Android开发全程实录 > >, 希望大家能看看原书

SharedPreferences

sharepreferences是Android中最轻量级的数据存储. 原理相信很多人也很清楚, 这里简单再介绍以下. 系统提供了SharedPreferences这个类, 所有用这个类存储的内容都会放在 data/data/< package name >/shares_prefs/… 目录下以xml文件的形式存储. xml文件中存储的都是键值对形式的内容. 一般可以存储java的简单类型(boolean, int, float, long, string, set< string > 等). 通常只使用SharedPreferences存储一些简单配置信息.

SharedPreferences存储基本流程

  1. 获取SharedPreferences 对象
  2. 获取Editor
  3. 写入数据
  4. editor.commit()
Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    SharedPreferences sp = getSharedPreferences("user_info", MODE_PRIVATE);
    SharedPreferences.Editor editor = sp.edit();
    editor.putString("username", "jake");
    editor.putInt("age", 20);
    editor.putBoolean("islogin", true);
    editor.commit();
}

查看结果如图:
这里写图片描述
导出 user_info.xml, 里面的内容是:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="username">jake</string>
    <boolean name="islogin" value="true" />
    <int name="age" value="20" />
</map>

获取SharedPreferences对象有两种方法:
1. context.getSharedPreferences("user_info", MODE_PRIVATE);
2. context.SharedPreferences sp = getPreferences(MODE_PRIVATE);

使用 getPreferences(mode)会直接使用 类名.xml 作为存储文件名, 注意只有Activity的context能使用 getPreferences(mode), 所以可以轻松的区分出每个activity保存的配置文件.
这里写图片描述

从SharedPreferences读取值

读取流程:
1. 获取到SharedPreferences对象
2. 使用get方法读取指定值, 这里注意, 如果SharedPreferences没有存储指定键的值, 可以使用默认值代替返回值.

SharedPreferences sp_read = getSharedPreferences("user_info", MODE_PRIVATE);
String username = sp_read.getString("username", "default_string");
int age = sp_read.getInt("age", 0);
boolean islogin = sp_read.getBoolean("islogin", false);
String not_exist = sp_read.getString("not_exist", null);
Log.e(TAG, "username:" + username + " age:" + age + " islogin:" + islogin + " not_exits:" + not_exist);

打印结果:

 E/MainActivity: username:jake age:20 islogin:true not_exits:null

SharedPreferences操作模式

这里写图片描述

封装SharedPreferences工具类

该工具类来自 hyman大神 的工具类, 很好用. 大家可自行尝试引入到自己项目中
SPUtils.java

package com.zhy.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import android.content.Context;
import android.content.SharedPreferences;

public class SPUtils
{
    public SPUtils()
    {
        /* cannot be instantiated */
        throw new UnsupportedOperationException("cannot be instantiated");
    }

    /**
     * 保存在手机里面的文件名
     */
    public static final String FILE_NAME = "share_data";

    /**
     * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
     * 
     * @param context
     * @param key
     * @param object
     */
    public static void put(Context context, String key, Object object)
    {

        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        if (object instanceof String)
        {
            editor.putString(key, (String) object);
        } else if (object instanceof Integer)
        {
            editor.putInt(key, (Integer) object);
        } else if (object instanceof Boolean)
        {
            editor.putBoolean(key, (Boolean) object);
        } else if (object instanceof Float)
        {
            editor.putFloat(key, (Float) object);
        } else if (object instanceof Long)
        {
            editor.putLong(key, (Long) object);
        } else
        {
            editor.putString(key, object.toString());
        }

        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
     * 
     * @param context
     * @param key
     * @param defaultObject
     * @return
     */
    public static Object get(Context context, String key, Object defaultObject)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);

        if (defaultObject instanceof String)
        {
            return sp.getString(key, (String) defaultObject);
        } else if (defaultObject instanceof Integer)
        {
            return sp.getInt(key, (Integer) defaultObject);
        } else if (defaultObject instanceof Boolean)
        {
            return sp.getBoolean(key, (Boolean) defaultObject);
        } else if (defaultObject instanceof Float)
        {
            return sp.getFloat(key, (Float) defaultObject);
        } else if (defaultObject instanceof Long)
        {
            return sp.getLong(key, (Long) defaultObject);
        }

        return null;
    }

    /**
     * 移除某个key值已经对应的值
     * 
     * @param context
     * @param key
     */
    public static void remove(Context context, String key)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.remove(key);
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 清除所有数据
     * 
     * @param context
     */
    public static void clear(Context context)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear();
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 查询某个key是否已经存在
     * 
     * @param context
     * @param key
     * @return
     */
    public static boolean contains(Context context, String key)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        return sp.contains(key);
    }

    /**
     * 返回所有的键值对
     * 
     * @param context
     * @return
     */
    public static Map<String, ?> getAll(Context context)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        return sp.getAll();
    }

    /**
     * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
     * 
     * @author zhy
     * 
     */
    private static class SharedPreferencesCompat
    {
        private static final Method sApplyMethod = findApplyMethod();

        /**
         * 反射查找apply的方法
         * 
         * @return
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private static Method findApplyMethod()
        {
            try
            {
                Class clz = SharedPreferences.Editor.class;
                return clz.getMethod("apply");
            } catch (NoSuchMethodException e)
            {
            }

            return null;
        }

        /**
         * 如果找到则使用apply执行,否则使用commit
         * 
         * @param editor
         */
        public static void apply(SharedPreferences.Editor editor)
        {
            try
            {
                if (sApplyMethod != null)
                {
                    sApplyMethod.invoke(editor);
                    return;
                }
            } catch (IllegalArgumentException e)
            {
            } catch (IllegalAccessException e)
            {
            } catch (InvocationTargetException e)
            {
            }
            editor.commit();
        }
    }

}

文件

Android文件操作就是使用Java的API, 这里需要注意的是 Android中的路径以及权限. Android中文件操作一般都用来存一些二进制文件和简单的文本, 例如图片, 视频等, 一般不用来存配置信息.

几个典型的路径

  1. 使用 context.this.openFileOutput("1.txt", MODE_PRIVATE);会将文件存储在 /data/data/< package name >/ files
    这里写图片描述
  2. context.getFilesDir(); 保存在 /data/user/0/< package name >/files
  3. Environment.getExternalStorageDirectory(); 保存在 /storage/emulated/0

注意: 这3个是比较典型的, 其中通过 context 获取的路径下的内容在 APP被卸载的时候都会被删除, 存在外接存储设备上的则不会被删除.

大家可以写代码测试:

        try {
            FileOutputStream fos = this.openFileOutput("1.txt", MODE_PRIVATE);
            OutputStreamWriter osw = new OutputStreamWriter(fos);
            String test = "test";
            osw.write(test, 0, test.length());
            fos.close();

            File file2 = this.getFilesDir();
            File file3 = this.getCacheDir();
            File file4 = Environment.getExternalStorageDirectory();
            File file5 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

            Log.e(TAG, "file2:" + file2);
            Log.e(TAG, "file3:" + file3);
            Log.e(TAG, "file4:" + file4);
            Log.e(TAG, "file5:" + file5);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

打印结果

file1:/data/data/< package name >/files // 这个看上面截图
file2:/data/user/0/org.yxm.filesimple/files
file3:/data/user/0/org.yxm.filesimple/cache
file4:/storage/emulated/0
file5:/storage/emulated/0/Pictures

文件读写权限:

如果要将文件写入外部存储设备, 需要一定的权限.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Sqlite

简介

这里写图片描述

管理工具

  1. SQLite Database Browser
  2. Sqlite Expert Professional (推荐)
  3. SqliteAdmin

使用方法

使用Sqlite数据库和使用普通数据库一样, 都是以下几个步骤:
1. 创建, 删除数据库
2. 增加数据
3. 删除数据
4. 修改数据
5. 查询数据

Android数据库核心类是: SQLiteDatabase, 为了让Android中的数据库更加好使用, Android还提供了: SQLiteOpenHelper来帮助管理数据库. 这里我只简单介绍数据库的基本操作, 后面再写一篇专门介绍 SQLiteOpenHelper的使用, 和对数据库的封装.

创建数据库
  1. 使用 context.openOrCreateDatabase(...), 数据库文件会默认保存到 /data/data/< package name >/ databases/…
SQLiteDatabase db = this.openOrCreateDatabase("mySqliteDb.db", MODE_PRIVATE, null);

这里写图片描述
2. 使用SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(getFilesDir() + "/mySqliteDb.db", null); 可以将数据库文件放在自定义目录中, 但是经过实验, 不能放在外部存储中, 会包下面的错误:

Caused by: android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database

所以建议还是建议使用方法一, 而且数据库本来就不应该被其他应用访问, 所以应该放在最隐私的位置. 如果想为其他应用提供数据, 可以使用后面介绍的 ContentProvider

创建table

使用 db.execSQL(createTableStr);来创建表, 例如:

String createTableStr = "CREATE TABLE IF NOT EXISTS account (" +
        "_id INTEGER PRIMARY KEY," +
        "username VARCHAR(60)," +
        "password VARCHAR(60)" +
        ");";
db.execSQL(createTableStr);
插入数据

当然可以直接使用原生sql语句, 使用 db.execSQL(insertStr); 执行插入, 但是Android为我们封装了键值对的map, 只需要构造键值对 ContentValues , 然后调用 insert 即可插入.

ContentValues values = new ContentValues();
values.put("username","yxm");
values.put("password", "123");
db.insert("account", null, values);
删除数据
db.delete("account", "_id>?", new String[]{"5"});
更新数据
ContentValues values = new ContentValues();
values.put("username", "mxy");
values.put("password", "321");
db.update("account", values, "_id<?", new String[]{"5"});
查询数据
Cursor cur = db.query("account", null, null, null, null, null, null);
if (cur.moveToFirst()) {
    while (!cur.isLast()) {
        int id = cur.getInt(0);
        String username = cur.getString(1);
        String password = cur.getString(2);
        Log.e(TAG, "id:" + id + " username:" + username + " password:" + password);
        cur.moveToNext();
    }
}
关闭数据库
db.close();

当然Adnroid还封装了很多其他关于 SQLiteDatabase 的方法, 大家自行探索, 都很easy.

ContentProvider

我们可以将ContentProvider 看做Android中一个应用向其他应用公开数据的接口, 做数据共享. Android系统本身就有一些 ContentProvider, 例如: Contacts, Browser, CallLog, Settrings 等. 当然我们也可以自己创建ContentProvider来向其他程序提供数据.

创建自己的ContentProvider

  1. 继承ContentProvider
  2. 声明 CONTENT_URI, 实现 UriMatcher
  3. 在 AndroidManifest.xml 中注册
继承ContentProvider

这里写图片描述
这里写图片描述
在抽象方法中我们发现一个重要的类: Uri, ContentProvider通过 Uri定位资源然后共享出来, Uri需要符合一定的格式, 来看看系统联系人的格式:
这里写图片描述
这里写图片描述

定义 CONTENT_URI, 实现UriMatcher
public static final Uri CONTENT_URI = Uri.parse("content://org.yxm.sqlitesimple");
在 AndroidManifest.xml 中注册
<provider
    android:authorities="org.yxm.sqlitesimple"
    android:name=".content.MyContentProvider" />

感觉继续写下去太长了, 下次写一篇新的放在最后

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值