1、概述
android中的数据是有权限控制的,应用软件数据为该应用软件所私有的。但是android也提供了一种标准的方式将软件的私有数据开放给其他应用软件。
android可供选择的存储方式:sharedPreferences、文件存储、SQLite数据库方式、内容提供器(Content provider)和网络存储。
2、sharedPreferences
sharedPreferences是一种轻量型的数据存储方式,本质上是基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息。sharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现的。(文件一般存放在/data/data/<包名>/shared_prefs目录下,只能在file explorer中看)
实现sharedPreferences存储的步骤为:
。根据Context获取sharedPreferences对象;
。利用edit()方法获取Editor对象(确保数据值是一致性的状态,并且控制所有的值都已经提交存储了);
。通过Editor对象存储key-value键值对数据;
。通过commit()方法提交数据
sharedPreferences比之于sqlLite数据库,免去了数据库方面的操作,更加的方便简洁。但是缺点就是只能存储boolean,int,float,long和String五种简单的数据类型,并且无法进行条件查询等。随意sharedPreferences只是一种补充方式。
private void doStore() {
// get the object of SharedPreferences
Context ctx = DataStoreActivity.this;
SharedPreferences sp = ctx.getSharedPreferences("SP", MODE_PRIVATE);
// store into values
Editor editor = sp.edit();
editor.putString("STRING_KEY", "string");
editor.putInt("INT_KEY", 0);
editor.putBoolean("BOOLEAN_KEY", true);
editor.commit();
//read it
Log.d("SP", sp.getString("STRING_KEY", "none"));
Log.d("SP", sp.getString("NOT_EXIST", "none"));
}
3、文件存储
android的文件操作和java中实现I/O的方式是类似的,其提供了openFileInput()和openFileOutput()方法来写入和读取设备上的文件。
FileOutputStream fos = ctx.openFileOutput("file_name", MODE_PRIVATE); //写入
FileInputStream fis = ctx.openFileInput("file_name"); //读取
这两个方法只支持读取该应用目录下的文件,读取非自身目录下的文件将会抛出异常。不存在操作的文件,系统会在写入的时候自动创建(创建的文件一般保存在/data/data/<包名>/files目录下)。
默认情况下,使用openFileOutput方法创建的文件只能被其调用的应用使用,其他应用无法读取这个文件。如果需要在不同应用中的共享,可以使用Content Provider实现这个功能。
// 写入文件
FileOutputStream fos = openFileOutput("test", MODE_WORLD_READABLE);
fos.write(text.getText().toString().getBytes());
// 读取
FileInputStream fis = openFileInput("test");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = -1;
while ((length=fis.read(buffer)) != -1) {
stream.write(buffer);
}
text.setText(stream.toString());
activity还提供了getCacheDir()和getFilesDir()方法:
。getCacheDir()方法用于获取/data/data/<package name>/cache目录
。getFilesDir()方法用于获取/data/data/<package name>/files目录
上面的操作都是将数据存储到手机内存或从手机内存读取数据,那怎么操作SD卡呢?
首先要在创建一张SDCard(镜像文件),创建SDCard可以再eclipse创建模拟器时同时创建,也可以使用dos命令【mksdcard -l mycard 20M F:\mysdcard.img】进行创建。
再就是需要为访问SDCard申请权限,如下:
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
要往sdcard存放文件,程序必须先判断手机是否装有sdcard,并且可以进行读写。
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
File saveFile = new File(sdCardDir, “a.txt”);
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("test".getBytes());
outStream.close();
}
Environment.getExternalStorageState():用于获取sdcard的状态,如果手机装有sdcard,并且可以进行读写,那么返回的状态就等于Environment.MEDIA_MOUNTED。
Environment.getExternalStorageDirectory():用于获取sdcard获取的目录,等价于:
File sdCardDir = new File("/sdcard"); //获取SDCard目录
4、Environment类
在没有挂载前存储媒体已经被移除。 | ||
正在检查存储媒体。 | ||
存储媒体已经挂载,并且挂载点可读/写。 | ||
存储媒体已经挂载,挂载点只读。 | ||
存储媒体是空白或是不支持的文件系统。 | ||
存储媒体被移除。 | ||
存储媒体正在通过USB共享。 | ||
存储媒体无法挂载。 | ||
存储媒体没有挂载。 |
static File | getDataDirectory() 获得android data的目录。 |
static File | getDownloadCacheDirectory() 获得下载缓存目录。 |
static File | getExternalStorageDirectory() 获得外部存储媒体目录。 |
static File | getExternalStoragePublicDirectory( String type) Get a top-level public external storage directory for placing files of a particular type(根据文件的类别放到想要放到的地方). |
static String | getExternalStorageState() 获得当前外部储存媒体的状态。 |
static File | getRootDirectory() 获得android的跟目录。 |
5、SQLLite存储方式
sqllite是一种轻型数据库,只支持五种数据类型,分别是:NULL(空值)、INTEGER(整数)、REAL(浮点数)、TEXT(字符串)、BLOB(大数据)。
1)创建数据库:openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory)— 根据给定条件连接数据库,如果此数据库不存在,则创建。
SQLiteDatabase db = this.openOrCreateDatabase("test_db.db", Context.MODE_PRIVATE, null);
SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase("/data/data/com.test/databases/test_db2.db3", null);
两种方式创建数据库,this.openOrCreateDatabase其实是根据SQLiteDatabase.openOrCreateDatabase而来的,SQLiteDatabase.openOrCreateDatabase()方法第一个参数要求输入绝对路径(所有的数据库都是存储在/data/data/<包名>/databases目录下);而采用this.openOrCreateDatabase()可以不用使用绝对路劲。
2)创建数据表:execSQL(String sql)— 执行给定SQL语句。
db.execSQL("create table tab(_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)");
execSQL()可以之人任何的sql语句。
3)CUID:insert、delete、update、query(rawQuery)
db.insert(String table, String nullColumnHack, ContentValues values);
db.update(String table, Contentvalues values, String whereClause, String whereArgs);
db.delete(String table, String whereClause, String whereArgs);
三个方法的第一个参数都是要进行操作的表名;
insert的第二个参数表示如果插入了一行空数据时,需要指定某一列的名称,系统将此列设置为NULL,否则会报错,第三个参数是ContentValues类型的变量,类似于键值对的Map,key代表列名,value代表值;
update的第二个ContentValues,更新该字段key为最新的value值,第三个参数whereClause表示WHERE表达式,比如"age > ? and age < ?”等,最后的whereArgs参数是占位符的实际参数值;
delete的参数和update是类似的;
db.rawQuery(String sql, String[] selectionArgs);
db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
上面几种都是常用的查询方法,第一种最为简单,将所有的SQL语句都组织到一个字符串中,使用占位符代替实际参数,selectionArgs就是占位符实际参数集;下面的几种参数都很类似,columns表示要查询的列所有名称集,selection表示WHERE之后的条件语句,可以使用占位符,groupBy指定分组的列名,having指定分组条件,配合groupBy使用,orderBy指定排序的列名,limit指定分页参数,distinct可以指定“true”或“false”表示要不要过滤重复值。需要注意的是,selection、groupBy、having、orderBy、limit这几个参数中不包括"WHERE”、"GROUP BY”、"HAVING”、"ORDER BY”、"LIMIT”等SQL关键字。
4)cursor操作
c.move(int offset); //以当前位置为参考,移动到指定行
c.moveToFirst(); //移动到第一行
c.moveToLast(); //移动到最后一行
c.moveToPosition(int position); //移动到指定行
c.moveToPrevious(); //移动到前一行
c.moveToNext(); //移动到下一行
c.isFirst(); //是否指向第一条
c.isLast(); //是否指向最后一条
c.isBeforeFirst(); //是否指向第一条之前
c.isAfterLast(); //是否指向最后一条之后
c.isNull(int columnIndex); //指定列是否为空(列基数为0)
c.isClosed(); //游标是否已关闭
c.getCount(); //总数据项数
c.getPosition(); //返回当前游标所指向的行数
c.getColumnIndex(String columnName);//返回某列名对应的列索引值
c.getString(int columnIndex); //返回当前行指定列的值
完整的实例:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//打开或创建test.db数据库
SQLiteDatabase db = openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null);
db.execSQL("DROP TABLE IF EXISTS person");
//创建person表
db.execSQL("CREATE TABLE person (_id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR, age SMALLINT)");
Person person = new Person();
person.name = "john";
person.age = 30;
//插入数据
db.execSQL("INSERT INTO person VALUES (NULL, ?, ?)", new Object[]{person.name, person.age});
person.name = "david";
person.age = 33;
//ContentValues以键值对的形式存放数据
ContentValues cv = new ContentValues();
cv.put("name", person.name);
cv.put("age", person.age);
//插入ContentValues中的数据
db.insert("person", null, cv);
cv = new ContentValues();
cv.put("age", 35);
//更新数据
db.update("person", cv, "name = ?", new String[]{"john"});
Cursor c = db.rawQuery("SELECT * FROM person WHERE age >= ?", new String[]{"33"});
while (c.moveToNext()) {
int _id = c.getInt(c.getColumnIndex("_id"));
String name = c.getString(c.getColumnIndex("name"));
int age = c.getInt(c.getColumnIndex("age"));
Log.i("db", "_id=>" + _id + ", name=>" + name + ", age=>" + age);
}
c.close();
//删除数据
db.delete("person", "age < ?", new String[]{"35"});
//关闭当前数据库
db.close();
//删除test.db数据库
deleteDatabase("test.db");
}
6、SQLiteOpenHelper
帮助管理数据库的创建和版本的管理。要使用它必须实现它的onCreate(SQLiteDatabase),onUpgrade(SQLiteDatabase, int, int)方法。
onCreate:当数据库第一次被建立的时候被执行;
onUpgrade:当数据库需要被更新的时候执行,如数据库版本的变更;
还可以根据实际情况实现onOpen()等方法。
7、ContentProvider
android系统的数据时私有的,包括文件数据和数据库数据,两个程序之间无法进行数据的交换。ContentProvider实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去,外部可以访问。步骤为:
1)在当前应用程序中实现一个Content Provider
2)在当前应用程序的AndroidManifest.xml中注册此Content Provider
3)其他程序通过ContentResolver和Uri来获取此Content Provider的数据
3、Handler
Handler主要负责发送和处理消息,用途:
。按计划发送消息或执行某个线程;
。从其他线程中发送来的消息放入消息队列中,避免线程冲突;