安卓数据存储总结及详解

前言(首先说说什么存储的几个概念)

打开手机设置,选择应用管理,选择任意一个App,然后你会看到两个按钮,一个是清除缓存,另一个是清除数据,那么当我们点击清除缓存的时候清除的是哪里的数据?当我们点击清除数据的时候又是清除的哪里的数据?

Android开发中我们常常听到这样几个概念,内存,内部存储,外部存储,很多人常常将这三个东西搞混,那么我们今天就先来详细说说这三个东西是怎么回事?

内存:

我们在英文中称作memory,内部存储,我们称为InternalStorage,外部存储我们称为ExternalStorage,这在英文中本不会产生歧义,但是当我们翻译为中文之后,前两个都简称为内存,于是,混了。

那么究竟什么是内部存储什么是外部存储呢?

首先我们打开DDMS,有一个File Explorer,如下:

这里有三个文件夹需要我们重视,一个是data,一个是mnt,一个是storage,我们下面就详细说说这三个文件夹。

1.内部存储

data文件夹就是我们常说的内部存储,当我们打开data文件夹之后(没有root的手机不能打开该文件夹),里边有两个文件夹值得我们关注,如下:

一个文件夹是app文件夹,还有一个文件夹就是data文件夹,app文件夹里存放着我们所有安装的app的apk文件,其实,当我们调试一个app的时候,可以看到控制台输出的内容,有一项是uploading .....就是上传我们的apk到这个文件夹,上传成功之后才开始安装。另一个重要的文件夹就是data文件夹了,这个文件夹里边都是一些包名,打开这些包名之后我们会看到这样的一些文件:

1.data/data/包名/shared_prefs

2.data/data/包名/databases

3.data/data/包名/files

4.data/data/包名/cache

如果打开过data文件,应该都知道这些文件夹是干什么用的,我们在使用sharedPreferenced的时候,将数据持久化存储于本地,其实就是存在这个文件中的xml文件里,我们App里边的数据库文件就存储于databases文件夹中,还有我们的普通数据存储在files中,缓存文件存储在cache文件夹中,存储在这里的文件我们都称之为内部存储。

2.外部存储

外部存储才是我们平时操作最多的,外部存储一般就是我们上面看到的storage文件夹,当然也有可能是mnt文件夹,这个不同厂家有可能不一样。

一般来说,在storage文件夹中有一个sdcard文件夹,这个文件夹中的文件又分为两类,一类是公有目录,还有一类是私有目录,其中的公有目录有九大类,比如DCIM、DOWNLOAD等这种系统为我们创建的文件夹,私有目录就是Android这个文件夹,这个文件夹打开之后里边有一个data文件夹,打开这个data文件夹,里边有许多包名组成的文件夹。

说到这里,我想大家应该已经可以分清楚什么是内部存储什么是外部存储了吧?好,分清楚之后我们就要看看怎么来操作内部存储和外部存储了。

3.操作存储空间

首先,经过上面的分析,大家已经明白了,什么是内部存储,什么是外部存储,以及这两种存储方式分别存储在什么位置,一般来说,我们不会自己去操作内部存储空间,没有root权限的话,我们也没法操作内部存储空间,事实上内部存储主要是由系统来维护的。不过在代码中我们是可以访问到这个文件夹的。由于内部存储空间有限,在开发中我们一般都是操作外部存储空间,Google官方建议我们App的数据应该存储在外部存储的私有目录中该App的包名下,这样当用户卸载掉App之后,相关的数据会一并删除,如果你直接在/storage/sdcard目录下创建了一个应用的文件夹,那么当你删除应用的时候,这个文件夹就不会被删除。

经过以上的介绍,我们可以总结出下面一个表格:

一目了然,什么是内部存储,什么是外部存储。

如果按照路径的特征,我们又可以将文件存储的路径分为两大类,一类是路径中含有包名的,一类是路径中不含有包名的,含有包名的路径,因为和某个App有关,所以对这些文件夹的访问都是调用Context里边的方法,而不含有包名的路径,和某一个App无关,我们可以通过Environment中的方法来访问。如下图:

Android提供了5种方式来让用户保存持久化应用程序数据。根据自己的需求来做选择,比如数据是否是应用程序私有的,是否能被其他程序访问,需要多少数据存储空间等,分别是:    

① 使用SharedPreferences存储数据 

② 文件存储数据

③  SQLite数据库存储数据

④ 使用ContentProvider存储数据

⑤ 网络存储数据

1. 使用Shared Preferences

这种存储方式用于存储原始类型数据,包括boolean、int、long、float、double、String等。具体的存储方式是键-值对,若我们不主动删除,这些数据会一直存在。

SharedPreferences:(是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下)

根据以上介绍,我们很容易得出Shared Preferences适合存储的数据有:

小游戏的历史最高分(整型数据);

用户偏好设置:是否只在wifi时才加载图片(boolean型)、是否开启夜间模式(boolean型);

所有能够用原始类型所表示的用户数据..

使用:

保存数据一般分为四个步骤:

1.使用Activity类的getSharedPreferences方法获得SharedPreferences对象;

2.使用SharedPreferences接口的edit获得SharedPreferences.Editor对象;

3.通过SharedPreferences.Editor接口的putXXX方法保存key-value对;

4.通过过SharedPreferences.Editor接口的commit方法保存key-value对。

方法:

(1)

①getSharedPreferences (String name, int mode)

当我们有多个SharedPreferences的时候,根据第一个参数name获得相应的SharedPreferences对象。

②getPreferences (int mode)如果你的Activity中只需要一个SharedPreferences的时候使用

这里的mode有四个选项:

Context.MODE_PRIVATE

该SharedPreferences数据只能被本应用程序读、写。

Context.MODE_WORLD_READABLE

该SharedPreferences数据能被其他应用程序读,但不能写。

Context.MODE_WORLD_WRITEABLE

该SharedPreferences数据能被其他应用程序读和写。

Context.MODE_MULTI_PROCESS

sdk2.3后添加的选项,当多个进程同时读写同一个SharedPreferences时它会检查文件是否修改。

(2)

api中还注册和注销SharedPreferences被编辑时的监听

SharedPreferences.OnSharedPreferenceChangeListener changeListener = new OnSharedPreferenceChangeListener() {

@Override

public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {

//preferences被 编辑的SharedPreferences实例

//该SharedPreferences中被编辑的条目所对应的key

}

};

//userInfo注册监听事件

userInfo.registerOnSharedPreferenceChangeListener(changeListener);

//userInfo注销监听事件

userInfo.unregisterOnSharedPreferenceChangeListener(changeListener);

(3)

Edit还有两个常用的方法:

editor.remove(String key) :下一次commit的时候会移除key对应的键值对

editor.clear():移除所有键值对

性能:

· ShredPreferences是单例对象,第一次打开后,之后获取都需无创建,速度很快。

· 当第一次获取数据后,数据会被加载到一个缓存的Map中,之后的读取都会非常快。

· 当由于是XML<->Map的存储方式,所以,数据越大,操作越慢,get、commit、apply、remove、clear都会受影响,所以尽量把数据按功能拆分成若干份。

2.文件存储方式

(1)内部存储

当文件被保存在内部存储中时,默认情况下,文件是应用程序私有的,其他应用不能访问。当用户卸载应用程序时这些文件也跟着被删除。

文件默认存储位置:/data/data/包名/files/文件名。

写入文件使用方法

① 调用Context的openFileOutput()函数,填入文件名和操作模式,它会返回一个FileOutputStream对象。

② 通过FileOutputStream对象的write()函数写入数据。

③  FileOutputStream对象的close ()函数关闭流。

例如:

String FILENAME = "a.txt";

String string = "fanrunqi";

try {

FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);

fos.write(string.getBytes());

fos.close();

} catch (Exception e) {

e.printStackTrace();

}

注解:在 openFileOutput(String name, int mode)方法中

name参数: 用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。

mode参数:用于指定操作模式,分为四种:

Context.MODE_PRIVATE = 0

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

Context.MODE_APPEND = 32768

该模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。 

Context.MODE_WORLD_READABLE = 1

表示当前文件可以被其他应用读取。

MODE_WORLD_WRITEABLE

表示当前文件可以被其他应用写入。

读取一个内部存储的私有文件

① 调用openFileInput( ),参数中填入文件名,会返回一个FileInputStream对象。

② 使用流对象的 read()方法读取字节

③ 调用流的close()方法关闭流

例如:

String FILENAME = "a.txt";

try {

FileInputStream inStream = openFileInput(FILENAME);

int len = 0;

byte[] buf = new byte[1024];

StringBuilder sb = new StringBuilder();

while ((len = inStream.read(buf)) != -1) {

sb.append(new String(buf, 0, len));

}

inStream.close();

} catch (Exception e) {

e.printStackTrace();

}

其他一些经常用到的方法:

getFilesDir(): 得到内存储文件的绝对路径

getDir(): 在内存储空间中创建打开一个已经存在的目录

deleteFile(): 删除保存在内部存储的文件。  

fileList(): 返回当前由应用程序保存的文件的数(内存储目录下的全部文件)

保存编译时的静态文件

如果你想在应用编译时保存静态文件,应该把文件保存在项目的 res/raw/ 目录下,你可以通过 openRawResource()方法去打开它(传入参数R.raw.filename),这个方法返回一个 InputStream流对象你可以读取文件但是不能修改原始文件。

InputStream is = this.getResources().openRawResource(R.raw.filename);

   保存内存缓存文件

 有时候我们只想缓存一些数据而不是持久化保存,可以使用getCacheDir()去打开一个文件,文件的存储目录( /data/data/包名/cache )是一个应用专门来保存临时缓存文件的内存目录。

当设备的内部存储空间比较低的时候,Android可能会删除这些缓存文件来恢复空间,但是你不应该依赖系统来回收,要自己维护这些缓存文件把它们的大小限制在一个合理的范围内,比如1MB.当你卸载应用的时候这些缓存文件也会被移除。

(2)外部存储(sdcard)

因为内部存储容量限制,有时候需要存储数据比较大的时候需要用到外部存储,使用外部存储分为以下几个步骤:

添加外部存储访问限权

 首先,要在AndroidManifest.xml中加入访问SDCard的权限,如下:

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

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

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

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

检测外部存储的可用性

  在使用外部存储时我们需要检测其状态,它可能被连接到计算机、丢失或者只读等。下面代码将说明如何检查状态:

//获取外存储的状态String state = Environment.getExternalStorageState();if (Environment.MEDIA_MOUNTED.equals(state)) {

// 可读可写

mExternalStorageAvailable = mExternalStorageWriteable = true;

} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {

// 可读

} else {

// 可能有很多其他的状态,但是我们只需要知道,不能读也不能写

}

访问外部存储器中的文件

1、如果 API 版本大于或等于8,使用

getExternalFilesDir (String type)

  该方法打开一个外存储目录,此方法需要一个类型,指定你想要的子目录,如类型参数DIRECTORY_MUSIC和 DIRECTORY_RINGTONES(传null就是你应用程序的文件目录的根目录)。通过指定目录的类型,确保Android的媒体扫描仪将扫描分类系统中的文件(例如,铃声被确定为铃声)。如果用户卸载应用程序,这个目录及其所有内容将被删除。

例如:

File file = new File(getExternalFilesDir(null), "fanrunqi.jpg");

2、如果API 版本小于 8 (7或者更低)

getExternalStorageDirectory ()

通过该方法打开外存储的根目录,你应该在以下目录下写入你的应用数据,这样当卸载应用程序时该目录及其所有内容也将被删除。

/Android/data/<package_name>/files/

读写数据:

if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){

File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录  "/sdcard"

File saveFile = new File(sdCardDir,"a.txt");

//写数据

try {

FileOutputStream fos= new FileOutputStream(saveFile);

fos.write("fanrunqi".getBytes());

fos.close();

} catch (Exception e) {

e.printStackTrace();

}

//读数据

try {

FileInputStream fis= new FileInputStream(saveFile);

int len =0;

byte[] buf = new byte[1024];

StringBuffer sb = new StringBuffer();

while((len=fis.read(buf))!=-1){

sb.append(new String(buf, 0, len));

}

fis.close();

} catch (Exception e) {

e.printStackTrace();

}

}

  我们也可以在 /Android/data/package_name/cache/目录下做外部缓存。

3、SQLite数据库存储数据(单独拿出来分析)

4、使用ContentProvider存储数据(单独拿出来分析)

5、网络存储数据(单独拿出来分析) 

时间: 2024-10-03 21:41:32

安卓数据存储总结及详解的相关文章

Android开发数据存储之ContentProvider详解

转载:十二.ContentProvider和Uri详解 一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查.关于数据共享,以前我们学习过文件操作模式,知道通过指定文件的操作模式为Context.MODE_WORLD_READABLE或Context.MODE_W

IOS 数据存储之 FMDB 详解

FMDB是用于进行数据存储的第三方的框架,它与SQLite与Core Data相比较,存在很多优势. FMDB是面向对象的,它以OC的方式封装了SQLite的C语言API,使用起来更加的方便,不需要过多的关心数据库操作的知识.但是它本身也存在一些问题,比如跨平台,因为它是用oc的语言封装的,所以只能在ios开发的时候使用,如果想实现跨平台的操作,来降低开发的成本和维护的成本,就需要使用比较原始的SQLite. Core Data是ORM的一种体现,使用Core Data需要用到模型数据的转化,虽

IOS 数据存储之 SQLite详解

在IOS开发中经常会需要存储数据,对于比较少量的数据可以采取文件的形式存储,比如使用plist文件.归档等,但是对于大量的数据,就需要使用数据库,在IOS开发中数据库存储可以直接通过SQL访问数据库,也可以通过ORM进行对象关系的映射,当然也可以选择使用第三方框架实现对数据库的操作.在这里,主要来讲解一下第一种方式,SQLite. SQLite 数据库有很多,分为重量级和轻量级两类,移动设备的内存比较小,因此需要选择轻量级的数据库.在移动设备上应用程序开发中使用数据库,比较主流的是SQLite.

解析activity之间数据传递方法的详解

转自:http://www.jb51.net/article/37227.htm 本篇文章是对activity之间数据传递的方法进行了详细的分析介绍,需要的朋友参考下 1  基于消息的通信机制 Intent--------boudle,extra用这种简单的形式,一般而言传递一些简单的类型是比较容易的,如int.string等详细介绍下Intent机制Intent包含两部分:1 目的[action]-------要去到哪里去2 内容[category.data]----------路上带些什么,

Android Preference存储、res/raw、asset、openFileOutput、sdcard存储、Cache详解

*res/raw.assets.其它存储的相同点: 两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制. 两者都是只读,是不能存储数据的目录. 相同都可以放文件. *res/raw.assets.其它存储的不同点: res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename:assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类. res/raw不可以有目录结构,而assets则可以有目

8天掌握EF的Code First开发系列之3 管理数据库创建,填充种子数据以及LINQ操作详解

本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LINQ to Entities 使用LINQ to Entities操作实体 LINQ操作 懒加载和预加载 插入数据 更新数据 删除数据 本章小结 本人的实验环境是VS 2013 Update 5,windows 10,MSSQL Server 2008. 上一篇<Code First开发系列之领域建模和管理实体关系>,我们主要介绍了EF中“约定大于配置”的概念,如何创建数据

ORACLE EXPDP IMPDP数据导入导出命令详解及同EXP IMP命令详细对比

ORACLE EXPDP IMPDP数据导入导出命令详解及同EXP IMP 命令详细对比 一.EXPDP IMPDP EXP IMP 可以实现 1.可以实现逻辑备份和逻辑恢复 2.可以在数据库用户之间移动对象 3.可以在数据库之间移动对象 4.可以实现表空间转移 二.EXPDP的命令详解 C:\Users\Administrator>20:42:32.90>expdp help=y Export: Release 11.2.0.1.0 - Production on 星期六 10月 10 09

线性表链式存储的实现详解

本文原创,转载请注明:http://blog.csdn.net/j903829182/article/details/38173681 #include<stdio.h> #include<malloc.h> //线性表的链式存储和实现,带头点 #define true 1 #define false 0 typedef int DataType;//定义抽象数据类型 //节点的结构体 typedef struct Node{ DataType data;//节点的数据域 stru

(转)MVC 3 数据验证 Model Validation 详解

继续我们前面所说的知识点进行下一个知识点的分析,这一次我们来说明一下数据验证.其实这是个很容易理解并掌握的地方,但是这会浪费大家狠多的时间,所以我来总结整理一下,节约一下大家宝贵的时间. 在MVC 3中 数据验证,已经应用的非常普遍,我们在web form时代需要在View端通过js来验证每个需要验证的控件值,并且这种验证的可用性很低.但是来到了MVC 新时代,我们可以通过MVC提供的数据验证Attribute来进行我们的数据验证.并且MVC 提供了客户端和服务器端 双层的验证,只有我们禁用了客