本文主要讲述各种存储方式,基本都是常用的。在此进行记录。
一、存储
1.概述
Android提供了多种方式来持久化数据,以及提供对数据的安全保护。主要包括:
1)SharePreference:用于保存键值对;
2)内部存储:在内部存储器中保存私有数据;
3)外部存储:在共享外部存储中存储公共数据;
4)SQLite数据库:在私有数据库中存储结构化数据;
5)网络连接:在网络服务器中存储。
同时,Android提供了ContentProvider来对外(其他应用)公开数据,其可以根据需求设置读写访问权限。
2.SharePreference
SharedPreferences类提供一个通用框架,以键值对的形式保存数据。可以保存任何原始数据:布尔型、浮点型、整形、长整型以及字符串。
获取SharedPreferences,有两种方法:
getSharedPreferences(),用于根据名称获取SharedPreferences文件;
getPreferences(),用于Activity的首选项文件,由于这将是用于Activity的唯一首选项文件,因此无需提供名称。
写入步骤:
1)调用edit()获取SharedPreferences.Editor;
2)使用putString()等方法添加值;
3)使用commit()提交新值。
读取则使用getString()等方法。
public class Calc extends Activity {
public static final String PREFS_NAME = "MyPrefsFile";
@Override
protected void onCreate(Bundle state){
super.onCreate(state);
. . .
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean silent = settings.getBoolean("silentMode", false);
setSilent(silent);
}
@Override
protected void onStop(){
super.onStop();
// 通过Editor对象编辑
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("silentMode", mSilentMode);
// 提交编辑
editor.commit();
}
}
3.使用内部存储
1)特点
a)永远都可获取;
b)文件只有本应用可以获取;
c)应用卸载之后会清空数据;
d)不需要申明访问权限;
应用一般默认安装在内部存储中,但可以在manifest中指定android:installLocation属性指定安装位置(app过大则可以保存到外部)。
2)写入到内部存储
String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;
try {
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(string.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
e)缓存文件:
public File getTempFile(Context context, String url) {
File file;
try {
String fileName = Uri.parse(url).getLastPathSegment();//根据文件路径提取文件名
file = File.createTempFile(fileName, null, context.getCacheDir());
} catch (IOException e) {
// Error while creating file
}
return file;
}
注意,别的应用如果想访问内部存储的文件,要有权限,且还要知道应用包名和文件名。其中,MODE_PRIVATE会创建文件(或替换),并设为应用私有。还有MODE_APPEND、MODE_WORLD_READABLE、MODE_WORLD_WRITEABLE。
API17及之后,MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE已被弃用。Android7.0之后,使用它们会引发SecurityException异常。且7.0之后,无法根据文件名共享私有文件,如果使用file:// URI会引发FileUriExposedException。可以用FileProvider与FLAG_GRANT_URI_READ_URI_PERMISSION配合使用。了解FileProvider使用,请参考我另一篇文章FileProvider使用详解。
3)从内部存储读取文件:
a)openFileInput()打开FileInputStream;
b)read()读取文件字节;
c)close()关闭输入流。
注意:如果要读取应用中的静态文件,要在项目res/raw/目录中保存文件。可以使用openRawResource()打开并传入R.raw.filename。返回一个InputStream,用于读取文件。但是不能写入到原始文件。
4.外部存储
1)特点
注意:Android4.4开始,如果仅读写应用的私有文件,则不需要这些权限。
2)检查外存储器可用性
/* 检查外存储器是否可用 */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* 检测外存储器是否可读 */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
3)保存可共享文件
使用getExternalStoragePublicDirectory(),并传递需要的目录参数,会打开指定目录,用于保存照片等不需要卸载时删除的文件。
public File getAlbumStorageDir(String albumName) {
// 获取pictures目录
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
4)保存私有文件
通过getExternalFilesDir(),并传入DIRECTORY_PICTURES等类型。也可以是null,访问私有根目录。代码就不写了,与上面类似。
4.4之后读写私有目录不需要在manifest中申明权限,因此,可以使用maxSdkVersion指定较低版本需要此权限:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
注:在卸载时,此目录内容将被删除。因此,如果不想删除文件,需要保存在公共目录中。
注意:上面两个获取文件夹的方法,所需要的参数可以是null(对应/storage/emulated/0/null(或.../Android/data/com.package.app/files))的,但是一般不建议这么用,需要指定其类型,如DIRECTORY_PICTRUES(对应/storage/emulated/0/Pictures(或.../Android/data/com.package.app/files/Pictures))等。而且,该参数还有些其他用途,比如,把铃声文件放在DIRECTORY_RINGTONS,则会默认为铃声而不是音乐文件。
5)保存缓存文件
通过getExternalCahceDir(),访问外部存储的缓存文件。用户卸载应用,文件将被删除。代码与上面类似。
6)总结或注意点
a)需要根据可用空间大小来确定文件是否可以写到存储器中,防止IO异常,getFreeSpace()以及getTotalSpace()获取可用和最大空间。但是文档说不一定能写getFreeSpace()获得的大小,最好是小几MB或者已存储不到90%时可以保存;
b)文件删除
file.delete();内部存储有个专用删除方法:context.deleteFile(file);
c)卸载时,内部存储以及外部存储中getExternalFilesDir()目录下的文件会被删除;
d)及时删除getCacheDir()中不需要的文件;
5.使用数据库
官方推荐继承SqLiteOpenHelper,并重写onCreate()方法创建SQLite数据库。在onCreate()方法中创建数据库表。
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);
}
}
之后,可以创建其实例,读写操作时,要传入getWritableDatabase()和getReadableDatabase()。这些都不赘述了。
注意,由于是数据库操作,有可能会消耗很长时间,因此要在工作线程调用getWriteableDatabase()或getReadabledDatabase(),比如AsynTask或IntentService中。
一般,我们开发中,都是使用开源的ORM框架,主流的有ORMLite、greenDao等等。
数据库调试可以使用sqlite3工具,在Android SDK中有。
6.使用网络连接
这个也不多说了