Android 数据存储方式:
SharedPreferences是使用键值对的方式来存储数据的,可存储任何基础类型:booleans, floats, ints, longs, and strings.SharedPreferences文件都是存放在/data/data//shared_prefs/目录下的。SharedPreferences文件是使用XML格式来对数据进行管理的
用户配置:PreferenceActivity 会使用shared preferences自动存储配置
获取SharedPreferences
操作模式
写SharedPreferences
读SharedPreferences
调用SharedPreferences的getXxx()方法即可
栗子
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();
}
}
默认情况存储是 App 的私有数据,其他 App 无法访问,随 App一起删除。所有的文件都是默认存储到/data/data//files/目录下。
栗子
String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
操作模式
栗子
public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
提示:静态文件可以放在res/raw/目录. 使用openRawResource(R.raw. )打开,返回InputStream ,然后你懂的(但你不能写入这个原始文件).
使用 getCacheDir() 存储临时缓存文件
当设备内部存储空间不足时可能释放这些文件,但你不能指望系统去清理这些文件, 你应当自己管理限制他们(比如1M), 缓存文件会随应用删除而删除。
getFilesDir() 获取应用内部存储的绝对路径
getDir() 创建(或打开已存在)你内部存储空间的目录
deleteFile() 删除内部存储文件
fileList()返回你的应用已经保存的文件列表
Android 设备支持外部存储,包括可移动设备(SD卡等)和内部(不可移动)存储,外部存储 App 间共享,都可操作。
外部存储在用户安装外部存储在计算机或删除媒体变得不可用,并没有强制的安全措施在文件保存到外部存储。
使用外部存储需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 权限
Android 4.4 之后, 读写应用自身的私有数据不需要声明权限
在使用外部存储之前,应当先调用 Environment.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;
}
Environment.getExternalStoragePublicDirectory()可以获取到公共目录,比如 DIRECTORY_MUSIC, DIRECTORY_PICTURES, DIRECTORY_RINGTONES等,会被系统MediaStore获取,出现在音乐、相册等应用中。
栗子在图片目录创建相册
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;
}
Context.getExternalFilesDir(),参数可以是DIRECTORY_MOVIES等特定目录,null 则获取私有目录根路径,4.4 之后访问私有目录不需要声明权限
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
提示:私有目录会随应用一起删除,所以图片、音乐等属于用户的文件应存储在公共目录
有时内部存储空间会分出一块作为外部存储也可能会提供一个SD卡插槽,Android 4.3或之前 getExternalFilesDir()只能获取内部分区,不能读写SD卡。4.4 之后 getExternalFilesDirs()可以获取全部,返回File列表。第一条记录被认为是主要的外部存储,你应当使用他除非满了或不可用。如果想在4.3之前使用这些位置,可以使用支持包中的静态方法ContextCompat.getExternalFilesDirs(),同样返回File数组,但是总是只会包含一条记录。
注意: getExternalFilesDir() and getExternalFilesDirs() 的私有目录虽然不会被MediaStore获取,但是拥有READ_EXTERNAL_STORAGE权限的应用可以获取所有外部存储,包括你的文件,如果你想完全限制你的文件,应当使用内部存储。
getExternalCacheDir(),会随应用一起删除。同样,兼容包中的ContextCompat.getExternalCacheDirs()获取次要的外部存储目录
创建数据库的推荐做法是继承 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);
}
}
然后你可以用你实现的SqliteHelper实例,调用 getWritableDatabase() and getReadableDatabase()来操作数据库。当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则将出现异常。数据库文件会存放在/data/data//databases/目录下
数据库创建后不会再调用onCreate方法,卸载应用后数据库随之删除,再安装则可再执行。SQLiteOpenHelper的升级功能就可以很轻松地解决这个问题。
public class MyDatabaseHelper extends SQLiteOpenHelper {
……
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
SQLiteOpenHelper的构造方法里接收的第四个参数表示当前数据库的版本号,之前我们传入的是1,现在只要传入一个比1大的数,就可以让onUpgrade()方法得到执行了
SQLiteDatabase.insert()
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
// 开始组装第一条数据
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("Book", null, values); // 插入第一条数据
values.clear();
// 开始组装第二条数据
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("Book", null, values); // 插入第二条数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" });
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book", "pages > ?", new String[] { "500" });
SQLiteDatabase.query()接受多个参数来做查询,复杂的查询可以使用SQLiteQueryBuilder。
query()方法参数 |
对应SQL部分 |
描述 |
table |
from table_name |
指定查询的表名 |
columns |
select column1, column2 |
指定查询的列名 |
selection |
where column = value |
指定where的约束条件 |
selectionArgs |
- |
为where中的占位符提供具体的值 |
groupBy |
group by column |
指定需要group by的列 |
having |
having column = value |
对group by后的结果进一步约束 |
orderBy |
order by column1, column2 |
指定查询结果的排序方式 |
查询将返回指向结果集的Cursor
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 查询Book表中所有的数据
Cursor cursor = db.query("Book", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
// 遍历Cursor对象,取出数据并打印
String name = cursor.getString(cursor. getColumnIndex("name"));
String author = cursor.getString(cursor. getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex ("pages"));
double price = cursor.getDouble(cursor. getColumnIndex("price"));
} while (cursor.moveToNext());
}
cursor.close();
添加数据的方法如下:
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
更新数据的方法如下:
db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });
删除数据的方法如下:
db.execSQL("delete from Book where pages > ?", new String[] { "500" });
查询数据的方法如下:
db.rawQuery("select * from Book", null);
可以看到,除了查询数据的时候调用的是SQLiteDatabase的rawQuery()方法,其他的操作都是调用的execSQL()方法。
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction(); // 开启事务
try {
db.delete("Book", null, null);
if (true) {
// 在这里手动抛出一个异常,让事务失败
throw new NullPointerException();
}
ContentValues values = new ContentValues();
values.put("name", "Game of Thrones");
values.put("author", "George Martin");
values.put("pages", 720);
values.put("price", 20.85);
db.insert("Book", null, values);
db.setTransactionSuccessful(); // 事务已经执行成功
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction(); // 结束事务
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
case 1:
db.execSQL(CREATE_CATEGORY);
case 2:
db.execSQL("alter table Book add column category_id integer");
default:
}
}
onCreate 里写最新的表结构
onUpgrade 里判断oldVerison ,增加新的表修改,注意不使用break,让每个版本更新的时候都能全部执行到。
使用sqlite3工具
https://developer.android.com/tools/help/adb.html#sqlite