在 Android 中,提供了三种数据存储的途径,和两种存储方式。
三种途径:
l 系统配置 ( Shared Preferences ) :
这类应用主要是系统的配置信息的保存,比如我给程序界面设置了颜色,我想在下一次启动时还是能够保留上次设置的颜色。由于 Android 系统的界面是采用 Activity 栈的形式,在系统资源不足时,会收回一些界面,那么,我想有些操作也是需要在不活动时保留下来的,等再次激活时能够显示出来。
l 文件( Files )
Android 是一个操作系统,自然而然对存储系统会有一个管理,因为采用提 Linux 核心,所有在 Andorid 系统中,文件也是 Linux 的形式。当然我们的应用程序也就可以把数据以文件的形式记录下来咯。
l 数据库( SQLite Databases )
在 Andriod 系统中,也少不了一个数据库管理,但考虑到系统资源(内存,硬盘),选择了轻便型的数据库 SQLite ,这是一个开源的关系型数据库,与普通关系型数据库一样,也具有 ACID 的特性。
两种存储方式:
主要是根据数据库共享方式来分
l 程序内自用:
通常我们程序中需要的数据一般都是为本程序来用,所以我们用上面三种途径来创建的程序都是默认为本程序使用,其他程序无法获取操作。
我们 ADB 插件功能在命令行下输入: adb shell 来进入手机的文件系统,进入 CD /data/data 目录。然后 ls 查看,我们发现,我们在系统中安装的每个程序在这里都有一个文件夹,再次进入我们的程序后 ls 查看,会出现几个目录: shared_prefs 、 files 、 databases ,这几个目录其实就是存的我们程序内自用的数据,内容分别就是由上面三种途径创建的,当然如果没有创建过,这个目录可能不存在。
l 数据需要共享:
这类数据通常是我们的一些共用数据,很多程序都会来调用,比如电话薄数据,就不可能存为私有的了。当然,这种方式的话,上面三种途径中的系统配置就不适用了,“系统配置”只能由本程序访问。也就是说,只有文件和数据库可以共享。
具体用没我们下面依次试一下:
(一) 系统配置
这类数据存储形式是 NVP 形式即, name 和 value 的映射 map 。
存:
// 取得活动的 preferences 对象 .
SharedPreferences uiState = getPreferences(0);
// 取得编辑对象
SharedPreferences.Editor editor = uiState.edit();
// 添加值
editor.putString( KEY , value );
editor.putBoolean( KEY , value );
editor.commit(); // 提交保存 .
取:
// 取得活动的 preferences 对象 .
SharedPreferences settings = getPreferences(Activity. MODE_PRIVATE );
// 取得值 .
String value = settings.getString( KEY , " 默认值 " );
Boolean value = settings.getBoolean( KEY , false );
存取时间:
有了存取的方法后,我们就要想在什么时候来存取:
protected void onPause()/ / 系统通知暂停,可以保存设置
public void onDestroy()/ / 系统通知关闭,可以保存设置
public void onCreate(Bundle savedInstanceState) / / 系统启动,可以打开设置
同样还有其他状态,我们可以根据实际情况来处理
(二) 文件操作
l 打开文件并可操作,如果文件不存在会自动创建:
FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
// 操作时,在 fos 里写入值即可
l 读文件:
FileInputStream fis = openFileInput(FILE_NAME);
l 取得当前活动创建的文件列表:
String[] lst =fileList()
l 删除文件:
super.deleteFile(FILE_NAME);
(三) SQLite 数据库操作
数据库操作无非是建库、建表、增、删、改、查的一些常用功能。在 Android 系统中封装了一个 SQLiteDatabase 类,用于这些操作。
l 建库
创建数据库,是由 SQLiteOpenHelper 类自动完成,同时,创建后,会返回一个 SQLiteDatabase 类:
try {
SQLiteOpenHelper dbHelper= new SQLiteOpenHelper(context, DBName, null,1);
db = dbHelper.getWritableDatabase();
} catch (SQLiteException ex) {
db = dbHelper.getReadableDatabase();
}
其中参数 DBName 就是数据库的名称。 getWritableDatabase() ,这方法就会获取数据库对象,如果库不存在就会新建,但这里为什么还要包一层 catch 呢,主要考虑到,手机的资源有限,有时空间不足了,就无法再写入数据,但这里我们可以读啊,所以在 catch 中调用了 getReadableDatabase 。
l 建表
_db.execSQL( “ create table tableName (id integer primary key autoincrement,name text not null); ” );
看到,这个建表其实跟我们写的 SQL 很类似,只是可能一些语法细节不同,具体这个就不写了, google 一下会 N 多了。
l 增
当然我们可以执行一条 SQL 语句来插入,我们也可以这样来:
ContentValues newTaskValues = new ContentValues();
// Assign values for each row.
newTaskValues.put( KEY_TASK , value);
newTaskValues.put( KEY_CREATION_DATE , value);
// Insert the row.
db .insert( DATABASE_TABLE , null , newTaskValues);
我们可以将值依次放入一个 Hash 表中,然后直接存入。
l 删
db.delete( tableName , "id=" + _rowIndex, null)
输入表名,以及条件,当然,直接拼成一条标准 SQL 也是可以的。
l 改
ContentValues newValue = new ContentValues();
newValue.put( KEY_TASK , _task);
db .update( DATABASE_TABLE , newValue, KEY_ID + "=" + _rowIndex, null
将值存于 Hash 表中,再直接调用
l 查
Cursor result =db.query(DATABASE_TABLE,
new String[] { KEY_ID, KEY_TASK, KEY_CREATION_DATE},
null, 条件 , null, null, null)
这里,查询后返回的是一个游标,我们可以通过这游标来获取数据,使用方法跟 java 通用程序一样。
还有更多的用法,只有边用边再搜 Google 了
(四) Content Providers 共享数据
在前面界面交互中,我们也学习到了, Android 系统的界面互相调用时,传值是采用 Uri 的方式进行的。表面上看, Uri 不就是一个字符串吗,能传多少值啊,其实不然,在 Uri 中格式: (1)://(2)/(3) ,这第一段我们不管,这第二段,其实在系统中是可以定义到我们的 Content Providers 的,我们可以写一个类,比如这个用来管理电话薄信息,如:
public class MyProvider extends ContentProvider {
private static final String myURI =
"content://com.zjf.MyProvider/items" ;
public static final Uri CONTENT_URI = Uri.parse ( myURI );
…
}
在这里 MyProvider 继承了 ContentProvider 类 ,然后我们在系统中注册一下(在 manifext.xml 中):
<provider android:name= ” MyProvider ” android:authorities= ” com.zjf.MyProvider ” />
然后我们在传值时使用: content:// com.zjf.MyProvider/1 来通过这个 Uri 我想得到电话薄中第一个人的信息,我们分析一下,这个最后一段“ 1 ”,就是第一个记录咯,“ com.zjf.MyProvider ” 这个就是标记我用哪一套程序来取数据,所以说,我们写的 MyProvider 就可以根据传入的“ 1 ”这个参数来进行数据的操作,这里你可以保存到文件中,也可以保存到数据库中,但对使用者来说是透明的,不需要知道内部细节。这就是我们说的数据共享模型。
我们进一步看 MyProvider 继承处理了哪些类:
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
@Override
public int delete(Uri uri, String selection, String[] selectionArgs)
@Override
public Uri insert(Uri uri, ContentValues values)
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs)
看到这些大家可能就更家明白了 MyProvider 就是一个数据的封装,它按照统一的接口来提供共享的数据,每一个 Provider 就是处理一类共享数据。
当然系统中其实己经供了这样的一些 Provider 供我们来使用,比如: Browser 记录了用户浏览记录; CallLog 记录系统日志; Contacts 电话薄; Settings 系统设置等。
看到这个不知大家有没有想到我们前面取得电话薄信息时是不是用的“ content:// Contacts/1 ”这样的 Uri ,对应起来咯。
看了具体的 Provider 的实现,我们又想到,那这个统一的数据接口标准到底能提供我们哪些功能呢,其实看了 Provider 内部实现,我们也发现了,不就也就是增、删、改、查么。对,我们可能通过统一的接口来对这公用数据进行这些操作。具体方法如下:
查询:
Cursor someRows = getContentResolver().query(MyProvider.CONTENT_URI,
null, where, null, order);
插入:
// Create a new row of values to insert.
ContentValues newValues = new ContentValues();
newValues.put(COLUMN_NAME, newValue);
Uri myRowUri = getContentResolver().insert(MyProvider.CONTENT_URI,newValues);
删除:
getContentResolver().delete(MyProvider.CONTENT_URI, where, null);
修改:
getContentResolver().update(MyProvider.CONTENT_URI, newValues, where,
null);
这些几个功能可以有点难理解,我们看个应用吧:
我想查询电话薄中姓陈的人的信息,然后再删除:
Cursor someRows = getContentResolver().query( “ content:// Contacts ” ,
null, “ name like ‘陈 % ’” , null, order);
// 然后从游标中获取数据即可。
getContentResolver().delete( “ content:// Contacts ” , “ name like ‘陈 % ’” , null);// 删除