文章目录
- 目标
- 一、内容提供者概述
- 目标
- 数据模型
- Uri
- 二、创建内容提供者
- 目标
- 创建内容提供者的步骤
- 三、访问其他应用程序
- 目标
- 3.1 查询其他程序数据
- 3.2 UriMatcher类
- 3.3 实战演练—读取手机通讯录
- 四、内容观察者
- 目标
- 4.1 什么是内容观察者
- **1.** **创建内容观察者**
- **2.** **注册内容观察者**
- **3. 取消内容观察者**
- 4.2 实战演练—监测数据变化
目标
- 掌握
内容提供者
的创建方式,能够独立完成创建内容提供者 - 掌握使用内容提供者访问其他应用程序的步骤,能够实现读取手机通讯录的功能
- 掌握内容观察者的使用方式,能够使用内容观察者观察其他程序的数据变化
在第5章数据存储中学习了Android数据持久化技术,包括文件存储、SharedPreferences存储以及数据库存储,这些持久化技术所保存的数据都只能在当前应用程序中访问。但在Android开发中,有时也会访问其他应用程序的数据。为了实现这种跨程序共享数据的功能,Android系统提供了一个组件ContentProvider(内容提供者)。本章将针对内容提供者进行详细地讲解。
一、内容提供者概述
目标
- 熟悉内容提供者,能够归纳内容提供者的工作原理
内容提供者(ContentProvider)是Android系统四大组件之一,它是不同应用程序之间进行数据共享的标准API,通过ContentResolver类可以访问ContentProvider中共享的数据。ContentProvider的工作原理如下:
数据模型
ContentProvider
使用基于数据库模型的简单表格来提供需要共享的数据,在该表格中,每一行表示一条记录,而每一列代表特定类型和含义的数据,并且其中每一条数据记录都包含一个名为“_ID”的字段类标识每条数据。
Uri
ContentResolver提供一系列增删改查的方法对数据进行操作,并且这些方法以Uri的形式对外提供数据。Uri为内容提供者中的数据建立了唯一标识符。它主要由三部分组成,scheme
、authorities
和path
。
二、创建内容提供者
目标
- 掌握内容提供者的创建方式,能够独立完成创建内容提供者
创建内容提供者的步骤
(1)选中程序包名右击选择【New】->【Other】->【Content Provider】选项
(2)输入内容提供者的Class Name(类名称)和URI Authorities(唯一标识,通常使用包名)
(3)点击“Finish”按钮完成创建
内容提供者创建完成后,Android Studio会自动在AndroidManifest.xml中对内容提供者进行注册。
<application ......>
......
<provider
android:name=".MyContentProvider"
android:authorities="cn.itcast.mycontentprovider"
android:enabled="true"
android:exported="true" >
</provider>
</application>
三、访问其他应用程序
目标
- 掌握使用内容提供者访问其他应用程序的步骤,能够实现读取手机通讯录的功能
3.1 查询其他程序数据
通过ContentProvider查询其他程序数据的具体步骤如下:
- 通过parse()方法解析Uri
Uri uri = Uri.parse("content://cn.itcast.mycontentprovider/person");
- 通过query()方法查询数据
//获取ContentResolver对象
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);
- 通过while()循环语句遍历查询到的数据
while (cursor.moveToNext()) {
String address = cursor.getString(0);
long date = cursor.getLong(1);
int type = cursor.getInt(2);
}
cursor.close(); //关闭cursor
3.2 UriMatcher类
如果一个ContentProvider中含有多个数据源(比如多个表)时,就需要对不同的Uri进行区分,此时可以用UriMatcher类对Uri进行匹配,匹配步骤如下:
1. 初始化UriMatcher类
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
2.注册需要的Uri
//第一个参数:Uri的authority部分
//第二个参数:Uri的path部分
//第三个参数:Uri匹配成功后返回的匹配码
matcher.addURI("cn.itcast.contentprovider", "people", PEOPLE);
matcher.addURI("cn.itcast.contentprovider", "person/#", PEOPLE_ID);
3. 与已经注册的Uri进行匹配
Uri uri = Uri.parse("content://" + "cn.itcast.contentprovider" + "/people");
int match = matcher.match(uri);
switch (match){
case PEOPLE:
//匹配成功后做的相关操作
case PEOPLE_ID:
//匹配成功后做的相关操作
default:
return null;
}
3.3 实战演练—读取手机通讯录
本节我们会通过一个读取手机通讯录的案例来演示如何使用ContentResolver操作Android设备的通讯录中暴露的数据。本案例的界面效果如下图所示。
放置界面控件 res\layout\activity_contact.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#eaeaea"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#4889f4"
android:gravity="center"
android:text="通讯录"
android:textColor="@android:color/white"
android:textSize="20sp"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_contact"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp" />
</LinearLayout>
搭建列表条目 res\layout\contact_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@drawable/item_bg"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:id="@+id/iv_photo"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/contact_photo" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginLeft="8dp"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:textColor="@android:color/darker_gray"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
封装实体类 contacts\ContactInfo.java
package cn.itcast.contacts;
public class ContactInfo {
private String contactName; //联系人名称
private String phoneNumber; //电话号码
public String getContactName() {
return contactName;
}
public void setContactName(String contactName) {
this.contactName = contactName;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
编写列表适配器 contacts\ContactAdapter.java
package cn.itcast.contacts;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter
.MyViewHolder> {
private Context mContext;
private List<ContactInfo> contactInfoList;
public ContactAdapter(Context context, List<ContactInfo> contactInfoList) {
this.mContext = context;
this.contactInfoList = contactInfoList;
}
// inflate()加载布局文件contact_item.xml
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(
LayoutInflater.from(mContext).inflate(
R.layout.contact_item, parent, false));
return holder;
}
//setText()将传递来的数据绑定到界面控件上
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.tv_name.setText(contactInfoList.get(position).getContactName());
holder.tv_phone.setText(contactInfoList.get(position).getPhoneNumber());
}
// 获取contactInfoList中数据的总条数
@Override
public int getItemCount() {
return contactInfoList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv_name, tv_phone;
ImageView iv_photo;
// findViewById()来获取界面上的控件
public MyViewHolder(View view) {
super(view);
tv_name = view.findViewById(R.id.tv_name);
tv_phone = view.findViewById(R.id.tv_phone);
iv_photo = view.findViewById(R.id.iv_photo);
}
}
}
显示界面数据 contacts\ContactActivity.java
package cn.itcast.contacts;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Build;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class ContactActivity extends AppCompatActivity {
private ContactAdapter adapter;
private RecyclerView rv_contact;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact);
init();
}
// 5.将获取的数据显示到通讯录界面上
private void setData() {
// getContacts()用来获取手机通讯录中的数据
List<ContactInfo> contactInfos = getContacts();
// 创建数据适配器adapter
adapter = new ContactAdapter(ContactActivity.this, contactInfos);
// 将adapter对象设置到列表控件rv_contact上
rv_contact.setAdapter(adapter);
}
// 4.获取手机通讯录的数据
public List<ContactInfo> getContacts() {
List<ContactInfo> contactInfos = new ArrayList<>();
// 获取通讯录数据,并将其存放到cursor对象中
Cursor cursor = getContentResolver().query(ContactsContract.
Contacts.CONTENT_URI, null, null, null, null);
if (contactInfos != null) contactInfos.clear(); //清除集合中的数据
// 获取联系人名称和电话号码,并将其设置到contactInfo类的对象中
while (cursor.moveToNext()) {
String id = cursor.getString(
cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.
Contacts.DISPLAY_NAME));
int isHas = Integer.parseInt(cursor.getString(cursor.getColumnIndex(
ContactsContract.Contacts.HAS_PHONE_NUMBER)));
if (isHas > 0) {
Cursor c = getContentResolver().query(ContactsContract.
CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +
" = " + id, null, null);
// 将contactInfo类的对象添加到集合contactInfos里
while (c.moveToNext()) {
ContactInfo info = new ContactInfo();
info.setContactName(name);
String number = c.getString(c.getColumnIndex(ContactsContract.
CommonDataKinds.Phone.NUMBER)).trim();
number = number.replace(" ", "");
number = number.replace("-", "");
info.setPhoneNumber(number);
contactInfos.add(info);
}
c.close();
}
}
cursor.close();
return contactInfos;
}
// 1.初始化界面控件
private void init() {
// 获取列表控件rv_contact
rv_contact = findViewById(R.id.rv_contact);
// 设置列表的方向为垂直方向
rv_contact.setLayoutManager(new LinearLayoutManager(this));
// 申请读取手机通讯录的权限
getPermissions();
}
String[] permissionList;
// 2.申请读取手机通讯录的权限
public void getPermissions() {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
permissionList = new String[]{"android.permission.READ_CONTACTS"};
ArrayList<String> list = new ArrayList<String>();
// 循环判断 permissionList 所需权限中有哪个尚未被授权,并将它加入到 list 中
for (int i = 0; i < permissionList.length; i++) {
if (ActivityCompat.checkSelfPermission(this, permissionList[i])
!= PackageManager.PERMISSION_GRANTED)
list.add(permissionList[i]);
}
// 存在未被授权的的权限,因此调用requestPermissions()方法来申请这些权限
if (list.size() > 0) {
ActivityCompat.requestPermissions(this,
list.toArray(new String[list.size()]), 1);
} else {
setData();//后续创建该方法
}
} else {
setData(); //后续创建该方法
}
}
// 3.获取申请通讯录权限信息是否成功的返回信息
// requestCode 请求码
// permissions 系统的权限数组
// grantResults 请求权限的状态
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
for (int i = 0; i < permissions.length; i++) {
// 判断返回的权限数组permissions是否包含 读取通讯录的权限: "android.permission.READ_CONTACTS"
// 并且该权限的状态值grantResults是否为 允许读取Android设备的通讯录: PackageManager.PERMISSION_GRANTED
if (permissions[i].equals("android.permission.READ_CONTACTS")
&& grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "读取通讯录权限申请成功",
Toast.LENGTH_SHORT).show();
setData();//后续创建该方法
} else {
Toast.makeText(this, "读取通讯录权限申请失败",
Toast.LENGTH_SHORT).show();
}
}
}
}
}
四、内容观察者
目标
- 掌握内容观察者的使用方式,能够使用内容观察者观察其他程序的数据变化
4.1 什么是内容观察者
内容观察者(ContentObserver)用于观察指定Uri所代表的数据的变化,当ContentObserver观察到指定Uri代表的数据发生变化时,就会触发onChange()方法,此时在onChange()方法中使用ContentResovler可以查询到变化的数据。
要使用ContentObserver观察数据变化,就必须在ContentProvider中调用ContentResolver的notifyChange()方法。
通过ContentObserver中的onChange()方法观察特定的Uri代表的数据的具体步骤如下:
1. 创建内容观察者
private class MyObserver extends ContentObserver{ //创建内容观察者
public MyObserver(Handler handler) {
super(handler);
}
// 当观察到Uri代表的数据发生变化时调用此方法,并在该方法中处理相关逻辑
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}
}
2. 注册内容观察者
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://aaa.bbb.ccc");
//注册内容观察者
//第一个参数uri 内容提供者的Uri
//第二个参数true 表示可以匹配Uri派生的其他Uri,false时只匹配当前提供的Uri
//第三个参数new MyObserver(new Handler()) 表示创建的内容观察者
resolver.registerContentObserver(uri, true, new MyObserver(new Handler()));
注册内容观察者的方法原型为:
public final void registerContentObserver(Uri uri,
boolean notifyForDescendents, ContentObserver observer)
功能:为指定的Uri注册一个ContentObserver派生类实例,当指定的Uri发生改变时,回调该实例对象去处理。
参数1:uri
需要观察的Uri
参数2:notifyForDescendents
false表示只匹配该Uri true表示可以同时匹配其派生的Uri
参数3:observer
创建的内容观察者对象
3. 取消内容观察者
@Override
protected void onDestroy() {
super.onDestroy();
// 取消注册的内容观察者
getContentResolver().unregisterContentObserver(new MyObserver(new Handler()));
}
注意:
在内容观察者监听的ContentProvider中,重写的insert()方法、delete()方法、update()方法中会调用ContentResolver的notifyChange()方法。
notifyChange(uri,null):用来通知所有注册在该Uri上的监听者,该ContentProvider共享的数据发生了变化
- 第一个参数: 表示Uri
- 第二个参数: 表示内容观察者,null表示该ContentProvider共享的数据发生了变化
4.2 实战演练—监测数据变化
本节就通过检测数据变化的案例来讲解如何使用内容观察者。本案例的界面效果如下图所示。
放置界面控件 res\layout\activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
android:orientation="vertical">
<Button
android:id="@+id/btn_insert"
android:layout_width="120dp"
android:layout_height="40dp"
android:layout_marginLeft="40dp"
android:layout_marginTop="30dp"
android:background="@drawable/btn_bg"
android:text="添加"
android:textColor="#006000"
android:textSize="20dp" />
<Button
android:id="@+id/btn_update"
android:layout_width="120dp"
android:layout_height="40dp"
android:layout_marginLeft="80dp"
android:layout_marginTop="30dp"
android:background="@drawable/btn_bg"
android:text="更新"
android:textColor="#006000"
android:textSize="20dp" />
<Button
android:id="@+id/btn_delete"
android:layout_width="120dp"
android:layout_height="40dp"
android:layout_marginLeft="120dp"
android:layout_marginTop="30dp"
android:background="@drawable/btn_bg"
android:text="删除"
android:textColor="#006000"
android:textSize="20dp" />
<Button
android:id="@+id/btn_select"
android:layout_width="120dp"
android:layout_height="40dp"
android:layout_marginLeft="160dp"
android:layout_marginTop="30dp"
android:background="@drawable/btn_bg"
android:text="查询"
android:textColor="#006000"
android:textSize="20dp" />
</LinearLayout>
创建数据库 contentobserverdb\PersonDBOpenHelper.java
package cn.itcast.contentobserverdb;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class PersonDBOpenHelper extends SQLiteOpenHelper {
//构造方法,调用该方法创建一个person.db数据库
public PersonDBOpenHelper(Context context) {
// context 上下文
// "person.db" 数据库的名称
// null 数据库的查询结果集
// 1 数据库的版本号
// 当调用super()方法时,程序会创建一个名为person的数据库
super(context, "person.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建该数据库的同时新建一个info表,表中有_id,name这两个字段
db.execSQL("create table info (_id integer primary key autoincrement, name varchar(20))");
}
// 当数据库版本升级时会回调该方法,在该方法中对数据库进行升级
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
创建内容提供者
package cn.itcast.contentobserverdb;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
//实现对数据库中数据进行操作的功能
public class PersonProvider extends ContentProvider {
//定义一个uri路径的匹配器,如果路径匹配不成功返回-1
private static UriMatcher mUriMatcher = new UriMatcher(-1);
private static final int SUCCESS = 1; //匹配路径成功时的返回码
private PersonDBOpenHelper helper; //数据库操作类的对象
//添加路径匹配器的规则,该静态代码块在程序启动时执行,且只执行一次。
static {
mUriMatcher.addURI("cn.itcast.contentobserverdb", "info", SUCCESS);
}
@Override
public boolean onCreate() { //当内容提供者被创建时调用
helper = new PersonDBOpenHelper(getContext());
return false;
}
/**
* 查询数据操作
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//匹配查询的Uri路径,若成功则返回SUCCESS,失败则抛出不合法的参数异常:IllegalArgumentException
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getReadableDatabase();
// 查询info表的信息并返回
return db.query("info", projection, selection, selectionArgs,
null, null, sortOrder);
} else {
throw new IllegalArgumentException("路径不正确,无法查询数据!");
}
}
/**
* 添加数据操作
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getReadableDatabase();
long rowId = db.insert("info", null, values);
if (rowId > 0) {
// rowId > 0表示数据添加成功,通过withAppendedId()方法重新构建一个Uri
// uri 内容提供者PersonProvider的Uri
// rowId 添加的数据在数据库表中的行id
Uri insertedUri = ContentUris.withAppendedId(uri, rowId);
//notifyChange()方法通知注册在该程序Uri上的内容观察者有数据发生变化了
getContext().getContentResolver().notifyChange(insertedUri, null);
return insertedUri;
}
db.close();
return uri;
} else {
throw new IllegalArgumentException("路径不正确,无法插入数据!");
}
}
/**
* 删除数据操作
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getWritableDatabase();
int count = db.delete("info", selection, selectionArgs);
//提示数据库的内容变化了
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
db.close();
return count;
} else {
throw new IllegalArgumentException("路径不正确,无法随便删除数据!");
}
}
/**
* 更新数据操作
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getWritableDatabase();
int count = db.update("info", values, selection, selectionArgs);
//提示数据库的内容变化了
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
db.close();
return count;
} else {
throw new IllegalArgumentException("路径不正确,无法更新数据!");
}
}
@Override
public String getType(Uri uri) {
return null;
}
}
编写界面交互代码
package cn.itcast.contentobserverdb;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements
View.OnClickListener {
private ContentResolver resolver;
private Uri uri;
private ContentValues values;
private Button btnInsert;
private Button btnUpdate;
private Button btnDelete;
private Button btnSelect;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView(); //初始化界面
createDB(); //创建数据库
}
// 初始化界面控件并设置控件的点击监听事件
private void initView() {
btnInsert = findViewById(R.id.btn_insert);
btnUpdate = findViewById(R.id.btn_update);
btnDelete = findViewById(R.id.btn_delete);
btnSelect = findViewById(R.id.btn_select);
btnInsert.setOnClickListener(this);
btnUpdate.setOnClickListener(this);
btnDelete.setOnClickListener(this);
btnSelect.setOnClickListener(this);
}
private void createDB() {
//创建helper对象的同时,也创建了person.db数据库
PersonDBOpenHelper helper = new PersonDBOpenHelper(this);
// 获取数据库类SQLiteDatabase的对象db
SQLiteDatabase db = helper.getWritableDatabase();
// 向数据库info表中插入三条数据
for (int i = 0; i < 3; i++) {
ContentValues values = new ContentValues();
values.put("name", "itcast" + i);
db.insert("info", null, values);
}
db.close();
}
@Override
public void onClick(View v) {
//得到一个内容提供者的解析对象
resolver = getContentResolver();
//获取一个Uri路径
uri = Uri.parse("content://cn.itcast.contentobserverdb/info");
//新建一个ContentValues对象,该对象以key-values的形式来添加数据到数据库表中
values = new ContentValues();
switch (v.getId()) {
case R.id.btn_insert:
Random random = new Random();
// 将要添加的数据添加到values对象中
values.put("name", "add_itcast" + random.nextInt(10));
// 将values对象里的数据添加到数据库中
// uri 内容提供者PersonProvider的Uri
// values 要添加的数据
Uri newuri = resolver.insert(uri, values);
// Toast和Log用来提示添加成功的信息
Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();
Log.i("数据库应用", "添加");
break;
case R.id.btn_delete:
//返回删除数据的条目数
int deleteCount = resolver.delete(uri, "name=?",
new String[]{"itcast0"});
Toast.makeText(this, "成功删除了" + deleteCount + "行",
Toast.LENGTH_SHORT).show();
Log.i("数据库应用", "删除");
break;
case R.id.btn_select:
List<Map<String, String>> data = new ArrayList<Map<String, String>>();
//返回查询结果,是一个指向结果集的游标
Cursor cursor = resolver.query(uri, new String[]{"_id", "name"},
null, null, null);
//遍历结果集中的数据,将每一条遍历的结果存储在一个List的集合中
while (cursor.moveToNext()) {
Map<String, String> map = new HashMap<String, String>();
map.put("_id", cursor.getString(0));
map.put("name", cursor.getString(1));
data.add(map);
}
//关闭游标,释放资源
cursor.close();
Log.i("数据库应用", "查询结果:" + data.toString());
break;
case R.id.btn_update:
//将数据库info表中name为itcast1的这条记录更改为name是update_itcast
values.put("name", "update_itcast");
int updateCount = resolver.update(uri, values, "name=?",
new String[]{"itcast1"});
Toast.makeText(this, "成功更新了" + updateCount + "行",
Toast.LENGTH_SHORT).show();
Log.i("数据库应用", "更新");
break;
}
}
}
至此,操作数据库的程序就创建完成了,接下来创建监测数据库变化的程序,具体步骤如下:
monitordata\MainActivity.java
package cn.itcast.monitordata;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 该uri路径指向数据库应用中的数据库info表
Uri uri = Uri.parse("content://cn.itcast.contentobserverdb/info");
//注册内容观察者,
// 参数uri指向要监测的数据库info表,
//参数true定义了监测的范围,
// 最后一个参数是一个内容观察者对象
getContentResolver().registerContentObserver(uri, true,
new MyObserver(new Handler()));
}
private class MyObserver extends ContentObserver {
public MyObserver(Handler handler) {//handler 是一个消息处理器。
super(handler);
}
@Override
//当info表中的数据发生变化时则执行该方法
public void onChange(boolean selfChange) {
Log.i("监测数据变化", "有人动了你的数据库!");
super.onChange(selfChange);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册内容观察者
getContentResolver().unregisterContentObserver(new MyObserver(
new Handler()));
}
}
注意:
内容观察者的目的是观察特定Uri引起的数据库的变化,继而做一些相应的处理,这种方式效率高内存消耗少,需要初学者掌握。