标签:
Android平台下的数据存储以及应用安装位置
可以存储应用的数据在数据库中、文件中,或者偏好设置中、或者内部和外部的存储卡中,你也可以开启一个数据备份的服务来让用户存储和恢复应用和系统的数据。
安卓为我们提供了几种方法来持久化应用数据。我们可以根据特定的需求来选择相应的方法,比如待存储的数据是否只对自己的应用使用,还是其它应用和用户也能访问,再比如数据需要的空间大小等。
数据存储方法有如下几种:
1. Shared Preferences(安卓专有名词,偏好)
以键值对的形式存储私有化的数据
2. Internal Storage 内部存储
在设备内部存储私有数据
3. External Storage 外部存储
在一个共享的外部区域上存储公共的数据
4. Sqlite Databases 数据库
在私有的数据库中存储结构化的数据
5. Network Connection 网络连接
在自己的网络服务器上存储数据
安卓为你提供了一种方法,让你能把自己私有的数据暴露给其它应用——ContentProvider(内容提供者)。一个内容提供者是一个可选的组件能公开读写你的应用数据,你也可以加一些限制。
SharedPreferences类提供了一个架构,允许你保存和检索持久化键值对的基本数据类型。你可以用它来保存任何基本数据类型:布尔、浮点型、整型、长整型、字符串。这些数据将持久存在用户手机应用中(即使应用被杀死)。
获取SharedPreferences对像,可以用下面两个方法中的一个:
- getSharedPreferences() 当你需要以名称为区分的多种偏好文件时,可以着用这个方法,以第一个参数区分。
- getPreferences() 当你的Activity只需要一个偏好文件时,用这个方法。因为他对于你的Activity来说。是唯一的偏好,你不需要提供一个name。
写数据:
1. 调用 edit()方法获取一个对象:SharedPreferences.Editor.
2. 添加数值用putBoolean()、putString()这些方法
3. 提交值用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();
}
}
你可以直接在设备应用内部存储里面保存文件,默认情况下,内部保存的文件是私有的,其它应用和用户不能访问。当用户卸载应用,这些文件会被移除。
创建和写一个私有文件到内部存储空间:
1. 调用openFileOutput 传入文件的名称和操作模式,返回值是一个FileOutputStream
2. 向文件里面写东西用方法write()
3. 关闭流用方法close()
例如:
String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
MODE_PRIVATE 将创建的文件对应用是私有的。其它的模式还有MODE_APPEND,MODE_WORLD_READABLE_MODE_WORLD_WRITEABLE.
从内部存储读取文件:
1. 调用openFileInput(),传入要读的文件名称,返回FileInputStream
2. 用read()方法读取文件字节
3. 关闭流
建议:
如果你要在应用的编译时期保存一个静态文件,将文件保存在项目的res/raw/ 目录下。你可以用openRawResource()方法打开,传入R.raw. 资源ID。该方法返回一个读取流,你可以用它来读取文件,但不能向里面写。
如果你想缓存数据,而不是持久化存储它,您应该使用getCacheDir()来打开一个文件,该文件目录代表内部目录,应用程序能保存临时缓存文件。
当内部空间不足时,安卓会删掉这些缓存文件来恢复空间。然而,你不应该依赖系统来为你清理,你应该自己维护缓存文件,保持合理的空间消费上限,比如1MB,当用户卸载应用时,这些文件被移除。
getFileDir()
获取内部文件存储文件目录的绝对路径
getDir()
创建或打开内部存储目录
deleteFile()
删除存储子在内部的文件
fileList()
返回应用存储的文件的数组
每个android兼容的设备都支持外部存储来保存文件。这是一个可以被移除的媒体(SD卡)或者内部(不可移除的)空间。
外部存储的文件是全局可读的并且在用户用USB连接电脑时,文件也可以修改。
注意:
如果用户在电脑上挂载了外部存储,或者移除媒体,外部存储就不能打开,并且保存在外部的文件没有任何安全防护。所有应用都可以读写这里的文件,也能移除他们。
为了读或写文件到外部存储空间上,你的app必须请求读取外部存储的权限或者写入外部存储空间的权限。比如:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
当你即需要读又需要写文件时,你只需要声明 WRITE_EXTERNAL_STORAGE权限,因为它隐式的含有了读的权限。
Note:
Android 4,4开始,如果你自是读取自己的app时,这些权限就不需要了。
在你用外部存储之前,你需要调用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;
}
getExternalState()方法返回的其它状态你也许想要检查,比如媒体是否被电脑分享,是否完全没挂载等,你可以用这些信息通知用户当他们需要方法媒体的时候。
一般来说,用户可以通过应用程序获得的新文件应该保存到设备上的一个公共位置,其他应用程序可以访问,用户可以很容易地从他们的设备里复制。当这样做时,你应该使用一个共享的公共目录,如Music/,Pictures/,Ringtones/等。
获取适当的公共目录的文件,调用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;
}
如果你处理的文件不希望被其它应用使用(比如图片文字或者声音效果),你应该调用getExternalFileDir()方法来使用一个私有的存储目录。这个方法用一个类型参数来区分子目录。如果你不需要特别的媒体目录,传入null来接收自己app的根目录。
android4.4 开始,读写文件不需要权限了,所以你可以声明需要用的权限即可,低版本的android可以添加最大版本属性
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
Note: 当用户卸载应用时,这些目录和内容都会被删除,同时,系统媒体扫描仪不会读取这些文件在这些目录下,因此,他们不可被MediaStore的ContentProvider访问。像这样,你不能使用最终是属于用户的这些目录,比如用你app编辑的照片,或者用户购买的音乐,这些文件应该保存早公共目录下。
有时,一个设备分配给一个内部内存用作外部存储器,也提供一个SD卡插槽。当这样一个设备运行Android 4.3或更低时,在getExternalFilesDir()方法只提供内部存储,应用程序不能读写SD卡。从Android 4.4开始,可以访问这两个位置通过调用getExternalFilesDirs(),它返回一个文件条目每个位置的数组。数组中的第一个元素被认为是主要的外部存储器,你应该使用这个位置,除非它不可用。如果你想访问这两个可能的位置,同时也支持Android 4.3和低,使用支持库的静态方法,ContextCompat.getExternalFilesDirs()。这也返回一个文件数组,但总是只包含一个条目在Android 4.3或更低时。
注意:尽管getExternalFilrDir()和getExternalFileDirs()不能被内容提供者访问,但其它有读外部权限的app可以访问这些。如果你需要完全显示访问你的文件,你应该用内部存储来代替它。
打开外部一个文件代表你应当保存缓存文件,调用getExternalCacheDir(),如果用户卸载了应用,这些文件将自动删除。
和ContextCompat.getExternalFilesDirs()类似,你可以通过ContextCompat.getExternalCacheDirs()来访问一个缓存目录在二级外部存储空间上(如果有的话)
Tip: 为了保留文件空间和维护app性能,小心管理缓存文件,通过app的生命周期来移除不必要的垃圾就十分重要。
安卓支持Sqlite数据库。支持所有的你在应用中以名字创建的数据库。但不是应用外的。
创建数据库的推荐写法是写一个类继承SqLiteOpenHelper并且重写onCreate方法,你可以执行一个sql命令来建表,例如:
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的实例,从数据库中读写数据,可以调用getWriteableDatabase()和getReadableDatabase(),他们都返回一个SqLiteDatabase对象,代表这个数据库并且提供方法操作数据。
你可以执行SQLite查询用SQLiteDatabase query()方法,他接受多种查询参数,比如查询的表,项,查询条件,分钟查询等,对于复杂查询,比如需要列的别名,你需要使用SQLiteQueryBuilder,它提供了几种方便的方法构建查询条件。
每个SQL查询都返回一个游标,指向查询的所有的行。这个游标总是可以定位到数据库查询读取的行和列的结果。
安卓SDK包含一个sqlite3数据库的工具,允许你打开表的内容,运行SQL语句,和其它一些有用的功能在数据上。
你可以使用网络来存储和检索数据在自己的服务里,在网络操作时,用下面的包:
- java.net.*
- android.net.*
从安卓 API 8(android2.2)开始,你可以让你的应用安装在外部存储空间上(比如:设备的SD卡上),这是一个可选的特征,你可以声明应用的位置用这个属性:android:installLocation,如果没有声明这个属性,你的应用讲被安装在内部存储空间里,并且不能够被移动到外部存储区域。
为了允许系统在外部区域安装你的应用,修改清单文件,引入android:installLocation属性作为它的元素,值为preferExternal或者auto,例如:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="preferExternal"
... >
如果你声明preferExternal,你请求应用安装在外部空间,但系统不会保证会安装应用在外部。如果外部存储空间满了,系统会安装在内部存储空间内,用户可以在两个地方切换。
如果你声明auto,意味着你的应用也许会被安装在外部,但你没有一个安装位置的偏好,系统将基于几个因素安装你的应用,用户仍可以在两个地方切换。
当你的应用被安装在外部存储空间内:
- 只要外部存储区域一直挂载着,就没有影响
- .apk文件被保存在外部空间上,但是所有的私有的用户数据,数据库,.dex文件和本地的代码都被保存在内部设备内存中
- 应用程序唯一的容器存储是使用一个随机生成的密钥加密,仅可以由最初的安装设备解密。因此,应用程序安装在一个SD卡上,只有该设备适用。
- 用户能通过系统设置移动应用到内部存储空间。
警告:当用户用电脑连接手机分享文件或者没有挂载SD卡,外部的存储将从设备中分离,所有外部应用将立刻被杀死
为了你的应用一直运行良好,如果它用了以下的属性,你就不应该让你的安装在外部,因为引用会出现一些不好的后果当外部没有挂载时。
Services:
你运行的Service将被杀死并且不会被重启当外部存储挂载的时候。但是你可以注册一个外部挂载的广播意图,它会通知你外部存储可以获取了,这时,你可以重启你的服务。
Alarm Services
你注册的闹钟将被取消,你必须重新注册当外部存储重新挂载的时候
Input Method Engines
你的输入法将被默认的替代,当重新挂载时,可以打开系统设置,重新选择你的输入法
Live Wallpapers
你的图库将被默认图库替代。当重新挂载时,可选择自己的图库
App Widgets
你的app小工具将从屏幕上移除,当重新挂载时,你的app小工具不会出现直到系统重置主页应用(通常系统重启后出现)。
Account Managers
你创建的账户将消失直到重新挂载
Device Administrators
你的设备接收者不能使用,有着不可遇见的后果,重新挂载后也许扔没用
Broadcast Receivers listening for “boot completed”
系统重启的广播是在外部存储挂载之前发送的,如果应用装在外部,它将收不到这个广播
如果你的应用使用了上面的一些,你不能允许它安装在外部存储设备上,默认,系统不会允许你的应用安装在外部存储设备上,所以你不用担心已经存在的应用。但是,如果你确定你的应用绝不会安装在外部,你可以这样声明:android:installLocation=”internalOnly”.虽然不会改变默认的行为,但它强调了应用的状态并且是对你和其它开发这的一个提醒。
简单来说,一切没有用的上面提到的特征的都是可以安全的安装在外部存储设备上的。大的游戏就是一个典型,因为游戏不需要提供多余的服务当交互的时候,当外部存储设备不可获得,游戏就被杀死了,并没有什么显著的影响当重新挂载外部存储设备,用户重启游(假定游戏里在Activity的生命周期里面保存了状态)。
如果你的应用需要一些apk文件,应该当心,考虑是否应该允许应用安装在外部存储设备,用户这样可以为内部存储保留一些空间。
标签:
原文地址:http://blog.csdn.net/sinat_20645961/article/details/50998035