浅析Android数据存储

还对Android数据存储感到困惑的人们,看了这篇博客你会获益匪浅。

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

打开手机设置,选择应用管理,选择任意一个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数据库存储数据//地址:http://blog.csdn.NET/qq_27280457/article/details/51790055

4、使用ContentProvider存储数据//地址:http://blog.csdn.Net/qq_27280457/article/details/51819299

5、网络存储数据 


发布了13 篇原创文章 · 获赞 11 · 访问量 9245

猜你喜欢

转载自blog.csdn.net/wangzizhong201205/article/details/54600217