数据存储和提供器

本文详细介绍了Android系统中四种数据存储方式:SharedPreference用于简单配置信息存储;SQLite是轻量级数据库,适用于数据持久化;ContentProvider实现应用间数据共享;文件存储适合大量数据,但更新不便。每种方式的特点、使用场景及操作方法均进行了阐述。
摘要由CSDN通过智能技术生成

作为一个完整的应用程序,数据的存储与操作是必不可少的。Android系统为我们提供了4种数据存储方式,分别是:SharedPreference、SQlite、ContentProvider和文件。

(1)SharedPreference存储:一种常用的数据存储方法,其本质就是基于xml文件存储键值对(key-value)数据,通常用来存储一些简单的配置信息。

(2)SQLite存储:一个轻量级的数据库,支持基本SQL语法,是Android系统中常被采用的一种数据存储方式。Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的API。

(3)ContentProvider存储:它是Android系统中能实现应用程序之间数据共享的一种存储方式。由于Android系统中的数据基本都是私有的,存放于"data/data/程序包名(package name)"目录下,所以要实现数据共享,正确的方式是使用ContentProvider。由于数据在各应用间通常是私密的,虽然此存储方式较少使用,但又是必不可少的一种存储方式。如果应用程序有数据需要共享时,就需要使用ContentProvider为这些数据定义一个URI(包装成Uri对象),其他的应用程序就通过ContentProvider传入这个URI来对数据进行操作。

(4)文件存储:即常说的文件存储方式,常用于存储数量较大的数据,但缺点是更新数据是一件困难的事情。

1.SharedPreference存储

SharedPreference将数据以键值对(key-value)的形式保存至xml文件中,而生成的xml文件保存于"data/data/程序包名(package name)/shared_prefs"目录下。SharedPreference使用非常简单,能够轻松地存放数据和读取数据,但只能保存基本数据类型的值。

       @SuppressLint("WrongConstant") SharedPreferences sharedPreferences = getSharedPreferences("type",Context.MODE_APPEND);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        //String字符类型
        editor.putString("String","words");
        //Boolean布尔型
        editor.putBoolean("Boolean",true);
        //Integer整型
        editor.putInt("Integer",1);
        //长整型
        editor.putLong("Long",100000);
        //Float浮点数型
        editor.putFloat("Float",3.15f);
        editor.commit();

生成的SharedPreference文件名为type.xml,保存在应用程序文件夹下的shared_prefs文件内,程序包名为"com.XXX"。

        <map>
            <float name="Float" value = "3.5"/>
            <Long name="Long" value = "100000"/>
            <boolean name="Boolean" value = "true"/>
            <String name="String" >words</String>
            <int name="Integer" value = "1"/>
        </map>

SharedPreferences对象本身只能获取数据,而不支持存储和修改。通过SharedPreference中的edit()方法可以获得响应的Editor对象,由Editor对象完成SharedPreferences中数据的存储和修改。

1.使用SharedPreferences保存键值对(key-value)

(1)使用Activity类的getSharedPreferences(String name,int mode)方法获得SharedPreferences对象,其中存储键值对(key-value)的文件的名称由getSharedPreferences方法的name指定,打开方式由mode指定。

(2)使用SharedPreferences的edit()方法获得SharedPreferences.Editor对象

(3)通过SharedPreferences.Editor的putXxx(String key,Xxx value)方法写入键值对(key-value)。

(4)通过SharedPreferences.Editor的commit()方法提交并保存键值对(key-value)

2.使用SharedPreferences读取键值对(key-value)

(1)使用Activity类的getSharedPreferences(String name,int mode)方法获得所要读取的SharedPreferences对象,其方法与上述保存键值对(key-value)的第(1)步相同

(2)使用SharedPreferences接口的getXxx(String key,Xxx value)方法,可以方便地获得对应键(key)的值(value)

2.SQLite存储

SQLite是一种轻量级数据库系统,以嵌入式操作系统为设计目标,占用的资源低,因此常被作为手机操作系统的本地数据库。它还是开源的,任何人可以使用。

2.1SQLite简介

SQLite由以下几个组件组成:SQL编译器、内核、后端以及附件。SQLite利用虚拟机和虚拟数据库引擎(VDBE),使调试、修改和扩展SQLite的内核变得更加方便。

2.2SQLite使用

它创建一个表时,可以在CREATETABLE语句中指定某列的数据类型,具体有以下5种类型的列。

(1)TEXT:使用NULL、TEXT或者BLOB存储任何插入此列的数据,如果数据是数字类型,则会转换为TEXT类型。

(2)NUMERIC:可以使用任何存储类型,它首先视图将插入的数据转换为REAL或INTEGER类型,如果成功则存储为REAL和INTEGER类型,否则不加改变地存入。

(3)INTEGER:和NUMERIC类型类似,只是它将可以转换为INTEGER类型的值都转换为INTEGER类,如果是REAL类型,且没有小数部分,也转换为INTEGER类型。

(4)REAL:和NUMERIC类型类似,只是它将可以转换为REAL和INTEGER类型的值都转换为REAL类型。

(5)NONE:不做任何改变的尝试。

实际上,SQLite内部仅有下列5种存储值的类型。

(1)NULL:空值

(2)INTEGER:整型,根据大小可以使用1、2、3、4、6、8位来存储

(3)REAL:浮点数

(4)TEXT:字符串

(5)BLOB:二进制大数据

当某个值插入数据库时,SQLite将检查这个值的类型。如果该值的类型与关联列的类型不匹配,SQLite就尝试将该值的类型转换成该列的类型;如果不能转换,就将该值作为其本身具有的类型存储。

在Android中,SQLite的使用涉及两个重要的类:SQLiteDatabase和SQLiteOpenHelper。

1.SQLiteOpenHelper类

SQLiteOpenHelper是SQLite的数据库辅助类,且是一个抽象类,用来管理数据库的创建和版本的管理,使用时必须实现它的onCreate(SQLiteDatabase db)方法和onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)方法。SQLiteOpenHelper的常用方法如下。

DatabaseHelper继承自SQLiteOpenHelper类,重写了onCreate()与onUpgrade()方法,并将这两个方法作为TableCreateInterface接口方法开放,供各个数据表进行实现。onCreate(SQLiteDatabase db)方法用于各个表的创建,具体表的创建由TableCreateInterface接口实现,在各自的onCreate方法中进行创建。onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)方法用于各个表的更新,具体表的更新由TableCreateInterface接口实现,在各自的onUpgrade方法中进行更新。注意,只有在真正操作数据库,如调用getReadableDatabase()或者getWritableDatabase()方法时,数据库才会被创建。创建的数据库文件位于"data/data/<package name>/databases/<数据库名>.db"。

2.SQLiteDatabase类

SQLiteDatabase类作为SQLite的数据库实体类,用于对数据库进行增删改查等操作,常用方法有insert()、delete()、update()、query()等。

表5 SQLiteDatabase类的常用方法
方法名称含义
public long insert(String table,String nullColumnHack,ContentValues values)table指定要插入数据表的名称,values类似一个map通过键值对的形式存储,其中的键必须与表中的字段名相同。当插入空行时,我们要在这里指定一个列名nullColumnHack,如果发现将要插入的行为空行时,就会将指定的这个列名的值设为null,然后再向数据库中插入。
public int delete(String table,String whereClause,String[] whereArgs)用于删除表中的一条记录。table为要删除的表名,whereClause指定要根据哪个列字段参数进行删除,whereArgs是删除的具体依据参数。
public int update(String table,ContentValues values,String whereClause,String[] whereArgs)用于修改数据表中的一条数据。table指定要删除的数据表,whereClause指定根据哪列字段参数进行删除,whereArgs是删除的具体依据参数。values用来保存需要修改的值
public Cursor query(String table,String[] columns,String selection,String[] selectionArgs,String groupBy,String having,String orderBy,String limit)用于查询数据表中的信息,获得指向对应数据的游标。table指定要删除数据的表的名称;columns指定需要查询的列,selection与selectionArgs参数的用法同wgereClause和whereArgs的用法相同;groupBy与having是与合计函数(Aggregate Functions),orderBy指定查询数据顺序,可以以其中一个字段的升降序进行查询,如以"ID"字段的降序排列,则orderBy为"ID DESC",而ASC代表升序;limit则指定限制查询数据的个数。

3.ContentProvider存储

Android平台中,ContentProvider是在不同应用程序之间实现数据共享的唯一机制。如果需要让别的应用程序能够操作一个应用程序自己的数据,就可采用这种机制。比如,我们操作手机里面的联系人、多媒体等一些信息,都可以用ContentProvider来实现。

一个程序可以通过一个ContentProvider的抽象接口将自己的数据完全暴露出去。ContentProvider是类似数据库中表的方式将数据暴露,就像一个“数据库”。外界获取其提供的数据,与从数据库中获取数据的操作基本一样,这时就用到另一个类(ContentResolver),并通过Uri来操作数据从而实现对数据的处理。

Uri是一个通用资源标识符,被分为A、B、C、D4个部分,如图

(1)A:无法改变的标准前缀,如"content://"和"tel://"等。当前缀是"content://"时,说明可通过ContentProvider控制这些数据。

(2)B:Uri的标识,它通过authorities属性声明,用于限制是哪个ContentProvider能够有权提供这些数据。对于第三方应用程序,为了保证Uri标识的唯一性,它必须是一个完整的类名。

(3)C:路径,可以近似理解为需要操作的数据库中表的名字。如"content://com.androidbook.client.contentprovider.DataProvider/Topic"中的Topic。

(4)D:如果Uri中包含表示需要获取的记录的ID,就返回该ID对于的数据;如果没有ID,就表示返回全部。

因为Uri代表了要操作的数据,所以经常需要解析,并从中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris。

1.UriMatcher的常用方法

 UriMatcher的常用方法如下表。

表6 UriMatcher的常用方法
方法名称含义
public void addURI(String authority,String path,int code)往UriMatcher类里添加一个拼凑的Uri,在此我们可以将UriMatcher理解为一个Uri的容器,这个容器包含着即将操作的Uri,它用于我们业务逻辑的处理。path相当于Uri中的路径,也就是想要操作的数据表名。如果通过下面的match()方法匹配成功,就返回code值。
public int match(Uri uri)与传入的Uri匹配,它会首先找到之前通过addURI方法添加的Uri匹配,如果匹配成功,就返回之前设置的code值,否则返回一个值为-1的常量UriMatcher.NO_MATCH

 

 //定义两个Uri匹配的返回值code
    private static final int topic = 1;
    private static final int privateLetter = 2;
    //定义AUTHORITY
    public static String AUTHORITY = "com.androidbook.client.contentprovider.DataProvider";
    ...
       //当没有匹配成功是,返回NO_MATCH的值
        UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        //匹配Topic表的Uri,匹配成功后返回topic整数值
        uriMatcher.addURI(AUTHORITY,"Topic",topic);
        //匹配PrivateLetter表的Uri,匹配成功后返回privateLetter整数值
        uriMatcher.addURI(AUTHORITY,"PrivateLetter",privateLetter);
        //使用match方法
        Uri uri = Uri.parse("content://com.androidbook.client.contentprovider.DataProvider/Topic");
        //进行匹配,获得返回的code值,可以根据code值判别并进行不同的操作
        int code = uriMatcher.match(uri);

2.ContentUris的常用方法

表7 ContentUris的常用方法
方法名称含义

public static Uri withAppendedId(Uri contentUri,long id)

用于为路径加上ID
public static long parseId(Uri contentUri)从路径中获取ID

 

        //首先定义一个Uri
        Uri uri = Uri.parse("Content://com.example.demo_for_exercise.contentprovider.DataProvider/Topic");
        //为Uri添加ID
        Uri newUri = ContentUris.withAppendedId(uri,3);
        //从newUri中提取出ID,若为空,返回-1
        long id = ContentUris.parseId(newUri);

3.ContentProvider的常用方法

表8 ContentProvider的常用方法
方法名称含义
public abstract boolean onCreate()

在ContentProvider创建后被调用

public abstract Uri insert(Uri uri,Content Values values)根据Uri插入values对应的数据
public abstract int delete(Uri uri,String selection,String[] selectionArgs)根据Uri删除selection指定条件匹配的全部记录
public abstract int update(Uri  uri,ContentValues values,String selection,String[] selectionArgs)根据Uri修改selection指定条件匹配的全部记录
public abstract Cursor query(Uri  uri,ContentValues values,String selection,String[] selectionArgs,String sortOder)根据Uri查询出selection指定条件匹配的全部记录,并可以指定查询哪些列,并以什么方式(sortOder)排序
public abstract String getType(Uri uri)返回当前Uri所指向数据的MIME类型(Multipurpose Internet Mail Extensions多用途互联网邮件扩展类型,即指定用什么东西打开该文件),MIME类型由自己定义。

那么具体该如何公开数据并且在外部对其操作呢?

(1)在当前应用程序中定义一个ContentProvider类,继承ContentProvider并重写它的几个抽象方法

(2)首先要定义UriMatcher类,分别加入对应的Uri以及要返回的code值,以便另一个程序进行调用时对Uri进行判别,并通过匹配不同的Uri来分别操作不同的表。

(3)需要在当前应用程序的AndroidManifest.xml文件中注册ContentProvider。android:authorities属性定义了是哪个ContentProvider提供这些数据,格式为:provider所在的包的名称+provider本身定义的名称。而android:name指定具体的ContentProvider类

        <provider
            android:authorities="com.example.demo_for_exercise.contentprovider.DataProvider"
            android:name="com.example.demo_for_exercise.contentprovider.DataProvider">
        </provider>

(4)其他应用程序通过ContentResolver和Uri来获取ContentProvider的数据。

4.文件存储

之前我们介绍了Android利用SharedPreferences与SQLite进行数据存储。使用SharedPreferences存储的内容是一些键值对(key-value),使用SQLite数据库来操作存储的数据表。如果我们要存储的是一些文件,就可以采用文件存储的方式。

文件存储可以分为两类。

(1)将文件存储在应用程序内。在Android系统中,这些文件保存在"data/data/<packagename>/files/"目录下,称为文件存储。

(2)将文件存储在外接的存储设备中,也就是存储在SDcard存储卡中,称为SDCard存储。

  • 首先我们研究第一种文件存储→File存储。Android中读取/写入文件的方法与java中的I/O是一样的,利用openFileInput()方法与FileInputStream对象来读取设备上的文件,并利用openFileOutput()方法与FileOutputStream对象来创建文件。但是在File存储状态下,不同的程序之间不能共享。以上两个方法只支持保存、读取该应用程序目录下的文件。创建的文件保存在"/data/data/<package name>/files"目录下,读取非自身目录下的文件将会抛出FileNotFoundException异常。
     
表9 文件存储的常用操作

方法名称

含义
openFileOutput(String name,int mode)

保存文件内容,打开指定的私有文件输出流,返回类型FileOutputStream。name为要打开的文件名,不能包含路径分隔符。mode为操作模式,有以下4种保存模式。

(1)Environment.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问。在该模式下写入的内容会覆盖源文件的内容。

(2)Environment.MODE_APPEND:检查文件是否存在,存在就追加内容,否则就创建新文件

(3)Environment.MODE_WORLD_READABLE:表示当前文件可以被其他应用读取

(4)Environment.MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。

在使用模式时,可以用“+”来选择多种模式,比如openFileOutput(FileName,Environment.MODE_PRIVATE+Environment.MODE_WORLD_READABLE)

openFileInput(String name)读取文件内容,打开指定的私有文件输出流,返回类型为FileInputStream。name为要打开的文件名,不能包含路径分隔符。File存储所创建的文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文件时,指定Environment.MODE_WORLD_READABLE和Environment.MODE_WORLD_WRITEABLE
deleteFile(String name)删除指定的文件,返回值类型为boolean。name为要删除的文件名,不能包含路径。
getDir(String name,int mode)在应用程序的数据文件下获取或创建name对应的子目录,返回值类型为File
getFilesDir()

得到该应用程序数据文件夹的绝对路径,返回值类型为File

fileList()得到该应用程序数据文件夹下的全部文件的文件名,返回值类型为String[]
  //File存储,创建并保存文件
    public void saveFileInApplication(){
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = this.openFileOutput("file.txt", Context.MODE_APPEND);
            fileOutputStream.write("保存文件到应用程序下".getBytes());
            fileOutputStream.close();
            Toast.makeText(MainActivity.this,"保存文件成功",Toast.LENGTH_SHORT).show();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Toast.makeText(MainActivity.this,"保存文件失败",Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            Toast.makeText(MainActivity.this,"保存文件失败",Toast.LENGTH_SHORT).show();
        }

    }
    //File文件存储--读取文件
    public void readFileInApplication(){
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = this.openFileInput("file.txt");

            byte[] buffer = new byte[1024];
            ByteArrayInputStream stream = null;
            int length = -1;
            while((length=fileInputStream.read(buffer))!= -1){
                stream = new ByteArrayInputStream(buffer);
            }
            stream.close();
            fileInputStream.close();
            textView.setText(stream.toString());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 然后我们来学习SDCard存储。Android也为我们提供了一些关于SDCard的相关方法,这些方法方便开发者进行操作。Environment这个类为我们提供了操作的便利。
表10 Environment的常用方法
方法名称含义
getDataDirectory()获得Android下的data文件夹的目录,返回值类型为File
getDownloadCacheDirectiory()获得AndroidDownload/Cache内容的目录,返回值类型为File
getExternalStorageDirectory()获得Android外部存储器也就是SDCard的目录,返回值类型为File
getExternalStorageState()

获得Android外部存储器的当前状态,返回值类型为String,有以下9中保存模式。

(1)Environment.MEDIA_BAD_REMOVAL:在没有正确卸载SDCard之前就移除。

(2)

(3)

(4)

(5)

(6)

(7)

(8)

(9)

getRootDirectoty()获取Anroid下的root文件夹的目录,返回值类型为File

 

以上5个方法都是静态方法,可以直接用Environment的类名调用。

下面通过详细的步骤来说明对SDCard的读取操作。

(1)要在AndroidManifest.xml文件中添加权限

    <!-- 在SDCard中创建与删除文件权限-->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <!--  往SDCard写入数据权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

(2)用Environment.getExternalStorageStata().equals(Environment.MEDIA_MOUNTED)来判断这台手机设备上是否有SDCard且具有读写SDCard的权限。

(3)调用Environment.getExternalStorageDirectory()获得外部存储器的目录

(4)使用I/O流对外部存储器File类进行文件的读、写等操作。

具体操作可以参考:https://blog.csdn.net/yoryky/article/details/78675373

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值