以下内容翻译自Android开发文档 Storage Options 篇,如有错误或不当之处,欢迎指出。
---------------------------------------------------------------------------------------------------------------------
Android提供了多种选择来存储持久的应用数据。你可以根据自身需求选择相应的存储方式,比如数据是否私有的,或者可以被其他应用(或用户)访问,以及你的数据需要的空间。
- Shared Preferences:用键值对的方式存储私有的基本的数据
- Internal Storage:存储在设备内存的私有数据
- External Storage:存储在可共享的外存上的公共数据
- SQLite Databases:存储在私有数据库的结构化数据
- Network Connection:存储在自己的网络服务器的数据
Android还提供了一种方式将你的私有数据暴露给别的应用——content provider
。
使用Shared Preferences
SharedPreferences
类提供了通用框架来允许你保存和检索基础数据类型的永久键值对。你可以使用SharedPreferences
阿狸保存基本数据:booleans
,floats
,ints
和strings
。这些数据将会随着用户会话一直保存,即使进程被杀掉(除非用户手动清除数据,或者卸载应用)。
获取SharedPreferences
对象,可以使用如下两种方法:
getSharedPreferences()
:当你需要多个preferences
文件时,使用这个方法,并在第一个参数中命名你的文件getPreferences()
:当你只需要一个preferences
文件时,使用这个方法,会使之成为唯一的preferences
文件,你不需要提供名字。
写入数据:
- 调用
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();
}
}
使用the Internal Storage
你可以直接将数据保存在设备的内存中。Android默认保存在内存中的文件是你的应用私有的,其他应用不能访问(包括用户)。当用户卸载应用时,这些文件也会被删除。
创建和写入内存中的私有文件方法
- 调用
openFileOutput()
方法,传入文件名和操作模式。这个方法会返回一个FileOutputStream
- 调用
write()
方法写入文件 - 调用
close()
方法关闭流
举个例子:
String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
从内存中读取文件
- 调用
openFileInput()
方法,并传入要读取的文件名。这个方法将会返回一个FileInputStream
- 调用
read()
方法读取字节 - 调用
close()
方法关闭流Tips:当你想要在编译时在你的应用中保存静态文件,将文件保存在项目的
res/raw/
目录中。你可以使用openRawResource()
方法传入资源IDR.raw.<filename>来打开该文件。这个方法会返回一个
InputStream`用来读取文件(但你不能写入原始文件)。
保存缓存文件
当你想要缓存某些数据时,比起永久保存,你可以使用getCacheDir()
方法来打开应用用来存储临时缓存文件的目录下的一个文件。
当设备内存空间不足时,Android可能会删除这些缓存文件来恢复空间。但是你不能以来系统为你清理这些文件。你应该时不时的清理下你的缓存文件,使之保持在内存消费的合理限制中,比如1MB。当用户卸载应用时,这些文件将会被删除。
其他可用的方法
- getFilesDir():获取你的内部文件所在的文件系统目录的绝对路径
- getDir():创建一个(或打开一个已经存在的)位于你的内存空间的目录
- deleteFile():删除保存在内存中的文件
- fileList():返回当前由你的应用保存的文件列表
使用the External Storage
每台Android兼容的设备都支持一个可共享的external storage
方便你存储文件。它可以时可移除的存储媒介(比如一张SD卡)或者内存(不可移除的)。保存在外存中的文件是全世界可读的,并且当它们启用USB大容量存储在计算机上传输文件时,它们可以被用户修改。
注意:当用户在计算机上
mount
外存或者移除媒介时,外存会变成不可用状态,你保存到外存的文件上也并没有安全保证。所有应用可以读写外存中的文件,用户也可以移除它们。
获取访问外存权限
要在应用中读写外存中的文件,你需要READ_EXTERNAL_STORAGE
和WRITE_EXTERNAL_STORAGE
系统许可。如下:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
检查媒介可用性
在对外存进行操作前,你总是需要调用getExternalStorageState()
方法来检查媒介是否可用。媒介可能正挂载在一台计算机上,丢失、只读或者其他状态。如下所示,这里由集中你可以用来检查可用性的方法:
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
保存可以分享给其他应用的文件
通常,用户从你的应用获取的新的文件应该被保存在公共的位置,以便其他应用可以访问,用户可以轻易拷贝。此时,你应该使用可共享的公共目录,比如Music/
,Pictures/
,Rintones/
等。
调用getExternalStoragePublicDirectory()
,传入你想要的目录类型,比如DIRECTORY_MUSIC
,DIRECTORY_PICTURES
,DIRECTORY_RINGTONES
,或其他。通过将文件保存到相应的媒体类型目录下,系统的媒体扫瞄器可以将你的文件适当归类。
如下提供了一个在公共的图片目录下创建一个新的相册目录的方法:
public File getAlbumStorageDir(String albumName) {
// Get the directory for the user's public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
保存应用私有的文件
如果你要处理一些不打算被其他应用使用的文件(比如只能用在你的应用中的图片、音效等),你应该调用getExternalFilesDir()
方法来创建(或打开)外存中的私有存储目录。该方法还需要一个type
参数来标识你的子目录类型(比如DIRECTORY_MOVIES
)。如果你不需要标识媒体目录,传入null
即可。
从Android 4.4.开始,读写应用的私有目录不许再添加READ_EXTERNAL_STORAGE
和WRITE_EXTERNAL_STORAGE
许可。因此,你只需在低版本中声明该许可:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
注意:当用户卸载你的应用时,该目录和它的所有内容都会被删除。同时,系统的媒体扫描器不再读取该目录下的文件,隐藏它们不能被
MediaStore
的content provider
访问。
有时候,设备分类了一部分内存来使用,同时外存也提供了SD卡槽。当一个设备运行的是Android 4.3或者更低,getExternalFilesDir()
方法只能访问内存部分而不能读写SD卡。从Android 4.4开始,你可以通过调用getExternalFilesDirs()
方法访问这两个位置,它会各返回一个文件列表。列表中的第一个条目就是主要的外存,你可以使用这个位置除非它满了或者不可用。如果你想要访问两个位置且又要支持Android 4.3及以下版本的,那么可以使用support library
的静态方法:ContextCompat.getExternalFilesDirs()
。它也会返回一个文件列表,但在Android 4.3及以下版本中,只包含一个条目。
注意:尽管这些目录由
getExternalFilesDir()
提供,且不能被MediaStore
的content provider
访问,但是其他带有READ_EXTERNAL_STORAGE
许可的应用可以访问外存的所有文件,包括这些目录。如果你要完全限制对你的文件的访问,你应该将文件写入内存中。
保存缓存文件
要打开一个外存目录中你用来存储缓存文件的File
,调用getExternalCacheDir()
方法。如果用户卸载了你的应用,这些文件会被自动删除掉。
类似ContextCompat.getExternalFilesDirs()
,上面的方法,你也可以通过调用ContextCompat.getExternalCacheDirs()
方法来访问位于第二外存(如果可以用)中的缓存目录。
Tips:为了保留文件空间和保持应用性能,小心地管理缓存文件并且一旦不需要了就及时移除它们是很重要的。
使用Databases
Android提供了对SQLite
数据库的完整支持。你在应用中创建的所有数据库,你都可以使用数据库名在任意类中访问,但不能在应用外访问。
推荐的创建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
对象,它表示你要操作的那个数据库并提供了SQLite
操作的相关方法。
你可以使用SQLiteDatabase
的query()
方法来执行SQLite
查询,它需要一些查询参数,诸如表名、projection
、selection
、columns
、grouping
,及其他。对于复杂的查询,比如那些需要字段别名的,你应该使用SQLiteQueryBuilder
,它提供了多种方便的方法来创建查询语句。
每个SQLite
查询会返回一个Cursor
指向本次查询找到的行。Cursor
是一种你可以从数据库查询中导航结果和读取行列的机制。
数据库调试
Android SDK包含了sqlite3
数据库工具,允许你浏览表的内容,执行SQL
命令,以及其他在SQLite
数据库中很有用的方法。