Android数据持久化存储

瞬时数据:存储在内存当中,有可能因为程序关闭或其他原因导致内存被回收而丢失的数据。
数据持久化:数据持久化是指将那些内存中的瞬时数据,保存到存储设备中,保证即使在手机或者电脑关机的情况下,这些数据依然不会丢失。
保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的,持久化奇数则是提供了一种机制可以让数据在瞬时状态和持久状态之间进行转换
Android系统中主要提供了四种方法用于实现数据的持久化功能即
SharedPreferences、文件存储、SQLite数据库存储、ContentProvider
一、SharedPreferences
SharedPreferences是一种轻量级的数据存储机制,他将一些简单的数据类型的数据,包括boolean类型,int类型,float类型,long类型以及String类型的数据,以键值对的形式存储在应用程序的私有Preferences目录(/data/data/<包名>/shared_prefs/)中,这种Preferences机制广泛应用于存储应用程序中的配置信息。

获取SharedPreferences的三种方式:

  1.Context类中的getSharedPreferences()方法
  2.Activity类中的getPreferences()方法
  3.PreferencesManager类中的getDefaultSharedPreferences()方法

获取到SharedPreferences对象后,向SharedPreferences文件中存储数据可分为三步:

  1.调用SharedPreferences的edit()方法来获取一个    
    SharedPreferences.Editer对象
  2.向SharedPreferences.Editer对象中添加数据
  3.调用commit()方法将添加的数据提交

二、文件存储
Android平台主要通过java.io.FileInputStream和java.io.FileOutputStream这两个类来实现对文件的读写
java.io.File类则用来构造一个具体指向某个文件或者文件夹的对象。

需要注意的是:每个应用程序所在的包都会有一个私有的存储数据的目录,只有属于这个包中的应用程序才有写入的权限,每个包中应用程序的私有数据目录位于 Android系统中的绝对路径/data/data/<package>/目录中,除了私有目录,应用程序还拥有/sdcard目录,即 Android设备上的SD卡的写入权限。文件系统中其他的系统目录,第三方应用程序都是不可写的。

文件存储是Android最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有的数据都是原封不用的保存到文件当中,因此它比较适合用于存储一些简单的文本数据或二进制数据

将数据存储到文件中:
Context类提供了一个openFileOutput()方法,可以用于将数据存储到指定文件中。这个方法接受两个参数,第一个是文件名,在文件创建的时候使用的就是这个名称(注意,这里指定的文件名不可以包含路径,因为所有文件都是默认存储到/data/data/< package name>/files/目录下的)第二个参数是文件的操作模式。
openFileOutput()方法返回的是一个FileOutputStream对象,得到了这个对象之后就可以使用java流的方式将数据写入到文件中了。

 private Context mContext;

    public void writeDataToFile() {
        String data = "data to save";
        FileOutputStream fileOutputStream = null;
        BufferedWriter bufferedWriter = null;
        try {
            fileOutputStream = mContext.openFileOutput("file_name", Context.MODE_APPEND);
            bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream));
            bufferedWriter.write(data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bufferedWriter != null) {
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

从文件中读数据
Context类中提供了openFileInput()对象,用于从文件中读取数据,它只接收一个参数,即要读取的文件名,然后系统会自动到/data/data/< package name/files目录下加载这个文件,并返回一个FileInputStream对象。得到了这个对象再通过Java流的方式就可以将数据读取出来

public String readDataFromFile() {
        FileInputStream fileInputStream = null;
        BufferedReader bufferedReader = null;
        StringBuilder data = new StringBuilder();
        try {
            fileInputStream = mContext.openFileInput("file_name");
            bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
            String line;

            while ((line = bufferedReader.readLine()) != null) {
                data.append(line);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (bufferedReader!=null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return data.toString();
    }

tips:文件存储不适合保存一些复杂的文本数据
三、SQLite数据库存储
SQLite是一款轻量级的关系型数据库,它运算速度非常快,占用资源很少,通常只需要几百K的内存就足够了,因而特别适合在移动设备上使用。
Android系统的很多用户数据,如联系人信息,通话记录,短信息等,都是存储在SQLite数据库当中的,所以利用操作SQLite数据库的API可以同样方便的访问和修改这些数据。
数据库文件会存放在/data/data/< package name>/database/目录下

创建数据库:
Android提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单地对数据库进行创建开放
SQLiteOpenHelper是一个抽象类,想要使用的话,就需要创建一个自己的帮助类去继承他,然后重写onCreate()、onUpdate()和构造方法。
构造出SQLiteOpenHelper实例后,可以调用SQLiteOpenHelper的getReadableDatabase()或getWriteableDatabase(),这两个方法可以创建或打开一个数据库(如果数据库存在则直接打开,否则创建一个),并返回一个可以对数据库进行读写操作的SQLiteDatabase对象

四、ContentProvider(其实还是对数据库进行操作)

ContentProvider用途:
1、 ContentProvider提供为存储和获取数据提供了统一的接口
2、使用ContentProvider可以在不同的应用程序之间共享数据
3、 Android为常见的一些数据提供了ContentPrivider(包括音频,视频,图片和通讯录等等)

用法:
1.使用现有的内容提供器来读取和操作相应程序中的数据
2.创建自己的内容提供器给我们程序的数据提供外部访问接口

访问其他程序中的数据
获取到要访问应用程序的Uri,然后借助ContentProvider进行CRUD操作。

ContentResolver:
要想访问内容提供器中共享的数据,就一定要借助ContentResolver
ContentResolver中的增删改查方法都是不接收表名参数的,二是使用一个Uri参数代替,这个参数被称为Uri。Uri给内容提供器中的数据建立了唯一标识符

Uri:统一资源标识符,它的作用是根据这个Uri找到某个资源文件
一个Uri由以下几部分组成:
1.scheme:ContentProvider(内容提供者)的scheme已经由Android所规定为:content://
2.权限(或Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它
3.路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定
eg: content://com.gengxin.app.provider/table1
协议声明 权限(包名) 路径(表名)
Uri可以非常清楚的表达出我们想要访问哪个应用程序中的哪张表
eg:读取电话联系人

  private Context context;

    private void readContacts() {
        Cursor cursor = null;
        cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
        while (cursor.moveToNext()) {
            String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
        }
        if (cursor != null) {
            cursor.close();
        }
    }

创建内容提供器
创建应用的内容提供器,可以提供给其他应用外部访问接口,还可以保证数据的安全性,使隐私数据不会泄露出去

实现跨程序共享数据,官方推荐的方式就是使用内容提供器,可以通过一个类去继承ContentProvider的方式来创建一个自己的内容提供器。ContentProvider类中有六个抽象方法,我们在使用子类继承它的时候,需要将这六个方法全部重写

public class MyProvider extends ContentProvider {

    /*
    * 初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作
    * 只有当ContentResolver尝试访问我们程序中的数据时,内容提供器才会被初始化*/
    @Override
    public boolean onCreate() {
        return false;
    }


    /*使用uri参数来确定查询哪张表,查询结果存放在Cursor对象中返回*/
    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    /*根据传入的uri来返回相应的mime类型*/
    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    /*添加数据,添加完成后,返回一个用于表达这条新纪录的uri*/
    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    /*从内容提供器中删除数据,被删除的行数将作为返回值返回*/
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    /*更新内容提供器中已有的数据,受影响的行数将作为返回值返回*/
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }

回顾一下,一个标准的uri写法是这样的:
content://com.gengxin.app.provider/table1
这就表示调用方期望访问的是com.gengxin.app这个应用的table1表中的数据。除此之外,还可以在这个uri的后面加上一个id,如下所示:
content://com.gengxin.app.provider/table1/1
这就表示调用方期望访问的是com.gengxin.app这个应用的table1表中id为1的数据。可以使用通配符的方式来分别匹配这两种合适的uri,规则如下:
1. *: 表示匹配任意长度的任意字符
2. #:表示匹配任意长度的数字
所以一个能够匹配任意表中的uri格式就可以写成:
content://com.gengxin.app.provider/*
而一个能够匹配table1表中任意一行数据的uri格式可以写成:
content://com.gengxin.app.provider/table1/#
然后再借助UriMatcher这个类就可以实现匹配uri的功能。UriMatcher提供了addURI()方法,这个方法接受三哥参数,可以分别把权限、路径和一个自定义代码传进去。当调用UriMatcher的match()方法时,就可以讲一个uri对象传入,返回值是某个能够匹配这个uri对象所对应的自定义代码,利用这个代码,我们就可以判断出调用方期望访问的是哪张表中的数据

 public static final int TABLE1_DIR = 0;//访问table1表中所有的数据
    public static final int TABLE1_ITEM = 1;
    public static final int TABLE2_DIR = 2;
    public static final int TABLE2_ITEM = 3;
    private static final String AUTHORITY = "com.gengxin.app.provider";

    private static UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, "table1", TABLE1_DIR);
        uriMatcher.addURI(AUTHORITY, "table1/#", TABLE1_ITEM);
        uriMatcher.addURI(AUTHORITY, "table2", TABLE2_DIR);
        uriMatcher.addURI(AUTHORITY, "table2/#", TABLE2_ITEM);
    }


 /*使用uri参数来确定查询哪张表,查询结果存放在Cursor对象中返回*/
    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

        switch (uriMatcher.match(uri)) {
            case TABLE1_DIR://查询table1表中的所有数据
                break;
            case TABLE1_ITEM://查询table1表中的单条数据
                break;
            case TABLE2_DIR://查询table2表中的所有数据
                break;
            case TABLE2_ITEM://查询table2表中的单条数据
                break;
        }
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值