android中通过文件 SharedPreferences Sqlite ContentProvider存储数据

来源:互联网 发布:增值税算法公式 编辑:程序博客网 时间:2024/05/20 06:24

一、文件

1、使用文件进行数据存储

在上下文中有一个方法叫openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的。
FileOutputStream outStream = this.openFileOutput("zxl.txt", Context.MODE_PRIVATE);
outStream.write("CurtainZXL".getBytes());
outStream.close();   

openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android会自动创建它。创建的文件保存 在/data/data/<package name>/files目录,如:/data/data/cn.itcast/files/zxl.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data/<package name>/files目录就可以看到该文件。

openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE    =  0
Context.MODE_APPEND    =  32768
Context.MODE_WORLD_READABLE =  1
Context.MODE_WORLD_WRITEABLE =  2

Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望文件被其他应用读和写,可以传入: 
openFileOutput("zxl.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);

注:android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package name>/files),其他程序无法访问。除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。

2、读取文件内容

如果要打开存放在/data/data/<package name>/files目录应用私有的文件,可以使用Activity提供openFileInput()方法。
FileInputStream inStream = this.getContext().openFileInput("zxl.txt");
Log.i("FileTest", readInStream(inStream));
readInStream()
或者直接使用文件的绝对路径:
File file = new File("/data/data/com.zxl/files/zxl.txt");
FileInputStream inStream = new FileInputStream(file);
Log.i("FileTest", readInStream(inStream));
注:上面文件路径中的“com.zxl”为应用所在包,当你在编写代码时应替换为你自己应用使用的包。
对于私有文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文件时,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。

Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录
3、把文件存放在SDCard

使用Activity的openFileOutput()方法保存文件,文件是存放在手机空间上,一般手机的存储空间不是很大,存放些小文件还行,如果要存放像视频这样的大文件,是不可行的。对于像视频这样的大文件,我们可以把它存放在SDCard。 SDCard可以把它看作是移动硬盘或U盘。
在模拟器中使用SDCard,你需要先创建一张SDCard卡(当然不是真的SDCard,只是镜像文件)。创建SDCard可以在Eclipse创建模拟器时随同创建。
在程序中访问SDCard,你需要申请访问SDCard的权限。
// 挂载和卸载SDCard
android.permission.MOUNT_UNMOUNT_FILESYSTEMS
// 写入外存储设备权限
android.permission.WRITE_EXTERNAL_STORAGE
要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且可以进行读写。
注:访问SDCard必须在AndroidManifest.xml中加入访问SDCard的权限
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
          File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
          File saveFile = new File(sdCardDir, “zxl.txt”);
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("CurtainZXL".getBytes());
outStream.close();
}
Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。
Environment.getExternalStorageDirectory()方法用于获取SDCard的目录,当然要获取SDCard的目录,你也可以这样写:
File sdCardDir = new File("/mnt/sdcard"); //获取SDCard目录
File saveFile = new File(sdCardDir, "zxl.txt"); 
//上面两句代码可以合成一句: File saveFile = new File("/mnt/sdcard/zxl.txt");
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("CurtainZXL".getBytes());
outStream.close();
4、使用Pull解析器生成XML文件

有些时候,我们需要生成一个XML文件,生成XML文件的方法有很多,如:可以只使用一个StringBuilder组拼XML内容,然后把内容写到文件中;或使用DOM API生成XML文件,或者也可以使用pull解析器生成XML文件,这里推荐大家使用Pull解析器。
使用Pull解析器生成一个与zxl.xml文件内容相同的mytest.xml文件。
使用代码如下(生成XML文件):
File xmlFile = new File("mytest.xml");
FileOutputStream outStream = new FileOutputStream(xmlFile);
OutputStreamWriter outStreamWriter = new OutputStreamWriter(outStream, "UTF-8");
BufferedWriter writer = new BufferedWriter(outStreamWriter);
writeXML(persons, writer);
writer.flush();
writer.close();
如果只想得到生成的xml字符串内容,可以使用StringWriter:
StringWriter writer = new StringWriter();
writeXML(persons, writer);
String content = writer.toString();

下面是本例子要解析的XML文件:
文件名称:zxl.xml
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id=“18">
<name>allen</name>
<age>36</age>
</person>
<person id=“28">
<name>james</name>
<age>25</age>
</person>
</persons>
javabean:
public class Person {
private Integer id;
private String name;
private Short age;

public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
}

生成Xml数据:
public static String writeXML(List<Person> persons, Writer writer){
    XmlSerializer serializer = Xml.newSerializer();
    try {
        serializer.setOutput(writer);
        serializer.startDocument("UTF-8", true);
        //第一个参数为命名空间,如果不使用命名空间,可以设置为null
        serializer.startTag("", "persons");
        for (Person person : persons){
            serializer.startTag("", "person");
            serializer.attribute("", "id", person.getId().toString());
            serializer.startTag("", "name");
            serializer.text(person.getName());
            serializer.endTag("", "name");
            serializer.startTag("", "age");
            serializer.text(person.getAge().toString());
            serializer.endTag("", "age");
            serializer.endTag("", "person");
        }
        serializer.endTag("", "persons");
        serializer.endDocument();
        return writer.toString();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}


解析Xml文件:
public static List<Person> readXML(InputStream inStream) {
XmlPullParser parser = Xml.newPullParser();
try {
parser.setInput(inStream, "UTF-8");
int eventType = parser.getEventType();
Person currentPerson = null;
List<Person> persons = null;
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_DOCUMENT://文档开始事件,可以进行数据初始化处理
persons = new ArrayList<Person>();
break;
case XmlPullParser.START_TAG://开始元素事件
String name = parser.getName();
if (name.equalsIgnoreCase("person")) {
currentPerson = new Person();
currentPerson.setId(new Integer(parser.getAttributeValue(null, "id")));
} else if (currentPerson != null) {
if (name.equalsIgnoreCase("name")) {
currentPerson.setName(parser.nextText());// 如果后面是Text节点,即返回它的值
} else if (name.equalsIgnoreCase("age")) {
currentPerson.setAge(new Short(parser.nextText()));
}
}
break;
case XmlPullParser.END_TAG://结束元素事件
if (parser.getName().equalsIgnoreCase("person") && currentPerson != null) {
persons.add(currentPerson);
currentPerson = null;
}
break;
}
eventType = parser.next();
}
inStream.close();
return persons;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

二、使用SharedPreferences进行数据存储

很多时候我们开发的软件需要向用户提供软件参数设置功能,例如我们常用的QQ,用户可以设置是否允许陌生人添加自己为好友。对于软件配置参数的保存,如果是window软件通常我们会采用ini文件进行保存,如果是j2se应用,我们会采用propertie属性文件或者xml进行保存。如果是Android应用,我们最适合采用什么方式保存软件配置参数呢?Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下:
SharedPreferences sharedPreferences = getSharedPreferences("zxl", Context.MODE_PRIVATE);
Editor editor = sharedPreferences.edit();//获取编辑器
editor.putString("name", "CurtainZXL");
editor.putInt("age", 4);
editor.commit();//提交修改
生成的zxl.xml文件内容如下:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="name">CurtainZXL</string>
<int name="age" value="4" />
</map>
因为SharedPreferences背后是使用xml文件保存数据,getSharedPreferences(name,mode)方法的第一个参数用于指定该文件的名称,名称不用带后缀,后缀会由Android自动加上。方法的第二个参数指定文件的操作模式,共有四种操作模式,这四种模式前面介绍使用文件方式保存数据时已经讲解过。如果希望SharedPreferences背后使用的xml文件能被其他应用读和写,可以指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。
另外Activity还提供了另一个getPreferences(mode)方法操作SharedPreferences,这个方法默认使用当前类不带包名的类名作为文件的名称。

访问SharedPreferences中的数据代码。
SharedPreferences sharedPreferences = getSharedPreferences("zxl", Context.MODE_PRIVATE);
//getString()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", -1);
如果想访问其他应用中的Preference。
有两个前提条件是:
两个应用程序需要在AndroidManifest.xml中manifest节点里添加sharedUserId属性,并且要一样,而且还要有两级,也就是需要有个“.”
该preference创建时必须指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。
满足上面两个条件之后在我们应用中需要得到另一个应用的上下文对象:
Context otherAppsContext = createPackageContext("com.zxl",Context.CONTEXT_IGNORE_SECURITY);
拿到上下文对象之后通过调用上下文中的方法得到SharedPreferences对象,最后进行数据的交互。
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("zxl",Context.MODE_WORLD_READABLE);

三、Sqlite 轻量级数据库

1、SQLite,一款轻型数据库,遵守ACID(原子性、一致性、隔离性、持久性)的关联式数据库管理系统,多用于嵌入式开中。

SQLite的数据类型:Typelessness(无类型), 可以保存任何类型的数据到你所想要保存的任何表的任何列中. 但它又支持常见的类型比如: NULL, VARCHAR, TEXT, INTEGER, BLOB, CLOB...等. 唯一的例外:integer primary key 此字段只能存储64位整数

在Android系统,提供了一个SQLiteOpenHelper抽象类,该类用于对数据库版本进行管理.该类中常用的方法:
onCreate 数据库创建时执行(第一次连接获取数据库对象时执行)
onUpgrade 数据库更新时执行(版本号改变时执行)
onOpen 数据库每次打开时执行(每次打开数据库时调用,在onCreate,onUpgrade方法之后)

2、使用SQLiteDatabase操作SQLite数据库

Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库的API,使用该类可以完成对数据进行添(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。对SQLiteDatabase的学习,我们应该重点掌握execSQL()和rawQuery()方法。 execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL句;rawQuery()方法用于执行select语句。
execSQL()方法的使用例子:

SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.execSQL("insert into person(name, age) values('CurtainZXL', 4)");
db.close();
执行上面SQL语句会往person表中添加进一条记录,在实际应用中, 语句中的“CurtainZXL”这些参数值会由用户输入界面提供,如果把用户输入的内容原样组拼到上面的insert语句,当用户输入的内容含有单引号时,组拼出来的SQL语句就会存在语法错误。要解决这个问题需要对单引号进行转义,也就是把单引号转换成两个单引号。有些时候用户往往还会输入像“ & ”这些特殊SQL符号,为保证组拼好的SQL语句语法正确,必须对SQL语句中的这些特殊SQL符号都进行转义,显然,对每条SQL语句都做这样的处理工作是比较烦琐的。 SQLiteDatabase类提供了一个重载后的execSQL(String sql, Object[] bindArgs)方法,使用这个方法可以解决前面提到的问题,因为这个方法支持使用占位符参数(?)。使用例子如下:
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"CurtainZXL", 4}); 
db.close();
execSQL(String sql, Object[] bindArgs)方法的第一个参数为SQL语句,第二个参数为SQL语句中占位符参数的值,参数值在数组中的顺序要和占位符的位置对应。

SQLiteDatabase的rawQuery() 用于执行select语句,使用例子如下:

SQLiteDatabase db =mOpenHelper.getReadableDatabase();
Cursor cursor = db.rawQuery(“select * from person”, null);
while (cursor.moveToNext()) {
int personid = cursor.getInt(0); //获取第一列的值,第一列的索引从0开始
String name = cursor.getString(1);//获取第二列的值
int age = cursor.getInt(2);//获取第三列的值
}
cursor.close();
db.close(); 
rawQuery()方法的第一个参数为select语句;第二个参数为select语句中占位符参数的值,如果select语句没有使用占位符,该参数可以设置为null。带占位符参数的select语句使用例子如下:
Cursor cursor = db.rawQuery("select * from person where name like ? and age=?", new String[]{"%ZXL%", "4"});
Cursor是结果集游标,用于对结果集进行随机访问,如果大家熟悉jdbc, 其实Cursor与JDBC中的ResultSet作用很相似。使用moveToNext()方法可以将游标从当前行移动到下一行,如果已经移过了结果集的最后一行,返回结果为false,否则为true。另外Cursor 还有常用的moveToPrevious()方法(用于将游标从当前行移动到上一行,如果已经移过了结果集的第一行,返回值为false,否则为true )、moveToFirst()方法(用于将游标移动到结果集的第一行,如果结果集为空,返回值为false,否则为true )和moveToLast()方法(用于将游标移动到结果集的最后一行,如果结果集为空,返回值为false,否则为true ) 。

除了execSQL()和rawQuery()方法, SQLiteDatabase还专门提供了对应于添加、删除、更新、查询的操作方法: 
insert() 增加数据
delete() 删除数据
update() 修改数据
query() 查询数据
这些方法实际上是给那些不太了解SQL语法的开发者使用的,对于熟悉SQL语法的程序员而言,直接使用execSQL()和rawQuery()方法执行SQL语句就能完成数据的添加、删除、更新、查询操作。

3、使用事务操作SQLite数据库

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

SQLiteDatabase db =mOpenHelper.getWritableDatabase();
db.beginTransaction();//开始事务
try {
    db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"ZXL", 4});
    db.execSQL("update person set name=? where personid=?", new Object[]{"ZXL", 1});
    db.setTransactionSuccessful();//调用此方法会在执行到endTransaction() 时提交当前事务,如果不调用会回滚事务
} finally {
db.endTransaction();//由事务的标志决定是提交事务,还是回滚事务

db.close(); 
上面两条SQL语句在同一个事务中执行。


四、ContentProvider

1、ContentProvider 内容提供者

当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:
采用文件方式对外共享数据,需要进行文件操作读写数据;
采用SharedPreferences共享数据,需要使用SharedPreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。

1):schema,用来说明一个ContentProvider控制这些数据。 "content://"
2):主机名或授权(Authority),它定义了是哪个ContentProvider提供这些数据。
3):path路径,URI下的某一个Item。
4):ID, 通常定义Uri时使用”#”号占位符代替, 使用时替换成对应的数字
"content://com.zxl.contentprovider/person/#" #表示数据id(#代表任意数字)
"content://com.zxl.contentprovider/person/*" *来匹配任意文本

2、ContentProvider 共享数据

onCreate 其它应用第一次访问时被调。
insert 外部应用使用此方法添加数据。
delete 外部应用使用此方法删除数据。
update 外部应用使用此方法更新数据。
query 外部应用使用此方法查询数据。
getType 主要用于匹配数据类型(例如:接收系统广播时传递的数据类型),返回当前Uri所代表数据的MIME类型。如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/自定义类型。数据属于非集合类型数据,应该返回vnd.android.cursor.item/自定义类型。
3、UriMatcher类使用介绍

UriMatcher类用于匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.zxl.provider.personprovider/person路径,返回匹配码为1
sMatcher.addURI(“com.zxl.provider.personprovider”, “person”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配content://com.zxl.provider.personprovider/person/230路径,返回匹配码为2
sMatcher.addURI(“com.zxl.provider.personprovider”, “person/#”, 2);//#号为通配符
switch (sMatcher.match(Uri.parse("content://com.zxl.provider.personprovider/person/10"))) { 
    case 1
    break;
    case 2
    break;
    default://不匹配
    break;
}
注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.zxl.provider.personprovider/person路径,返回的匹配码为1

4、ContentUris类使用介绍

ContentUris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:
Uri uri = Uri.parse("content://com.zxl.provider.personprovider/person")
Uri resultUri = ContentUris.withAppendedId(uri, 10); 
//生成后的Uri为:

content://com.zxl.provider.personprovider/person/10
parseId(uri)方法用于从路径中获取ID部分:
Uri uri = Uri.parse("content://com.zxl.provider.personprovider/person/10")
long personid = ContentUris.parseId(uri);//获取的结果为:10
另外Uri类中还有一个静态方法withAppendedPath(baseUri, pathSegment)也可以在某个路径上继续添加路径:
Uri uri = Uri.parse("content://com.zxl.provider.personprovider/person")
Uri resultUri = Uri.withAppendedPath(uri, “update”);
// 生成后的Uri为: 

content://com.zxl.provider.personprovider/person/update

5.使用ContentObserver监听 ContentProvider中数据的变化

如果ContentProvider的访问者需要知道ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下:
public class PersonContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("person", "personid", values);
getContext().getContentResolver().notifyChange(uri, null);
}
}
如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:
getContentResolver().registerContentObserver(Uri.parse("content://com.zxl.providers.personprovider/person"),true,    new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
  }
public void onChange(boolean selfChange) {
   //此处可以进行相应的业务处理
}
}

阅读全文
1 0
原创粉丝点击