Android中的数据存储有以下几种方式:

  1. 文件存储
  2. SharedPreferences
  3. SQLite数据库
  4. Content Provider

文件存储

Activity中的openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在JavaSE 环境中保存数据到文件中是一样的。
openFileInput()方法则用于读取当前应用的保存的数据

openFileOutpupt详解

openFileOutput(String name, int mode)
name - 指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data//files目录,如: /data/data/net.yrom.xxx/files/xxx.txt
mode - 文件操作模式,即访问权限

0 或者MODE_PRIVATE 默认的模式,文件为私有的,只能本应用程序才能访问;
MODE_APPEND 添加默认,数据将追加到文件末尾;
MODE_WORLD_READABLE 全局可读;!危险
MODE_WORLD_WRITEABLE 全局可写。!危险

eg.

1
2
3
4
5
6
7
8
9
public class FileActivity extends Activity {
//...
public void save2File(byte[] data, String filename) {
//...
FileOutputStream out = this.openFileOutput(filename, Context.MODE_PRIVATE);
out.write( data);
out.close();
}
}

获得应用的文件存储路径

getFileDir() - /data/data/<当前应用包名>/files/
getCacheDir() - /data/data/<当前应用包名>/cache/
Environment.getExternalStorageDirectory() - 用于获取SDCard的目录,
注意:

  1. 写数据应在程序清单文件中加入sdcard的访问权限:
    android.permission.WRITE_EXTERNAL_STORAGE
  2. 先判断sdcard是否挂载:
    Environment.getExternalStorageState() 应返回 Environment.MEDIA_MOUNTED
    eg.获取sdcard的可用大小:
    1
    2
    3
    4
    5
    6
    7
    8
    if(Environment.getExternalStorageState().equals(Environment. MEDIA_MOUNTED)){
    File sd = Environment. getExternalStorageDirectory();
    StatFs stat = new StatFs(sd.getPath());
    long availableBlocks = stat.getAvailableBlocks();
    long blockSize = stat.getBlockSize();
    long availableSize = availableBlocks * blockSize;
    String totalAvailableSize = Formatter.formatFileSize(getApplicationContext(), availableSize);
    }

    SharedPreferences

    Android应用一般采用SharedPreferences来存储于应用相关的配置参数
    其实就是 /data/data/<package name>/shared_prefs/ 目录下的xml文件

    存数据

    SharedPreferences存数据需要调用edit()来获得Editor对象,调用其putXxx()方法来设置key及对应的值,赋值结束后需调用commit()方法来提交更改
    eg.
    1
    2
    3
    4
    5
    6
    7
    String data = "test";
    // 指定文件名,及其访问模式
    SharedPreferences sp = context.getSharedPreferences("config", MODE_PRIVATE);
    Editor edit = sp .edit();
    edit.putString( "data", data);
    edit.putInt( "num", 123);
    edit.commit();
    生成的config.xml文件
    SharedPreferences - config.xml
    1
    2
    3
    4
    5
    <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <map>
    <string name="data">test</string>
    <int name="num" value="123" />
    </map>

    读数据

    getXxx(key,defaultValue) Xxx为数据类型。
    如果对应key在SharedPreferences没有数据则会返回defaultValue
    eg.
    1
    2
    3
    SharedPreferences sp = context.getSharedPreferences("config", MODE_PRIVATE );
    String data = sp.getString("data","abc");// 若没有读取到key为data对应的值,则返回abc
    int num = sp.getInt("num",111);// 若没有读取到key为num对应的值,则返回111

    SQLite数据库

    Android底层内嵌了数据库SQLite。
    Android sdk提供了SQLiteOpenHelper抽象类,来进行数据库的版本控制

Android下创建数据库的步骤:

  1. 创建一个数据库打开的帮助类,继承SQLiteOpenHelper
  2. 构造方法中的参数:设置数据库文件的名称,设置游标工厂(一般为null),数据库的版本
  3. 覆盖onCreate()方法:数据库表结构的初始化,数据库第一次被创建的时候会调用的方法
  4. helper.getReadabledatabase() 或者调用helper.getWriteabledatabase() 获取数据库的示例

SQLiteOpenHelper

为了实现对数据库版本进行管理,SQLiteOpenHelper类提供了两个重要的方法,分别是onCreate(SQLiteDatabase db)onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
前者用于初次使用软件时生成数据库表,后者用于升级软件时更新数据库表结构。

当调用SQLiteOpenHelper对象的getWritableDatabase()或者getReadableDatabase()方法获取用于操作数据库的SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生成一个数据库,接着调用onCreate()方法。

  • onCreate()方法在初次生成数据库时才会被调用,在onCreate()方法里可以生成数据库表结构及添加一些应用使用到的初始化数据。
  • onUpgrade()方法在数据库的版本发生变化时会被调用,一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的

WritableDatabase和ReadableDatabase

getWritableDatabase()getReadableDatabase()方法都可以获取一个用于操作数据库的SQLiteDatabase实例。
但getWritableDatabase()方法以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用getWritableDatabase()打开数据库就会出错。
getReadableDatabase()方法先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。
第一次调用getWritableDatabase()或getReadableDatabase()方法后,SQLiteOpenHelper会缓存当前的SQLiteDatabase实例,SQLiteDatabase实例正常情况下会维持数据库的打开状态,所以在当不再需要SQLiteDatabase实例时,应及时调用close()方法释放资源。
一旦SQLiteDatabase实例被缓存多次调用getWritableDatabase()或getReadableDatabase()方法得到的都是同一实例

SQLite事务

使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction()方法时会检查事务的成功标识是否为true,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标识为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务

eg.

SQLiteOpenhelper - NoteSQLiteOpenhelper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class NoteSQLiteOpenhelper extends SQLiteOpenHelper {
public NoteSQLiteOpenhelper(Context context) {
super(context, "note.db", null, 1);
}
/**
* 仅当第一次创建数据库时访问此方法进行数据表的初始化
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL( "create table account (id integer primary key autoincrement, name varchar(50), cost float(8,2) not null)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO 在更新数据库版本时,进行的操作
}
}

Dao - NoteDao.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class NoteDao {
private NoteSQLiteOpenhelper helper;
public NoteDao(Context context){
helper = new NoteSQLiteOpenhelper(context);
}
public void add(String name, float cost){
SQLiteDatabase db = helper.getWritableDatabase();
String sql = "insert into account (name, cost) values (?,?)";
Object[] bindArgs = {name,cost};
db.execSQL(sql, bindArgs );
db.close();
}
public void delete(int id){
SQLiteDatabase db = helper.getWritableDatabase();
String sql = "delete from account where id=?";
Object[] bindArgs = {id};
db.execSQL(sql, bindArgs );
db.close();
}
public void update(String name, float cost){
SQLiteDatabase db = helper.getWritableDatabase();
String sql = "update account set cost=? where name=?";
Object[] bindArgs = {cost,name};
db.execSQL(sql, bindArgs );
db.close();
}
public List<NoteBean> queryAll(){
List<NoteBean> notes = new ArrayList<NoteBean>();
SQLiteDatabase db = helper.getReadableDatabase();
String sql = "select id,name,cost from account";
Cursor cursor = db.rawQuery(sql, null);
while(cursor.moveToNext()){
int id = cursor.getInt(0);
String name = cursor.getString(1);
float cost = cursor.getFloat(2);
notes.add( new NoteBean(id,name,cost));
}
db.close();
return notes;
}
public void update(int id, float cost) {
SQLiteDatabase db = helper.getWritableDatabase();
String sql = "update account set cost=? where id=?";
Object[] bindArgs = {cost,id};
db.execSQL(sql, bindArgs );
db.close();
}
public void trans(int from, int to) {
SQLiteDatabase db = helper.getWritableDatabase();
db.beginTransaction(); // 开启事务
try {
db.execSQL( "update account set cost=cost-5 where id=?",new Object[]{from});
db.execSQL( "update account set cost=cost+5 where id=?",new Object[]{to});
// 事务结束,标记为成功
db.setTransactionSuccessful();
} finally {
// 如果被标记为成功,则commit
// 否则,rollback
db.endTransaction();
}
}
}

SQLite3命令

可以利用adb工具进入到Android的linux控制台,通过sqlite3 命令进行数据库的操作(一般需要有root权限,Android虚拟机默认为root)。
$ adb shell 首先挂载到linux
$ cd data/data/com.android.contacts.provider
$ cd databases
$ sqlite3 <数据库> 打开数据库:sqlite3 contacts.db
sqlite3>.tables 查看所有的表
sqlite3>.schema 查看所有的创建表、视图
sqlite3>.help 查看帮助
sqlite3>.header(s) NO |OFF是否显示列头信息
sqlite3>.mode MODE table 指定数据显示风格 eg: .mode column
sqlite3>.nullValue NULL空值数据显示问题 eg: .nullValue NULL

内容提供者content provider

应用的数据一般是私有的,外部是不可以访问的,可以通过content provider来对外提供私有数据的访问,如增删改查。
*具体见ContentProvider的详解篇