实验名称: 使用ContentProvider共享数据
实验目的:
(1)能使用ContentProvider共享数据
(2)能使用内容观察者观察其他程序的数据变化
实验内容及原理:
设计一个监测数据库的工程,要求:
- 能够操作数据库的数据
- 数据库发生变化时立即响应
创建工程,设计界面和功能,完成项目的调试,并将程序的代码和运行结果记录下来。
实验设备及实验步骤:
实验设备:Windows+Android Studio
在配置中可能遇到的问题
1.不是右键New-Other-Content Provider来生成内容观察者,导致AndroidManifest.xml没有注册。
错误提示:INSTALL_FAILED_CONFLICTING_PROVIDER
2.Android java.lang.SecurityException: Failed to find provider异常说明
这是因为安卓8.0以上有了权限管理,本代码比较老,我还没有解决,可以参考一下,如果用8.0以下的版本应该可以运行。(1条消息) Android java.lang.SecurityException: Failed to find provider异常说明_hellopeng1的博客-CSDN博客_failed to find provider
实验步骤:
1.创建ContentObserverDB程序
创建名为ContentObserverDB程序,指定包名为cn.itcast.contenobserverdb。
2.导入界面图片
设置背景图片。背景可自己在drawable修改
3.放置按钮控件
放置4个Button控件,分别显示“添加”、“更新”、“删除”、“查询”。背景可自己在drawable修改
<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>
4.创建数据库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) {
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) {
}
}
5.创建内容提供者PersonProvider.java
需要右键选择New-Other-Content Provider,即在AndroidManifest.xml注册好,代码如下。
package cn.itcast.contentobserverdb;
//省略导包
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路径
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getReadableDatabase();
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) {
Uri insertedUri = ContentUris.withAppendedId(uri, rowId);
//提示数据库的内容变化了
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;
}
}
6.编写界面代码MainActivity.java
package cn.itcast.contentobserverdb;
//省略导包
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() {
//创建数据库并向info表中添加3条数据
PersonDBOpenHelper helper = new PersonDBOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
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.put("name", "add_itcast" + random.nextInt(10));
Uri newuri = resolver.insert(uri, values);
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;
}
}
}
7.创建MonitorData程序
新建一个工程,创建一个名为MonitorData的程序,不用设置其界面。代码如下。
package cn.itcast.monitordata;
//省略导包
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()));
}
实验结果与分析:
程序界面运行结果:
访问数据库info数据:
响应结果:
内容观察者响应结果:
问题及思考:
简述内容观察者的工作原理。
内容观察者(ContentObserver)是用来观察指定Uri所代表的数据。当ContentObserver观察到指定Uri代表的数据发生变化时,就会触发ContentObserver的onChange()方法。此时在onChange()方法里使用ContentResovler可以查询到变化的数据。