Android数据存储方式总结



从总体出发,数据存储有三种方式:文件、数据库和网络。但从开发者来讲它分为以下5种方式:

1.SharedPreferences存储

2.InternalStorage内部存储空间

3.ExternalStorage外部存储空间

4.SQLite数据库

5.Internt网络

下面主要介绍前4种数据存储方式。

1.Shared Preferences

SharedPreferences是用来存储一些Key/Value类似的成对的基本数据类型,注意,它只能存储基本数据类型,也即int, long, boolean, String, float。事实上它完全相当于一个HashMap,唯一不同的就是HashMap中的Value可以是任何对象,而SharedPreferences中的值只能存储基本数据类型。

最适合SharedPreferences的地方就是保存配置信息,因为很多配置信息都是Key/Value,不论是系统的Settings还是各个应用中的SettingsSharedPreferences存储的本质是基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息。

其存储位置在/data/data/< >/shared_prefs目录下。

SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现。实现SharedPreferences存储的步骤如下:

一、根据Context获取SharedPreferences对象;

二、利用edit()方法获取Editor对象;

三、通过Editor对象存储key-value键值对数据;

四、通过commit()方法提交数据。

再谈谈一些使用SharedPreference时的技巧,它只能保存基本数据类型,但假如我想保存一个数组,该怎么做。可以把数据进行处理,把它转化成一个String,取出的时候再还原就好了;再如,如想保存一个对象,怎么办,同样,可以把对象序列化成为字符序列,或转成StringObject.toString()),或是把它的HashCodeObject.hashCode())当成Value保存进去。

总之,SharedPreferences使用起来很方便,能用它就尽量不要用文件或是数据库。

2.Internal Storage内部存储空间

所谓的内部存储与外部存储,是指是否是手机内置。手机内置的存储空间,称为内部存储,它是手机一旦出厂就无法改变,它也是手机的硬件指标之一,这里的内存应该是手机内部存储的简称为内存,而并非严格意义上的内存。

上面所谈到的Shared Preferences和下面要谈到的SQLite数据库也都是存储在内部存储空间上的。

Android本身来讲是一个Linux操作系统,所以它的内部存储空间,对于应用程序和用户来讲就是“/data/data"目录。它与其他的(外部的存储)相比有着比较稳定,存储方便,操作简单,更加安全(因为可以控制访问权限)等优点。而它唯一的缺点就是它比较有限。

虽然,可以非常容易的知道程序本身的数据所在路径,所有的应用程序的数据路径都是“/data/data/app-package-name/”,所有的程序用到的数据,比如libs库,SharedPreferences都是存放在这个路径下面。

使用内部存储主要有二个方式,一个是文件操作,一个是文件夹操作。无论哪种方式,Context中都提供了相应的函数来支持,使用Context不但操作简单方便,最重要的是Context会帮助我们管理这些文件,也可以方便帮助我们控制文件的访问权限。

Context中关于文件和文件夹操作的函数如下

1)创建一个文件,并打开成一个文件输出流,需要提供一个String,作为文件名

FileOutputStream  output = Context.openOutputFile(filenameContext.MODE_PRIVATE);

output.write(data);// useoutput to write whatever you like

output.close();

2)同样,想打开一个文件作为输入的话,也是只需要提供文件名

FileInputStreaminput = Context.openInputFile(filename);

input.read();

input.close();

3)列出所有的已创建的文件

String[] files= Context.fileList();

for (Stringfile : files)

  Log.e(TAG, "file is " + file);

}

4)删除文件,能创建就要能够删除,当然也会提供了删除文件的接口,它也非常简单,只需要提供文件名

if(Context.deleteFile(filename)) {

 Log.e(TAG, "delete file " + filename+ " sucessfully“);

} else {

 Log.e(TAG, "failed to delete file "+ filename);

}

5)获取文件已创建文件的路径,它返回一个文件对象用于操作路径

File fileDir =Context.getFileDir();

Log.e(TAG,"fileDir " + fileDir.getAbsolutePath();

总结一下文件相关操作,可以得出以下三个特点:

1. 文件操作只需要向函数提供文件名,所以程序自己只需要维护文件名即可;

2. 不用自己去创建文件对象和输入、输出流,提供文件名就可以返回File对象或输入输出流

3. 对于路径操作返回的都是文件对象。

3.External Storage外部存储空间

与内部存储空间相对,外部存储空间是指手机出厂的时候不存在,用户在使用时候可以自由添加的外部存储介质比如TS卡,SD卡等闪存储介质。Android也是不例外,它完全支持外部存储介质。

外部存储相关的使用方法:

1 Checkmedia availability检查介质的可用性

如前所述,外部存储介质的稳定性十分的差,所以在使用之前一定要先检查它的可用性,如果可用再去用。

final Stringstate = Environment.getExternalStorageState();

 if (state.equals(Environment.MEDIA_MOUNTED) ||state.equals(Environment.MEDIA_READ_ONLY)) {// sd card is ready to us }

注意:在程序中访问SDCard,你需要申请访问SDCard的权限。在AndroidManifest.xml中加入访问SDCard的权限如下:

  <!-- SDCard中创建与删除文件权限 -->

    <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

  <!-- SDCard写入数据权限 -->

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

(2)Get thedirectory获取外部存储卡的路径

事实上,外部存储卡的路径是“/mnt/sdcard",所以你直接这样写去访问也能访问的到。鉴于可读性和可移植性的考虑,建议这样写:

   File sdcardDir = Environment.getExternalStorageDirectory();

下面总结一下使用时应该注意的一些和外部存储的特点:

1. 外部存储卡不是随时想用就能够用的,所以一定要记得在使用之前检查它的可用性

2. 存储在外部存储卡上的数据是所有应用程序都可见,用户也可见(使用FileManager),所以安全性不是很好,虽然文档声称可以在外部存储卡上写程序私有数据,但没用,用FileManager仍然可以删除或编辑文件(Market上面的FileManager功能都十分的强大,能让用户看到SD卡中的所有文件,和操作能看到的文件)。

3. Android手机支持把外部存储卡MountPC做为U盘,当连接数据线时,这时SD卡变成了U盘连接到了另外的操作系统中。什么意思,就是在Android当中虽然有的文件属性(隐藏,私有等),到了PC上就不一定管用了,用户在PC上可以随意操作文件(这就是第二点中所提及的)。

4. 如果使用外部存储卡保存数据,一定要额外做好异常处理:外部存储卡不可用时把数据存入哪里;可用的时候再怎么同步数据(这是比较头疼的地方,可行的做法就是当SD卡不可用时不准用户写数据,但这用户体验又不是很好,很多应用都这么干);你的数据被破坏了。当然常见的异常也要考虑,比如空间满了,无法写入,磁盘坏道等。

4.SQLite数据库

Android对数据库的支持很好,它本身集成了SQLite数据库,每个应用都可以方便的使用它,或者更确切的说,Android完全依赖于SQLite数据库,它所有的系统数据和用到的结构化数据都存储在数据库中。

不同Activity和不同应用之间传递数据很麻烦,特别是对于大型数据结构,因为Activity虽是Java对象,但去无法像使用其他类对象那样去创建一个实例然后使用它,更无法给Activity加上SettersGetters(虽然这样做了没有编译错误)。比较好的解决方案就是把结构化数据写入数据库,然后在不同的Activity之间传递它们的UriSQLite Database数据库具有以下几个优点

1. 由专门的ContentProvider来帮忙管理和维护数据库

2. 可以方便的设置访问权限,私有还是公有都可见

3. 操作方便,使用标准的CRUDE语句,ContentResolver.query(),update(), delete() insert(),详见ContentResolver

4. 良好的可移植性和通用性,用标准的SQL语句就能实现CRUDE

一、SQLiteDatabase

应用程序获得了代表指定数据库的SQLiteDatabase对象,就能通过SQLiteDatabase对象来管理,操作数据库。

Android系统中提供了android.database.sqlite包,用于进行SQLite数据库的增、删、改、查工作。其主要方法如下:

openOrCreateDatabase(Stringpath, SQLiteDatabase.CursorFactory factory): 根据给定条件连接数据库,如果此数据库不存在,则创建。

beginTransaction(): 开始一个事务。

endTransaction(): 结束一个事务。

delete(String table, String whereClause,String[] whereArgs): 根据给定条件,删除符合条件的记录。

execSQL(String sql): 执行给定SQL语句。

insert(String table, String nullColumnHack,ContentValues values): 根据给定条件,插入一条记录。

query(String table, String[] columns,String selection, String[] selectionArgs, String groupBy, String having, StringorderBy): 执行查询。

rawQuery(String sql, String[]selectionArgs): 根据给定SQL,执行查询。

update(String table, ContentValues values,String whereClause, String[] whereArgs): 根据给定条件,修改符合条件的记录。

close(): 关闭连接,释放资源。

二、SQLiteOpenHelper

该类是SQLiteDatabase一个辅助类。这个类主要生成一个数据库,并对数据库的版本进行管理。在实际项目中很少使用SQLiteDatabase的方法打开数据库,通常会继承SQLiteOpenHelper开发子类,并通过该子类的getReadableDatabase()getWritableDatabase()方法打开数据库,并提供close()关闭数据库连接。当在程序当中调用这个类的方法getWritableDatabase()或者getReadableDatabase()方法的时候,如果当时没有数据,那么Android系统就会自动生成一个数据库。

SQLiteOpenHelper是一个抽象类,我们通常需要继承它,并且实现里面的3个函数:

1.onCreateSQLiteDatabase

在数据库第一次生成的时候会调用这个方法,也就是说,只有在创建数据库的时候才会调用,当然也有一些其它的情况,一般我们在这个方法里边生成数据库表。

 

2. onUpgradeSQLiteDatabaseintint

当数据库需要升级的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,当然是否还需要做其他的操作,完全取决于应用的需求。

3. onOpenSQLiteDatabase):

这是当打开数据库时的回调函数,一般在程序中不是很常使用。

三、Android内容提供器ContentProviderContectResolver的使用详解

1ContentProvider

当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。ContentProvider为存储和获取数据提供了统一的接口。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferencesAPI读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。

1ContentProvider使用表的形式来组织数据

无论数据的来源是什么,ContentProvider都会认为是一种表,然后把数据组织成表格

2ContentProvider提供的方法

 query:查询

 insert:插入

 update:更新

 delete:删除

 getType:得到数据类型

 onCreate:创建数据时调用的回调函数

3、每个ContentProvider都有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。Android所提供的ContentProvider都存放在android.provider包当中。

4、自定义一个ContentProvider,实现步骤:

1)定义一个CONTENT_URI常量(里面的字符串必须是唯一)

Publicstatic final Uri CONTENT_URI =Uri.parse("content://com.WangWeiDa.MyContentprovider");

如果有子表,URI为:

Publicstatic final Uri CONTENT_URI =Uri.parse("content://com.WangWeiDa.MyContentProvider/users");

2)定义一个类,继承ContentProvider

Publicclass MyContentProvider extends ContentProvider

3)实现ContentProvider的所有方法(queryinsertupdatedeletegetTypeonCreate)

2Uri类简介

Uri代表了要操作的数据,Uri主要包含了两部分信息:需要操作的ContentProviderContentProvider中的什么数据进行操作。

Uri的组成部分:

Android平台而言,URI主要分三个部分:scheme, authority and path。其中authority又分为hostport。格式如下:

scheme://host:port/path

举个实际的例子:

content://com.example.project:200/folder/subfolder/etc

\---------/  \---------------------------/ \---/\--------------------------/

scheme                 host               port        path

\--------------------------------/

authority

scheme:ContentProviderscheme已经由Android所规定为content://

主机名(Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。建议为公司域名,保持唯一性

路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定:

android中提供ContectResolverContentProvider来操作别的应用程序的数据。

3ContentResolver

一个应用实现ContentProvider来提供内容给别的应用来操作,一个应用通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。ContentProvider相当与一个“网站”,他的作用是提供可操控的数据,而其他的应用程序是通过ContentResolver来获取ContentProvider提供的数据。

ContentResolver的获取

  通过Context类:

  public abstract ContentResolver getContentResolver(); 

ContentResolver常用操作

1.            //查询:   

2.            public final Cursorquery(Uri uri, String[] projection,   

3.                      String selection, String[] selectionArgs, String sortOrder);   

4.            //新增   

5.            public final Uriinsert(Uri url, ContentValues values)       

6.            //更新   

7.            public final int update(Uriuri, ContentValues values, String where,   

8.                        String[] selectionArgs)   

9.            //删除   

10.        public final int delete(Uriurl, String where, String[] selectionArgs)

4ContentProvider是什么时候创建的,是谁创建的?访问某个应用程序共享的数据,是否需要启动这个应用程序?

这个问题在Android SDK中没有明确说明,但是从数据共享的角度出发,ContentProvider应该是Android在系统启动时就创建了,否则就谈不上数据共享了。这就要求在AndroidManifest.XML中使用<provider>元素明确定义。

5)可能会有多个程序同时通过ContentResolver访问一个ContentProvider,会不会导致像数据库那样的“脏数据”?

这个问题一方面需要数据库访问的同步,尤其是数据写入的同步,在AndroidManifest.XML中定义ContentProvider的时候,需要考虑是<provider>元素multiprocess属性的值;另外一方面AndroidContentResolve中提供了notifyChange()接口,在数据改变时会通知其他ContentObserver,这个地方应该使用了观察者模式,在ContentResolver中应该有一些类似registerunregister的接口。

对比这几种方式,可以总结下:

1. 简单数据和配置信息,SharedPreference是首选。

2. 如果SharedPreferences不够用,那么就创建一个数据库。

3. 结构化数据,一定要创建数据库,虽然这稍显烦锁,但是好处无穷。

4. 文件就是用来存储文件(也即非配置信息或结构化数据),如文本文件,二进制文件,PC文件,多媒体文件,下载的文件等等。

5. 尽量不要创建文件。

6. 如果创建文件,如果是私密文件或是重要文件,就存储在内部存储,否则放到外部存储。

7. 不要收集用户数据,更不要发到网络上,虽然你们也有很多无奈。用户也无奈,也无辜,但更无助平台为开发者准备了这么多的方式固然是一件好事,但我们要认清每一种的优点和缺点,根据实际情况选择最合适的。还有一个原则就是最简单原则,也就是说能用简单的方式处理,就不要用复杂的方式。比如存储几个数据或简单对象,用 SharedPreference 也能做到,没必要去写 ContentProvider
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值