CH6-内容提供者

文章目录

  • 目标
  • 一、内容提供者概述
    • 目标
    • 数据模型
    • 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的工作原理如下:

image-20220302170715651

数据模型

ContentProvider使用基于数据库模型的简单表格来提供需要共享的数据,在该表格中,每一行表示一条记录,而每一列代表特定类型和含义的数据,并且其中每一条数据记录都包含一个名为“_ID”的字段类标识每条数据。

image-20220302170841417

Uri

ContentResolver提供一系列增删改查的方法对数据进行操作,并且这些方法以Uri的形式对外提供数据。Uri为内容提供者中的数据建立了唯一标识符。它主要由三部分组成,schemeauthoritiespath

image-20220302170953285

二、创建内容提供者

目标

  • 掌握内容提供者的创建方式,能够独立完成创建内容提供者

创建内容提供者的步骤

(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查询其他程序数据的具体步骤如下:

  1. 通过parse()方法解析Uri
Uri uri = Uri.parse("content://cn.itcast.mycontentprovider/person");
  1. 通过query()方法查询数据
//获取ContentResolver对象
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(Uri uri, String[] projection, String selection,                                                String[] selectionArgs, String sortOrder);
  1. 通过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设备的通讯录中暴露的数据。本案例的界面效果如下图所示。

image-20220302171841654

image-20220302171931401

image-20220302171943774

放置界面控件 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()方法

image-20220302172217451

通过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 实战演练—监测数据变化

本节就通过检测数据变化的案例来讲解如何使用内容观察者。本案例的界面效果如下图所示。

image-20220302172925609

放置界面控件 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;
        }
    }
}

至此,操作数据库的程序就创建完成了,接下来创建监测数据库变化的程序,具体步骤如下:

image-20220302173017380

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引起的数据库的变化,继而做一些相应的处理,这种方式效率高内存消耗少,需要初学者掌握。

  • 4
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绿洲213

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值