在好例子网,分享、交流、成长!
您当前所在位置:首页Java 开发实例Android平台开发 → 安卓-Junit-文件存储-XML解析方式-数据库-ListView-内容提供者-观察者 实例讲解

安卓-Junit-文件存储-XML解析方式-数据库-ListView-内容提供者-观察者 实例讲解

Android平台开发

下载此实例
  • 开发语言:Java
  • 实例大小:0.96M
  • 下载次数:21
  • 浏览次数:304
  • 发布时间:2013-05-23
  • 实例类别:Android平台开发
  • 发 布 人:huashan
  • 文件格式:.doc
  • 所需积分:2
 相关标签: 数据库 Xml Android

实例介绍

【实例简介】
【实例截图】
【核心代码】

安卓中的Junit

在安卓项目中写Main方法,并不能运行,想要测试某些方法,只能通过Junit测试。

使用方法可参考:

方法一,直接在项目中添加测试类

1. AndroidManifest.xml清单文件中添加配置

在根节点中加入:<instrumentation android:targetPackage="要测试的包名" android:name="android.test.InstrumentationTestRunner" />

application节点中加入:<uses-library android:name="android.test.runner" />

2. 定义一个类继承AndroidTestCase,定义测试方法

AndroidTestCase类中的两个方法

/**

 * 在测试用例执行之前 如果我们想进行一些初始化的操作

 */

@Override

protected void setUp() throws Exception {

super.setUp();

//测试前的操作

}

/**

 * 在测试用例执行之后调用的方法 ,

 * 扫尾,擦屁股的操作

 */

@Override

protected void tearDown() throws Exception {

super.tearDown();

//测试后的操作

}

方法二,创建测试项目

通常使用这种方式来测试,因为这样可以不污染要测试的项目。

1. 创建Android Test Project

2. 输入项目名,选择一个已存在的工程,Eclipse可以自动配置Junit环境

   

完成后,系统会在AndroidManifest.xml清单文件中添加配置。只需要写一个类继承AndroidTestCast即可。

文件操作方式(FileSharedPreferences XML、数据库)

File方式

思路:

1、 编写界面,要求,把指定的文件内容,保存到指定的文件中。

2、 添加写文件到SD卡里的权限:WRITE_EXTERNAL_STORAGE
注:写到ROM不需要权限,播放MP3和读SD卡不需要权限

3、 编写逻辑代码:

a) 获取文件名与文件内容

b) 获取按钮,并添加单击事件,在事件方法中判断按了哪个按钮就保存到哪里。

如果是保存到SD卡用: Environment.getExternalStorageDirectory()// 获取SD卡目录兼容所有版本,一般情况获取到的目录为:mnt/sdcard

如果是保存到ROM用:context.openFileOutput(name, Context.MODE_WORLD_READABLE   Context.MODE_WORLD_WRITEABLE);每一个参数为文件名,第二个参数为读写权限指定。这里的返回值是一个输出流。这里打开的输出流中的文件保存在:/data/data/com.itheima.file/files/上面指定的变量文件名(如abc.txt),这里的其他权限说明:
Context.MODE_PRIVATE;//以私有方式创建abc.txt文件,这个文件只能被这个应用所读写,其他应用程序将读取不到,要想被其他程序读取到则可以用Context.MODE_WORLD_READABLE方式创建

保存文件的两种方式:

1、保存文件到/data/data/cn.itcast.file_2/files/目录下:

FileOutputStream in = new FileOutputStream("/data/data/cn.itcast.file_2/files/abc.txt");

OutputStream out = context.openFileOutput(name, Context.MODE_WORLD_READABLE   Context.MODE_WORLD_WRITEABLE);

2、保存文件到SD卡(mnt/sdcard/)目录下:

FileOutputStream out = new FileOutputStream("mnt/sdcard/abc.txt");

FileOutputStream out = new FileOutputStream(“Environment.getExternalStorageDirectory()” abc.txt");

注意:保存到ROM中的第①种方式和保存到SD卡中的两种方式都没有指定权限,一般写文件时如果没有指定文件的权限,那么这个文件的权限默认继承它的父文件夹的权限

读写权限的总结如下:

使用Context.openFileOutput()方法中的权限有:

Context.MODE_PRIVATE (私有读写)

Context.MODE_WORLD_READABLE (全局可读)

Context.MODE_WORLD_WRITEABLE(全局可写)

Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE (全局可读写)
这里用 或   都可以,因为MODE_WORLD_READABLE = 0x0001
                     MODE_WORLD_WRITEABLE = 0x0002
二进制为  0000 0000 0000 0001
           0000 0000 0000 0010
不管是用或(|)还是用加( ),它们的结果都:0000 0000 0000 0011

context.MODE_APPEND(私有,以追加的方式写)

通过append的模式打开的文件 ,以后操作这个文件会以追加的方式把新的数据添加到文件的末尾,权限是私有的权限.

如果在别的应用中想以追加的方式写,则可以这样:

FileOutputStream fos = new FileOutputStream("/data/data/com.itheima.file/files/private.txt",true);

注意:在写到SD卡时,有可能SD卡没插或不可用,这时要判断SD卡的状态:Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

查看DDMS透视图中的Permissions(权限)

-rw-rw---- 

-rw-rw-rw-

-rw-rw-r--

-rw-rw--w-

-rw-rw----

第一个参数:“-”代表是文件,“d”代表是文件夹

后面的内容是linux操作系统的文件访问权限,后面的9个参数 可以把它分为3

前三个参数   当前用户的权限,这三个中的前两个代表读写,第三个如果是x代表可执行的

中间三个参数 当前用户所在的组的权限

最后面三个参数 所有人的权限

代表的是可执行

命令:abd shell   进入模拟器手机的根目录

      ls l         显示目录中的内容

     chmod是“change mode”的缩写,意为更改文件的读写权限,如:

      chmod 777  abc.txt  更改abc.txt 文件的读写权限为全局可读可写

这里的777是怎么来的呢?在Linux系统中,如果权限是rwx,则用111表示,即10进制中的7,如果权限是rw-,则用110表示,即10进制中的6

如果创建的文件夹或目录没有指定权限,那么默认继承父目录的权限 

读取文件的两种方式:

1、读取/data/data/cn.itcast.file_2/files/下的文件,可以用两种方式:

FileInputStream in = new FileInputStream("/data/data/cn.itcast.file_2/files/abc.txt");

FileInputStream in = activity.openFileInput(abc.txt);//默认读取:"/data/data/cn.itcast.file_2/files/”目录下的文件
注:ContextWrapper.getFilesDir()可获取/data/data/cn.itcast.file_2/files/路径,ActivityContextWrapper的子类

2、读取SD卡(mnt/sdcard/)目录下的文件:

FileInputStream in = new FileInputStream("mnt/sdcard/abc.txt");

FileInputStream in = new FileInputStream(“Environment.getExternalStorageDirectory()” abc.txt");

具体代码在07.File 项目中。

SharedPreferences方式

SharedPreferences类,它的功能与Properties类似,是保存键值对的,Properties是把键值对保存在.properties文件中的,而SharedPreferences是把键值对保存到xml文件中的,通过这个类操作的xml文件的读写不需要设置权限 

通过SharedPreferences方式保存的xml文件默认保存在:/data/data/com.itheima.sp目录下。

实例:如上图,通过点保存后,下次再启动这个程序时自动读取上次保存的数据并显示出来。

核心代码:

1、 获取SharedPreferences对象:Context. getSharedPreferences("config", MODE_PRIVATE);

2、 获取Editor对象,用于存放数据到SharedPreferences对象中:SharedPreferences.edit();
得到Editor之后,通过EditorputString()commit()方法向SharedPreferences存放数据和提交数据。

3、 SharedPreferences对象中有数据之后,通过该SharedPreferences对象的getString()取出数据

Activity中可以调用getPreferences(int mode)方法获得一个SharedPreferences,文件名和Activity名一致

具体代码在:08.SharedPreferences 项目中

XML方式

Ø Pull简介

Ÿ 常见的XML解析方式有三种,DOMSAXPullAndroid系统中推荐使用Pull

Ÿ Pull解析器是一个开源的Java项目,Android系统内部解析XML文件均为此种方式,也可用于JavaEE项目

Ÿ Android SDK中已经集成了Pull解析器,无需添加任何jar文件

Ÿ Pull解析器运行方式与SAX类似,提供各种事件的判断

Ÿ 官方网站:http://xmlpull.org/

Ø 使用Pull解析器解析XML文件

Ÿ Xml.newPullParser() 获得解析器

Ÿ parser.setInput(in, "UTF-8") 设置输入流以及编码

Ÿ parser.next() 获取下一个解析事件,得到一个事件代码

Ÿ XmlPullParser中定义了常量来标识各种解析事件

START_DOCUMENTEND_DOCUMENT START_TAG END_TAG TEXT 

Ø 使用XmlSerializer写出XML

Ÿ 使用以下方法生成XML,和XML文档顺序类似

startDocument

startTag

attribute

text

endTag

endDocument

Pull方式解析XML

有如下person.xml文件

<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>

<persons>

<person id="1">

<name>范冰冰</name>

<age>31</age>

</person>

<person id="2">

<name>林志玲</name>

<age>38</age>

</person>

<person id="3">

<name>杨幂</name>

<age>26</age>

</person>

</persons>

这里相当于描述了一个List<Person>  (persons标签),在这个集体里存放了3Person对象(person标签)

思路:

1、 根据上页的xml文件写一个Person类。

2、 获取xml文件:通过类加载器加载为一个输入流

3、 获取XmlPullParse解析器:XmlPullParser parser = Xml.newPullParser(); 并为这个解析器设置要解析的xml文件(以流的方式传入参数),并且指定流的解析编码:parser.setInput(in, "UTF-8");

4、 开始解析并生成Person对象

核心代码如下:

List<Person> persons = new ArrayList<Person>();

Person p = null;

XmlPullParser parser = Xml.newPullParser(); // 获取解析器

parser.setInput(in, "UTF-8"); // 设置输入流指定码表

for (int type = parser.getEventType(); type != XmlPullParser.END_DOCUMENT; type = parser.next()) { // 定义循环开始解析

if (type == XmlPullParser.START_TAG) { // 如果遇到开始标签的事件

if (parser.getName().equals("person")) { // 如果标签名为person

p = new Person(); // 创建Person对象

p.setId(Integer.valueOf(parser.getAttributeValue(0))); // 获取0号属性的值赋值给id属性

persons.add(p); // 将对象装入集合

} else if(parser.getName().equals("name")) { // 如果标签名为name

p.setName(parser.nextText()); // 获取下一个文本设置给name属性

} else if(parser.getName().equals("age")) { // 如果标签名为age

p.setAge(Integer.valueOf(parser.nextText())); // 获取下一个文本赋值给age属性

}

}

}

具体代码在:09.XML  项目中PersonService.java中的loadPersons()方法

通过Person对象生成XML

思路:

1、 获取一个保存了Person对象的List集合

2、 写一个输出流,这个输出流用于把Person写到哪个xml文件中。

3、 获取XmlSerializer 对象:XmlSerializer s = Xml.newSerializer(); 这个对象用于把java对象序列化成XML文件

4、 为这个XmlSerializer对象设置要序列化到哪个xml文件(以输出流的方式传入),并设置输出的编码格式:s.setOutput(out, "UTF-8");

5、 通过XmlSerializer对象把Person序列化为XML

核心代码如下:

XmlSerializer s = Xml.newSerializer();

s.setOutput(out, "UTF-8");

s.startDocument("UTF-8", true); // 开始文档

s.startTag(null, "persons"); // 开始persons标签

for (Person p : persons) {

s.startTag(null, "person"); // 开始person标签

s.attribute(null, "id", p.getId().toString()); // 设置id属性

s.startTag(null, "name"); // 开始name标签

s.text(p.getName()); // 设置文本

s.endTag(null, "name"); // 结束name标签

s.startTag(null, "age"); // 开始age标签

s.text(p.getAge().toString()); // 设置文本

s.endTag(null, "age"); // 结束age标签

s.endTag(null, "person"); // 结束person标签

}

s.endTag(null, "persons"); // 结束persons标签

s.endDocument(); // 结束文档

具体代码在:09.XML  项目中PersonService.java中的savePersons ()方法

Android中的数据库

安卓中使用的数据库都是SQLite数据库,SQLite中的一个数据库就是一个文件

安卓SDK中提供了一套操作SQLiteAPI

SQLite特点

Ÿ Android平台中嵌入了一个关系型数据库SQLite,和其他数据库不同的是SQLite存储数据时不区分类型

例如一个字段声明为Integer类型,我们也可以将一个字符串存入,一个字段声明为布尔型,我们也可以存入浮点数。

除非是主键被定义为Integer,这时只能存储64位整数

Ÿ 创建数据库的表时可以不指定数据类型,例如:

CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(20))

CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT, name)

Ÿ SQLite支持大部分标准SQL语句,增删改查语句都是通用的,分页查询语句和MySQL相同

SELECT * FROM person LIMIT 20 OFFSET 10

SELECT * FROM person LIMIT 10,20

Ÿ JDBC访问数据库不同,操作SQLite数据库无需加载驱动,不用获取连接,直接可以使用

获取SQLiteDatabase对象之后通过该对象直接可以执行SQL语句

SQLiteDatabase.execSQL()

SQLiteDatabase.rawQuery()

通过SQLiteOpenHelper.getWritableDatabase();方式创建的数据库默认保存在:/data/data/ 目录下

创建数据库与表的步骤

1、 创建一个类继承SQLiteOpenHelper,这会实现两个方法,且要显示写一个构造函数

构造函数,就是用来调用父类的构造函数来创建或打开数据库用的

2、 onCreate()方法是数据库第一次创建的时候自动执行的,在这里写上创建表的语句

3、 onUpgrade()方法是数据库版本升级的时候自动调用的

4、 写一个测试类继承AndroidTestCast,调用刚刚创建的类的getWritableDatabase();方法即可以获取到一个可写的数据库对象(SQLiteDatabase)。SQLiteDatabase对象就有增、删、改、查的方法。通过查看getWritableDatabases()方法的源代码可以了解到获取数据库的原理。

具体代码在:10.SQLite项目中的

com.itheima.sqlite包:DBOpenHelper文件、BTest文件。

SQLite的增删改查方式一

---------------使用SQLiteDatabaseexecSQL()方法和rawQuery( )方法

具体代码(包括分页查询)在:10.SQLite项目中的:

com.itheima.sqlite包:BOpenHelper文件

com.itheima.sqlite.domain包:Person文件

com.itheima.sqlite.dao包:PersonDaoClassic文件

com.itheima.sqlite.test包:DBTest文件。

关于getWritableDatabase()getReadableDatabase()的区别

1、 getWritableDatabase()获取到的数据库并不是只能写,它也能读

2、 getReadableDatabase()这也不是说获取只能读的数据库,执行方法时,方法内部会先调用:getWritableDatabase(),如果获取不到可写的数据库才去获取一个只能读的数据库。

SQLite的增删改查方式二

---------------使用SQLiteDatabaseinsert( )update( )delete( )方法和query( )方法

使用几个增删改查的方法好处是省略了写SQL关键字,只需指定要操作的表名,字段名,值,条件等即可。

具体代码(包括分页查询)在:10.SQLite项目中的:

com.itheima.sqlite包:BOpenHelper文件

com.itheima.sqlite.domain包:Person文件

com.itheima.sqlite.dao包:PersonDao文件

com.itheima.sqlite.test包:DBTest文件。

关于数据库的总结:

-------------总结所用到的类的功能

SQLiteOpenHelper要想创建或者得到一个数据库,必须要创建一个类继承它,通过创建的个类可以得到一个数据库(即SQLiteDatabase对象),并且可以在这个类的onCreate()生命周期方法里创建数据库表;

SQLiteDatabase: 这个类提供了增删改查的方法。其中查的方法返回的是一个Cursor对象

Cursor:这个类用来保存查数据库时返回的一条一条的记录,该对象提供了获取这些记录的方法

ContentValues:相当于Map,是用于保存键值对,用于在SQLite的增删改查方式二中需要指定的值。

事务管理

Ÿ 在使用SQLite数据库时可以用SQLiteDatabase类中定义的相关方法控制事务

beginTransaction() 开启事务

setTransactionSuccessful() 设置事务成功标记

endTransaction() 结束事务

Ÿ endTransaction()需要放在finally中执行,否则事务只有到超时的时候才自动结束,会降低数据库并发效率

ListView组件

要求,显示数据库中的记录,然后点某条记录的时候显示详细信息。

步骤:

1、 先写主界面,代码在:10.SQLite/res/layout/main.xml ,这个布局用来显示ListView组件

2、 根据数据库中的表(如Person)的记录的字段建立一个10.SQLite/res/layout/item.xml 

3、 Activity中完成逻辑代码,步骤如下:

核心代码:

主要在ListAdpter的:

View view = View.inflate(getApplicationContext(), R.layout.item, null);通过个语句可能把item.xml文件创建为一个View对象,这个View就是所需要的item项。只要把这个View设置上Person对象的数据然后挂到ListView中即完成了任务。即view.findViewByID( ).setText,先找到这个View对象的组件,再把组件上设置好显示的内容即可。

ListView中添加item项的方式

通过ListView.setAdapter(ListAdapter adapter)方法添加适配器来完成添加item项的操作。

ListAdapter是一个接口,它有常用的三个子类:BaseAdpterSimpleAdapterSimpleCursorAdapter

方式一:BaseAdpter

在上图的getCount( )法中,想知道一共有多少个Count,用persons.size( )返回,这里的Count有多少个,那么getView方法就会执行多少次,调用一次getView方法就会创建一个Viewitem项),也就是说你有多少个Person对象就会调用多少次getView()方法,来创建多个Viewitem项),每创建一个View这个BaseAdapter对象会自动把这个View对象挂到ListView对象当中。每调用一次getView都会传一个Position参数,这个参数从0开始一直到小于getCount。也就是说position记录了每个View(item)ListView中的索引(位置),0代表第一个item项,1代表第二个item项。
注意:这个返回的View对象是根据item.xml创建的,也就是说这个View对象包括了item.xm文件中的布局属性和item.xml文件中的各个TextViewEditViewButton等标签节点,在把View返回之前,也就是挂到ListView之前,我们要通过findViewById查找到各个TextView,然后把TextView的文本改成Person对象里的数据,注意这个findViewByid方法必须使用的是View view = View.inflate(getApplicationContext(), R.layout.item, null); 这里的这个view.findViewById()方法
再注意:当调用TextView.setText( )时,如果我们要显示的是一个Integer对象值,不可以直接把Integer放进来,为如果这样的就不会直接显示这个Integer字符,而是去R文件中找有没有这个值为IntegerID,找不到就会报错。

当点击ListView中的某个item项时,会自动调用getItem( )方法,这时我们可以通过persons.get(position)方法来返回一个Person对象。

getItemId( )一般就返回posion即可,即返回ListViewitem项的索引

具体代码在10.SQLite项目中的:

res/layout/main.xml

res/layout/item.xml

com.itheima.sqlite包:MainActivityByBaseAdapter

方式二:SimpleAdpter

new SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) 

persons里的每个Person对象里的属性封装成一个Map,即map.put(name,person.getName)的形式转换,一个Person变成一个Map然后把所有的Map存到一个List中,这个List就是上面所需要的data参数。

Resourceitem.xml的引用(即R.layout.item),from,为Map里的各个key的值

to指定要resource参数中对应key的值保存到对应的R.id.tv_name中,to是一个数组,就是用来指定所有的R.id

具体代码在10.SQLite项目中的:com.itheima.sqlite包:MainActivityBySimpleAdapter

方式三:SimpleCursorAdpter

new SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to)

这里所需要的参数跟方式二中的类似,Cursor是一个保存了所有Person记录集的对象,也就是说相当于一条记录就是一个Person对象,在这条记录里有nameidbalance等字段。from就是Cursor对象里记录中的字段名
注意:这里要求的Cursor对象必须要有一个名为“ _id ”的字段,要不然就会报错。注意,只是说这个Cursor对象的记录中必须要有而已,并没有说数据库中一定要有这个“_id”字段,因为在执行查询语句时可以给id起一个别名为“_id”,但是在SQLite中有一个不成名的规定:数据库中的id字段一般名为“_id”,所以最好把数据库里的id直接起名为“_id”。

layout是一个item.xml文件的引用:R.layout.item

具体代码在10.SQLite项目中的:com.itheima.sqlite包:MainActivityBySimpleCursorAdapter

总结:

用第三种方式最方便,因为从数据库里查询数据调用SQLiterawQuery( )query( )方法返回的就是一个Cursor

如果数据不是从数据库查询来的,用第二种方式会比较方便一些。

ListView中的item项增加监听器

增加监听器: ListView. setOnItemClickListener(OnItemClickListener listener)

这里需要一个OnItemClickListener接口的子类

如果使用的是SimpleCursorAdpter

private class MyOnItemClickListener implements OnItemClickListener {

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

Cursor c = (Cursor) parent.getItemAtPosition(position); // 得到选中的条目上的Cursor, 这个Cursor已经被移动到选中的位置了

Person p = new Person(c.getInt(0), c.getString(1), c.getInt(2)); // 获取Cursor中的数据组装成对象

Toast.makeText(getApplicationContext(), p.toString(), 0).show(); // 弹出

}

}

在代码中选择AdapterView然后按Ctrl T可以查看AdapterView的类继承结果,可以看到的父类是View,它还有一个子类ListView,其实这个prarent参数就是Item项的容器,即ListView对象。

因为SimpleCursorAdpter生成的item项是通过Cursor对象生成的,所以parent.getItemAtPosition(position)返回的当然也就是一个Cursor对象。

view即所点的LinearLayoutItem项)

position为所点击的是第几个

id 一般这个值和position是一样的

具体代码在10.SQLite项目中的:com.itheima.sqlite包:MainActivityBySimpleCursorAdapter

如果使用的是SimpleAdpter

private class MyOnItemClickListener implements OnItemClickListener {

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

Map<String, Object> map = (Map) parent.getItemAtPosition(position); // 得到选中的条目上的Map

Person p = new Person((Integer)map.get("id"), (String)map.get("name"), (Integer)map.get("balance")); // 获取Map中的数据组装成对象

Toast.makeText(getApplicationContext(), p.getName(), 0).show(); // 弹出

}

}

因为SimpleAdpter生成的item项是通过Map对象生成的,所以parent.getItemAtPosition(position)返回的当然也就是一个Map对象。

具体代码在10.SQLite项目中的:com.itheima.sqlite包:MainActivityBySimpleAdapter

如果使用的是BaseAdpter

private class MyOnItemClickListener implements OnItemClickListener {

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

Person p = (Person) parent.getItemAtPosition(position); // 得到选中的条目上的Person

Toast.makeText(getApplicationContext(), p.toString(), 0).show(); // 弹出

}

}

parent.getItemAtPosition(position)的返回值是什么呢?Adpter接口中的getItem( )方法返回的对象,因为之前写的时候返回的是Person所以这里的返回值就是Person,其实使用SimpleAdpter时,内部的getItem()方法返回的就是Map,而使用SimpleCursorAdptergetItem()返回的是Cursor对象。

具体代码在10.SQLite项目中的:com.itheima.sqlite包:MainActivityByBaseAdapter

ArrayAdapater

ListView.setAdapter(new ArrayAdapter<String>(this, R.layout.me_item, R.id.fav_titleString[ ]{}));

这种方式适合只填充Item里的一个值,如上面只能给item项中的R.id.fav_title的文本框赋值。

内容提供者(ContentProvider

数据库文件默认是只能在本项目(应用)中被读写,在其他应用中是不能读写的,如果想给外面的应用读写,这时就可以用内容提供者。

作用就是共享数据,手机中的各种应用,基本上都是通过内容提供者把数据共享出来的。比如安装了一个QQ通讯录,能读到手机里的短信,那是因为手机的短信程序里有内容提供者,提供了短信的获取。

编写ContentProvider

AndroidManifest.xml文件的Application节点中配置:

     

在清单文件的<application>节点下进行配置,<provider>标签中需要指定nameauthorities属性

name为类名,包名从程序Package开始,以“.”开始

authorities:是访问Provider时的路径,要唯一

URI代表要操作的数据,由schemeauthoritespath三部分组成

content://cn.itcast.sqlite.provider/person

scheme:固定为content,代表访问内容提供者

authorites<provider>节点中的authorites属性

path:程序定义的路径,可根据业务逻辑定义

编写一个类继承ContentProvider,必然实现以下几个方法

至此一个简单的内容提供者就写好,在其他的应用中怎么访问这个内容提供者呢?

通过ContentResolver对象访问内容提供者

在其他应用中写一个测试类,然后:

public class TestProvider extends AndroidTestCase {

public void invokeProvider(){

ContentResolver resolver = getContext().getContentResolver();

Uri uri = Uri.parse("content://daizhenliang");

ContentValues values = new ContentValues();

resolver.insert(uri, values);

resolver.delete(uri, nullnull);

resolver.update(uri, nullnullnull);

resolver.query(uri, nullnullnullnull);

}

}

当执行resolver的增、删、改、查,就会自动调用相应"content://daizhenliang"这个提供者中的增删改查方法,并且这里的参数会原封不动的传过去

通过"content://daizhenliang/person"指定了要访问person表,如这时调用resolver.insert(uri, values)方法,则会自动调用ContentProvider中的public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 方法,这里的Uri就是resolver传过来的Uri,要进行查询操作,肯定要知道操作的是哪个表,这时就要对Uri进行解析,这时可以用UriMatcher工具类,代码如下:

UriMatche matcher = new UriMatcher(UriMatcher.NO_MATCH); // 匹配器

matcher.addURI("com.itheima.sqlite.provider", "person", 1); // 添加可以匹配的URI

matcher.addURI("com.itheima.sqlite.provider", "student", 2);

这样就设置好了一个匹配器,当调用matcher.match(uri)时,就会和刚刚配置的Uri进行匹配,如果匹配到了person,则此方法返回1,如果匹配到了student,则返回2,通过这个返回值我们就直到要操作的是哪个表了。

matcher.addURI("com.itheima.sqlite.provider""person/#", 2);//这里的#代表要匹配一个数字,但是这个数字还不知道是多少,就可以用这种试匹配。通过这种匹配我们就可以在查询的时候在URI中指定要查的条件(Id),URIContentResolver中传到ContentProvicer中时就要解析出这个ID,这时可以用:long id = ContentUris.parseId(uri); 这个工具可以方便地解析出URI中的id。 具体使用在:10.SQLite项目中:com.itheima.sqlite.provider包:SQLiteProvider文件中

关于ContentProvider中的getType()方法的使用:

UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

matcher.addURI("com.itheima.sqlite.provider", "person", 1); 不带id说明操作的是多条数据

matcher.addURI("com.itheima.sqlite.provider", "person/#", 2);  带id说明操作的是单条记录

public String getType(Uri uri) {

switch (matcher.match(uri)) {

case 2:

return "vnd.android.cursor.item/person"; item代表是单条记录

case 1:

return "vnd.android.cursor.dir/person";      dir代表是多条记录

default:

throw new IllegalArgumentException("无法匹配的URI: "   uri);

}

}

其实这个getType方法主要就是用来测试Uri有没有写错,就是说在进行增删改查之前,先测一下Uri对不对。

关于ContentProviderContentResolver进行增、删、改、查的使用具体代码在:

10.SQLite项目中:com.itheima.sqlite.provider包:SQLiteProvider

06.Test项目中:com.itheima.junit.test包:ProviderTest

总结:

使用ContentResolver进行数据库的增删改查的调用过程:

ContentObserver内容观察者

用于观察ContentProvider的变化。

ContentProvider的增、删、改、查是通过ContentResolver对象进行调用的,所以内容观察者是注册在ContentResolver对象中的,代码如下:

ContentResolver.registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)

uri:要观察的ContentProvideruri,如:content://com.itheima.sqlite.provider

notifyForDescendents:如果为trueuri中的后代也观察,如:content://com.itheima.sqlite.provider/person
content://com.itheima.sqlite.provider/person/#
content://com.itheima.sqlite.provider/student

observer:当观察者观察的Uri发生改变时就会自动执行observer对象的onChange()方法。

注:在ContentProvider的增、删、改、查操作后要加上:getContext().getContentResolver().notifyChange(uri, null);// 通知监听这个ContentProviderContentObserver,作用就是通知观察者说ContentProvider发生改变了。

观察者的项目没有修改过时不要发布多次,比如发布了3次,那么在Uri发生改变时,就会通知观察者3次,也就是说会执行3onChange()方法。如果观察者项目修改过然后发布1次就不会出现监听多次的情况。

练习使用观察者:

1、 写一个内容提供者项目

2、 写一个观察者项目,注册一个观察者观察1中的内容提供者,在观察者的onChange中输出一个语句。

3、 写一个测试项目,在这里用ContentResolver操作内容提供者,查看观察者的输出语句有没有执行

总结:

观察者是注册在ContentResovler对象上的

Content有一个getContentResolver()的方法获取ContentResovler对象。

所以只要在能拿到Content对象的类里就能注册内容观察者。

能获取Context的类:

ActivityApplicationService就是Context的子类

AndroidTestCase. mContext 、AndroidTestCase. Context()

ContentProvider. getContext( )

实例:监听手机中的短信

手机中的各种应用,基本上都是通过内容提供者把数据共享出来的。比如安装了一个QQ通讯录,能读到手机里的短信,那是因为手机的短信程序里有内容提供者,提供了短信的获取。所以就可以通过一个观察者来监听短信。

要想使用观察者监听手机中的短信,必须要知道authorites

查看短信程序的项目:

短信的发送相关界面是这个项目:data/data/ ,而短信的数据是在:data/data/ 打开mmssms.db数据库:

怎么知道短信程序的authorites呢?通过查它的源代码就可以知道,但是在SDK里是没有发短信的源代码的,在这个网站:https://github.com/android里有所有的安卓手机内置程序的源代码,而要下载这些源代码需要使用Git工具,这个工具的下载网站:http://code.google.com/p/msysgit/

注意:

GIT1.7.7安装后不能卸载,可以用其他版本覆盖后再卸载。

使用GIT时不要使用中文目录,否则GIT GUI会报错无法启动。删除C盘中.gitconfig文件可以解决。

1、 首先安装Git工具

2、 

3、 
这个界面就是要把网上的代码克隆下来的,这个代码的克隆地址是什么呢?打开 :https://github.com/android,搜telphonyprovider如下图:

下载到之后:

然后找到这个:

然后查看提供者的源码:

在源代码中查找:

使用观察者查数据的时候,

由于具体的查询代码是写在ContentProvider中的,所以我们在查的时候可以用查询结果Cursor.getColumnNames()查看都查到了哪些字段,然后再获取我们所需字段的值。

需要的字段如下:

注:有时候发一短信能收到多条通知,是因为我们监听了authorites的后代。

代码如下:

具体代码在:11.ContentObserver项目:MainActivity类中

读取手机联系人:

电话联系人的保存位置

可以看到这个联系人数据库在外部的应用是不能访问的,所以只能通过内容提供者来访问了

要使用内容提供者,首先要知道内容提供者的Uri,通过查看源代码就能知道:

先看清单文件,再看src源代码:

查看清单后就能知道主机地址:content://com.android.contacts/xxx  ,下一步就是要查xxx有哪些,查我们需要的,这时就要看源代码了(ContactsProvider2.java

查看.java中配置的uri信息,可以直接搜索 static {  这样就比较快:

1、 找到:matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS); 那么要获取raw_contact表就可以使用:content://com.android.contacts/raw_contact 这个URI

2、 找到:matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);   data

data表中的mimetype_id字段与mimetypes表中的_id进行了关联,所以在查到data表的Cursor后,这个Cursor里就已经保存有mimetype这个字段了,可以直接获取这个字段的值

获取联系人步骤:

1、 增加权限

标签: 数据库 Xml Android

实例下载地址

安卓-Junit-文件存储-XML解析方式-数据库-ListView-内容提供者-观察者 实例讲解

不能下载?内容有错? 点击这里报错 + 投诉 + 提问

好例子网口号:伸出你的我的手 — 分享

网友评论

发表评论

(您的评论需要经过审核才能显示)

查看所有0条评论>>

小贴士

感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。

  • 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
  • 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
  • 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
  • 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。

关于好例子网

本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明

;
报警