安卓中的数据存储
SharePreferences
什么是SharePreferences?
SharedPreferences
对象指向包含键值对的文件并提供读写这些文件的简单方法。
每个SharedPreferences
文件由框架进行管理并且可以专用或共享。
用在何处?
如果您有想要保存的相对较小键值集合,您应使用 SharedPreferences
API。
用法:
1、获取 SharedPreferences 实例句柄
getSharedPreferences()
如果您需要按照您用第一个参数指定的名称识别的多个共享首选项文件,请使用此方法。
您可以从您的应用中的任何Context
调用此方法。
Context context = getActivity(); SharedPreferences sharedPref = context.getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE)
命名您的共享首选项文件时,您应使用对于您的应用而言唯一可识别的名称,比如 "com.example.myapp.PREFERENCE_FILE_KEY"
getPreferences()
如果您只需使用Activity的一个共享首选项,请从 Activity
中使用此方法。
因为此方法会检索属于该Activity的默认共享首选项文件,您无需提供名称。
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
getDefaultSharedPreferences()
如果你需要使用当前应用程序的包名作为前缀来命名SharedPreferences 文件,请使用 PreferenceManager 中使用该方法。
这是一个静态方法,它接收一个 Context 参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences 文件。
2、写入SharedPreference
要写入共享首选项文件, 请通过对您的 SharedPreferences
调用 edit()
来创建一个 SharedPreferences.Editor
。
传递您想要使用诸如 putInt()
和 putString()
方法写入的键和值。然后调用 commit()
以保存更改。例如:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt(getString(R.string.saved_high_score), newHighScore); editor.commit();
3、从 SharedPReference 读取数据
要从共享首选项文件中检索值,请调用诸如 getInt()
和 getString()
等方法,为您想要的值提供键,并根据需要提供要在键不存在的情况下返回的默认值。 例如:
//获取SharedPreferences SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); //默认值 int defaultValue = getResources().getInteger(R.string.saved_high_score_default); //读取 long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);
保存文件(内部存储目录和SD卡)
File
对象适合按照从开始到结束的顺序不跳过地读取或写入大量数据。 例如,它适合于图像文件或通过网络交换的任何内容。
文件系统分类
内部存储系统
- 它始终可用。
- 默认情况下只有您的应用可以访问此处保存的文件。
- 当用户卸载您的应用时,系统会从内部存储中删除您的应用的所有文件。
当您希望确保用户或其他应用均无法访问您的文件时,内部存储是最佳选择。
您无需任何权限,即可在内部存储中保存文件。 您的应用始终具有在其内部存储目录中进行读写的权限。
外部存储系统
- 它并非始终可用,因为用户可采用 USB 存储的形式装载外部存储,并在某些情况下会从设备中将其删除。
- 它是全局可读的,因此此处保存的文件可能不受您控制地被读取。
- 当用户卸载您的应用时,只有在您通过 getExternalFilesDir() 将您的应用的文件保存在目录中时,系统才会从此处删除您的应用的文件。
对于无需访问限制以及您希望与其他应用共享或允许用户使用电脑访问的文件,外部存储是最佳位置。
注意:要向外部存储写入信息,您必须在您的 AndroidManifest 中请求 WRITE_EXTERNAL_STORAGE
权限。
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!--或者只读权限--> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ... </manifest>
提示:尽管应用默认安装在内部存储中,但您可在您的 AndroidManifest 中指定
android:installLocation
属性,这样您的应用便可安装在在外部存储中。 当 APK 非常大且它们的外部存储空间大于内部存储时,用户更青睐这个选择。 如需了解详细信息,请参阅应用安装位置。
将文件保存在内部存储中
在内部存储中保存文件时,您可以通过调用以下两种方法之一获取作为 File
的相应目录:
- getFilesDir() - 返回表示您的应用的内部目录的
File
。 - getCacheDir() - 返回表示您的应用临时缓存文件的内部目录的
File
。 务必删除所有不再需要的文件并对在指定时间您使用的内存量实现合理大小限制,比如,1MB。 如果在系统即将耗尽存储,它会在不进行警告的情况下删除您的缓存文件。
可以通过 File() 构造函数来新建一个文件
File file = new File(context.getFilesDir(), filename);
可以通过 openFileOutput() 获取 FileOutputStream 对象来进行写入文本
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(); }
如果您需要缓存某些文件,您应改用 createTempFile()
。例如,以下方法从 URL
提取文件名并在您的应用的内部缓存目录中以该名称创建文件:
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; }
将文件保存在外部存储中
外部存储并不是一定可用的,因此在使用前需要检查状态
通过调用 getExternalStorageState()
查询外部存储状态。 如果返回的状态为 MEDIA_MOUNTED
,那么您可以对您的文件进行读写。
/* 检查是否可读可写 */ 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; }
外部文件又分为两种:
公共文件
应供其他应用和用户自由使用的文件。 当用户卸载您的应用时,用户应仍可以使用这些文件。
例如,您的应用拍摄的照片或其他已下载的文件。
使用 getExternalStoragePublicDirectory()
方法获取表示外部存储上相应目录的 File
该方法使用指定 您想要保存以便它们可以与其他公共文件在逻辑上组织在一起的文件类型的参数,比如 DIRECTORY_MUSIC
或 DIRECTORY_PICTURES
。、
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; }
私有文件
存放在外部存储的 Android/data/包名/
本属于您的应用且应在用户卸载您的应用时删除的文件。
尽管这些文件在技术上可被用户和其他应用访问(因为它们在外部存储上),它们是实际上不向您的应用之外的用户提供值的文件。
当用户卸载您的应用时,系统会删除应用外部专用目录中的所有文件。
例如,您的应用下载的其他资源或临时介质文件。
通过调用 getExternalFilesDir()
获取相应的目录并向其传递指示您想要的目录类型的名称
public File getAlbumStorageDir(Context context, String albumName) { // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
查询可用空间
getFreeSpace() 提供目前的可用空间
getTotalSpace()
提供存储卷中的总空间
注意:保存您的文件之前,您无需检查可用空间量。 您可以尝试立刻写入文件,然后在
IOException
出现时将其捕获。 如果您不知道所需的确切空间量,您可能需要这样做。 例如,如果在保存文件之前通过将 PNG 图像转换成JPEG 更改了文件的编码,您事先将不知道文件的大小。
删除文件
通过打开文件的 delete() 方法自行删除
myFile.delete();
如果文件保存在内部存储中,您还可以请求 Context
通过调用 deleteFile()
来定位和删除文件:
myContext.deleteFile(fileName);
注意:当用户卸载您的应用时,Android 系统会删除以下各项:
- 您保存在内部存储中的所有文件
- 您使用
getExternalFilesDir()
保存在外部存储中的所有文件。但是,您应手动删除使用
getCacheDir()
定期创建的所有缓存文件并且定期删除不再需要的其他文件。
SQLite
创建自己的 SQLiteOpenHelper
构造函数
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version)
需重写的两个回调函数
public abstract void onCreate(SQLiteDatabase db); //主要用于数据库第一次创建时建表
public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) //更新数据库时调用
public class FeedReaderDbHelper extends SQLiteOpenHelper { // If you change the database schema, you must increment the database version. public static final int DATABASE_VERSION = 1; public static final String DATABASE_NAME = "FeedReader.db"; public FeedReaderDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(db, oldVersion, newVersion); } }
获取数据库实例
getReadableDatabase()
getWritableDatabase()
相同:都可以创建或打开一个现有的数据库(不存在则创建),并返回一个可对数据库进行读写操作的对象
不同:当数据库不可写时,getReadableDatabase返回的对象以只读的方式去打开数据库,getWritableDatabase抛出异常
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); SQLiteDatabase db = dbHelper.getWritableDatabase();
操作数据库实例
方法一:使用sql语句直接操作
更删改都用 db.execSQL(SQL语句)
查询用 db.rawQuery()
方法二: 利用SQLiteDatabase封装好的函数来操作数据库
增:
1、先获取SQLiteDatabase对象
2、利用ContentValues组装数据
3、调用insert()添加数据
insert()
第一个参数是表名
第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值 NULL,一般我们用不到这个功能,直接传入 null 即可
第三个参数是一个 ContentValues 对象它提供了一系列的 put()方法重载,用于向 ContentValues 中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
// Gets the data repository in write mode SQLiteDatabase db = mDbHelper.getWritableDatabase(); // Create a new map of values, where column names are the keys ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id); values.put(FeedEntry.COLUMN_NAME_TITLE, title); values.put(FeedEntry.COLUMN_NAME_CONTENT, content); // Insert the new row, returning the primary key value of the new row long newRowId; newRowId = db.insert( FeedEntry.TABLE_NAME, FeedEntry.COLUMN_NAME_NULLABLE, values);
删:
要从表格中删除行,您需要提供识别行的选择条件。
数据库 API 提供了一种机制,用于创建防止 SQL 注入的选择条件。 该机制将选择规范划分为选择子句和选择参数。
该子句定义要查看的列,还允许您合并列测试。 参数是根据捆绑到子句的项进行测试的值。
由于结果并未按照与常规 SQL 语句相同的方式进行处理,它不受 SQL 注入的影响。
// Define 'where' part of query. String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; // Specify arguments in placeholder order. String[] selectionArgs = { String.valueOf(rowId) }; // Issue SQL statement. db.delete(table_name, selection, selectionArgs);
改:
当您需要修改数据库值的子集时,请使用 update()
方法。
可将insert()
的内容值句法与 delete()
的 where
句法相结合。
SQLiteDatabase db = mDbHelper.getReadableDatabase(); // New value for one column ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_TITLE, title); // Which row to update, based on the ID String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; String[] selectionArgs = { String.valueOf(rowId) }; int count = db.update( FeedReaderDbHelper.FeedEntry.TABLE_NAME, values, selection, selectionArgs);
查:
query()
第一个参数是表名
第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列
第三、第四个参数用于去约束查询某一行或某几行的数据,不指定则默认是查询所有行的数据。 where
第五个参数用于指定需要去 group by 的列,不指定则表示不对查询结果进行 group by 操作 group by
第六个参数用于对 group by 之后的数据进行进一步的过滤,不指定则表示不进行过滤 having by
第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。 order by
返回一个 Cursor 对象
SQLiteDatabase db = mDbHelper.getReadableDatabase(); // Define a projection that specifies which columns from the database // you will actually use after this query. String[] projection = { FeedEntry._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_UPDATED, ... }; // How you want the results sorted in the resulting Cursor String sortOrder = FeedEntry.COLUMN_NAME_UPDATED + " DESC"; Cursor c = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The columns to return selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order );
对Cursor对象的操作
if (cursor.moveToFirst()) { do { // 遍历Cursor对象,取出数据并打印 //cursor.getString(cursor.getColumnIndex(ColumnName)) //cursor.getInt(cursor.getColumnIndex(ColumnName) ..... } while (cursor.moveToNext()); } cursor.close();