Android 中的数据存储
转自:http://easyread.ly/blogs/data-storage-on-android
在 Android 中有多种数据持久化的方式,你可以根据自己的需求来选择:比如说你的数据应该是私有的,还是可以和其它应用程序和用户访问的?需要多少存储空间?等等。
Android 提供如下几种数据存储选项:
-
Shared Preferences
以键值对的形式存储基本类型的数据
-
Internal Storage
在设备的内存当中存储应用程序的私有数据
-
External Storeage
在共享的外部存储器上保存共享的数据
-
SQLite Database
在应用程序私有的数据库中存储结构化的数据
-
Network Connection
在你自己的网络服务器上存储数据
Android 使用一种叫做content provider的组件向外暴露数据——包括私有数据。
使用 SharedPreferences
SharedPreferences
类提供了一个允许你存取基本类型(布尔类型、浮点类型、整型、长整型和字符串)的通用框架。这些数据的持久化是跨 session 的。
可以使用以下两个方法获取 SharedPreferences 对象:
getSharedPreferences()
- 如果你需要多个 preferences 文件,就使用这个方法,它的第一个参数用于指定文件名。
getPreferences()
- 如果你只需要 1 个 preferences 文件,就使用 getPreferences 方法,它返回属于当前 Activity 的唯一的 preferences。
要想把数据写入:
-
调用 SharedPreferences 的 edit() 方法得到一个 SharedPreferences.Editor 对象;
-
使用 putBoolean, putString 等方法往里面写数据;
-
用 commit() 方法提交(保存)数据。
如果要获取数据,则可以使用 SharedPreferences 中对应的 getBoolean(), getString() 等方法。
下面是一段示例代码:
public class Calc extends Activity { public static final String PREFS_NAME = "MyPrefsFile"; @Override protected void onCreate(Bundle state){ super.onCreate(state); . . . // Restore preferences SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); boolean silent = settings.getBoolean("silentMode", false); setSilent(silent); } @Override protected void onStop(){ super.onStop(); // We need an Editor object to make preference changes. // All objects are from android.context.Context SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("silentMode", mSilentMode); // Commit the edits! editor.commit(); }}
使用内部存储空间(Internal Storage)
你可以直接在设备的内部存储空间中保存文件。默认情况下,保存在内部存储空间中的文件是应用程序私有的,其它应用程序以及用户是无法访问的。当用户把应用卸载的时候,这些文件也将随之删除。
要在内部存储空间中创建并写入一个文件:
-
调用
openFileOutput()
方法并把文件名和操作模式(operating mode)作为参数传递给它。该方法返回一个FileOutputStream
对象; -
调用 FileOutputStream 的 write() 方法写入数据;
-
调用 FileOutputStream 的 close() 方法关闭输出流。
例:
String FILENAME = "hello_file";String string = "hello world!";FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);fos.write(string.getBytes());fos.close();
openFileOutput 方法的第 2 个参数是文件的创建模式,有 4 个可选项:
-
MODE_PRIVATE - 创建的文件只能被当前应用程序访问;
-
MODE_APPEND - 追加模式,如果文件已经存在则直接打开并往文件尾添加内容,而不是清除原文件;
-
MODE_WORLD_READABLE - 允许其它应用程序读取该文件。从 API 1.7 开始已经被不建议使用(deprecated);
-
MODE_WORLD_WRITABLE - 允许其它应用程序对该文件进行写操作。同样,从 API 1.7 开始已经不建议使用。
从内部存储空间中读取文件:
-
调用
openFileInput()
方法,把文件名作参数传递给它,返回一个FileInputStream
对象; -
用 read() 方法读取文件内容;
-
用 close() 方法关闭文件流。
小提示:如果你想在编译时在应用程序中使用某个静态文件,可以把它保存到 res/raw 目录下,用openRawResource()
方法打开它,参数是 R.raw.resource ID。这个方法返回一个 InputStream 对象以供读取内容(但不支持写入)。
缓存文件
getCacheDir()
返回当前应用程序临时缓存文件的绝对路径,如果你想缓存而不是持久的保存一些文件,可以选择把它们保存在缓存目录下。
当设备上的内部存储可用空间很低的时候,Android 会删除这些缓存的文件。但是不该依赖系统来为你清除这些文件,你应该手动维护这些缓存文件,让缓存大小保持在一个受限的值(例如 1MB)之内。
当用户卸载这个应用的时候,缓存文件会随之被删除。
一些其它有用的方法:
-
getFilesDir() - 获取当前应用程序内部存储空间所在目录。
-
getDir() - 在当前应用程序的内部存储空间中创建(或打开已存在的)目录。
-
deleteFile() - 删除内部存储空间中的文件。
-
fileList() - 返回当前程序创建的所有内部存储空间中的文件。
使用外部存储(External Storage)
所有 Android 兼容 (Android-compatible) 的设备都有一个共享的外部存储空间 (external storage),用于存储文件。它可以是一个可移除的存储媒体(例如 SD 卡),也可以是一个内部的(不可移除的)存储器。保存在外部存储空间里的文件对所有应用程序和用户都是可读的。当用户启用 USB 大容量存储来与电脑连接传输文件时,用户可以修改里面的内容。
有可能存在这种情况:某个设备使用了内部存储器的某个分区作为外部存储空间,同时提供了一个 SD 卡槽。这时候,SD 卡不再属于外部存储空间的一部分,这时候外部存储器只能做为用户提供的媒体空间。(也就是说getExternalFilesDir()
访问的 /Android/data 目录不会出现在 SD 卡上,getExternalStoragePublicDirectory()
和getExternalStorageDirectory()
返回的目录也不会是 SD 卡中的目录。)
注意:用户把外部存储空间挂载到计算机上或是从设备中移除的时候,外部存储空间有可能不可用。这时保存在外部存储空间里的文件没有受任何安全机制保护,所有的应用程序都可以读写存放在外部存储空间里的文件,用户也有可能删除这些文件。
检测存储介质可用性
在外部存储空间里进行任何操作的时候,应该先调用getExternalStorageState()
方法来检测存储媒体是否可用,它可能处于被挂载到计算机、被移除、只读或者其它什么状态。下面是一段检测状态的示例代码:
boolean mExternalStorageAvailable = false;boolean mExternalStorageWriteable = false;String state = Environment.getExternalStorageState();if (Environment.MEDIA_MOUNTED.equals(state)) { // 可读写 mExternalStorageAvailable = mExternalStorageWriteable = true;} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { // 只读 mExternalStorageAvailable = true; mExternalStorageWriteable = false;} else { // 其它不正常的状态,但在这里我们需要知道的只是它是否可读或者可写。 mExternalStorageAvailable = mExternalStorageWriteable = false;}
上面这个示例检测外部存储空间是否可读写,getExternalStorageState() 方法会返回一些其它状态的值,例如存储介质是否被共享(连接到计算机上)、是否被移除、是否被异常的移除等等。你可以依此给用户更具体的提示信息。
访问外部存储空间中的文件
如果你在使用 API Level 8 或更高版本,可以使用getExternalFilesDir()
方法来打开保存在外部存储空间上的文件所在的目录。这个方法接受一个字符串做为参数来指定要访问的子目录的类型,例如 DIRECTORY_MUSIC, DIRECTORY_RINGTONES 等。如果参数为 null, 返回的则是外部存储空间中该应用的根目录。如果指定目录不存在,该方法则会自动创建它。可以通过指定目录的类型,确保 Android 的媒体扫描器对目录里的文件进行正确分类。比如说,ringtones 里的音乐文件会被识别为铃声而不是音乐。
一旦应用被卸载,对应的外部存储空间上的目录也将随之被删除。
如果你正在使用 API Level 7 或者 API 版本更低的设备,就只有getExternalStorageDirectory()
方法可用了。要打开程序对应的外部存储空间根目录,你应当把你的数据写到这个路径当中:/Android/data/<package_name>/files/
<package_name>是应用程序 Java 风格的包名,例如"com.example.android.app". 如果用户的设备上运行着 API Level 8 或更高版本的 API,当他卸载应用的时候,这个目录和它里面的内容也将自动的随之删除。
小技巧:对媒体扫描器隐藏你的文件
有时候我们希望 Android 的媒体扫描器不对应用程序的目录——比如说你写的应用里的用户头像所在的目录——进行扫描,因为这样会让用户的相册里充斥着一些乱七八糟的头像图片。这时候你可以在该目录下添加一个空白的文件,命名为.nomedia(注意文件名是以一个小点为前缀)。这样,Android 的媒体扫描器就不会扫描该目录,从而保持用户的相册、音乐播放器列表的干净。
保存共享的文件
如果你想把文件保存在应用程序对应的外部存储空间以外的地方,想让这些文件在应用卸载之后不会随着被删除,你可以选择把它们保存在外部存储空间中的某一个公共目录中。这些目录存在于外部存储空间的根目录上,例如:Music/, Pictures/, Ringtones/ 等等。
在 API Level 8 或更高版本的 API 中,可以使用getExternalStoragePublicDirectory()
方法访问共享目录,该方法参数不能为 null,可以是 DIRECTORY_MUSIC, DIRECTORY_PICTURES, DIRECTORY_RINGTONES 或其它字符串。注意,该方法不会自动创建不存在的目录。
如果你在使用 API Level 7 或更低版本的 API,可以使用getExternalStorageDirectory()
方法打开外部存储空间的根目录,然后把共享文件保存在以下目录其中之一:
-
Music/ - 媒体扫描器把该目录下的所有媒体归类为音乐。
-
Podcasts/ - 媒体扫描器把该目录下的所有媒体归类为明信片。
-
Ringtones/ - 媒体扫描器把该目录下的所有媒体归类为手机来电铃声。
-
Alarms/ - 媒体扫描器把该目录下的所有媒体归类为闹铃。
-
Notifications/ - 媒体扫描器把该目录下的所有媒体归类为消息通知铃声。
-
Pictures/ - 所有图片(相机拍的不包括在这里面)
-
Movies/ - 所有视频(相机拍的不包括在这里面)
-
Download/ - 所有下载到的文件
保存缓存文件
API Level 8 或高于 8 的设备可以使用getExternalCacheDir()
方法来打开外部存储空间中程当前应用对应的存缓存文件所在的目录。该目录一样也将随着应用的卸载而被删除,但是在应用程序未被卸载之前,你应当自己管理这些缓存文件,定期删除没用的文件以节省空间。
API Level 7 或低于 7 的设备可以使用getExternalStorageDirectory()
方法打开外部存储空间中当前应用的根目录,然后把缓存数据写入 /Android/data/<package_name>/cache/
使用数据库
Android 对 SQLite 提供了完整的支持。应用程序中创建的任何数据库都可以在当前应用的任何一个类中通过数据库名来访问,但是不能被其它应用程序访问。
比较推荐的创建一个数据库的方法是创建一个SQLiteOpenHelper
的子类,并实现它的 onCreate() 方法,在这个方法中执行 SQLite 命令来创建数据库表。例如:
public class DictionaryOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 2; private static final String DICTIONARY_TABLE_NAME = "dictionary"; private static final String DICTIONARY_TABLE_CREATE = "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" + KEY_WORD + " TEXT, " + KEY_DEFINITION + " TEXT);"; DictionaryOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DICTIONARY_TABLE_CREATE); }}
你可以通过你自己实现的 SQLiteOpenHelper 的子类的构造方法来获得一个实例。调用它的getWritableDatabase()
和getReadableDatabase()
方法可以获得一个SQLiteDatabase
对象以便对数据库进行读、写等操作。
你可以通过 SQLiteDatabase 的query()
方法来执行 SQLite 查询。它接受各种查询参数,例如要查询的表名 (the table to query)、投影 (projection)、选择 (selection)、字段 (columns)、分组 (grouping) 等等。对复杂的查询,例如需要字段别名 (column aliases) 的时候,你应当使用SQLiteQueryBuilder
,它提供了许多方便的方法来创建查询语句。
每个 SQLite 查询会返回一个指向所有查询结果的Cursor
对象,用于在查询结果集中移动、读取行和列。
这里有两个 demo 演示了如何在 Android 中使用 SQLite 数据库:Note Pad,Searchable Dictionary。
小提示:
Android 没有对标准的 SQLite 概念做任何限制。但是建议在数据库表当中加一个自增主键以便快速的查找数据库记录——这不是必须的,但是如果你要实现一个 content provider,就必须包含一个名为BaseColumns._ID
常量值的字段作为 ID。
数据库调试
Android SDK 包含了一个 sqlite3 数据库工具,它可以用来浏览数据库表的内容。
使用网络连接
你也可以使用网络(当网络可用时)来在你基于 web 的服务器上存储数据。要进行网络操作,可以使用 java.net.* 和 android.net.* 这两个包。
更多相关文章
- Android中bindService基本使用方法概述
- Unity 编辑器环境下不能正确加载Android Assetbundle 中的 Shade
- ionic3文件目录介绍
- android从未安装的apk文件里获取信息(包信息,资源信息)
- 提高开发效率-使用Android Studio Template快速生成模板文件
- Android API开发之OpenGL开发之Android OpenGL显示STL模型文件