Android——使用ContentProvider共享数据

实验名称: 使用ContentProvider共享数据                                                

实验目的:

(1)能使用ContentProvider共享数据

(2)能使用内容观察者观察其他程序的数据变化

实验内容及原理:

设计一个监测数据库的工程,要求:

  1. 能够操作数据库的数据 
  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可以查询到变化的数据。

猜你喜欢

转载自blog.csdn.net/Liwo4418/article/details/128093763