自定义ContentProvider

当多个应用程序共享同一数据时,我们可以给这个数据定义一个URI,然后把这个URI以接口的形式暴漏出去,之后其他应用程序对此数据进行增删改查时,只需要从当前上下文对象获得一个ContentResolver(内容解析器)传入相应的URI就可以了。数据库支持直接操作db文件,问什么还要用内容提供人?这是出于安全的考虑,内容提供者可以根据自己的意愿选择性的提供数据给客户端使用。

(一)、操作步骤:

1、编写一个类,让其继承自ContentProvider类;

2、实现ContentProvider类中所有的抽象方法;需要实现:onCreate() 、getType() 、query() 、insert() 、update()、delete() 等方法。

3、定义ContentProvider的Uri。这个Uri是ContentResolver对象执行CRUD操作时重要的参数;

4、使用UriMatcher对象映射Uri返回代码;

5、在AndroidMainfest.xml文件中使用<provider>标签注册ContentProvider。

备注:

ContentProvider暴露出来的数据和方法并不是给自身调用的,而是给其他应用程序来调用。其他应用程序通过其ContentResolver对象调用的query() 、insert() 、update()、delete() 等方法就是我们在这里暴露出来的ContentProvider类中的重写后的query() 、insert() 、update()、delete() 方法。


(二)、ContentProvider类中的六个抽象方法:

1、boolean  onCreate()

2、Uri  insert(Uri  uri, ContentValues  values)

3、int  delete(Uri  uri, String selection, String[]  selectionArgs)

4、int  update(Uri  uri, ContentValues values, String  selection, String[]  selectionArgs)

5、Cursor  query(Uri  uri, String[]  projection, String  selection, String[]  selectionArgs, String  sortOrder)

6、String  getType(Uri  uri)


(三)、ContentProvider类中六个抽象方法的说明:

1、onCreate() 初始化provider

2、query() 返回数据给调用者

3、insert() 插入新数据到ContentProvider

4、update() 更新ContentProvider已经存在的数据

5、delete() 从ContentProvider中删除数据

6、getType() 返回ContentProvider数据的Mime类型


(四)、在清单文件中声明注册ContentProvider:

<provider android:name=".MyWordsProvider"
android:authorities="com.steven.wordscontentprovider"
android:exported="true" />

//android:name  属性的值是:ContentProvider类的子类的完整路径;
//android:authorities 属性的值是:content:URI中authority部分。一般就是将name属性的值全小写。
//android:exported 属性是否允许其他应用调用。如果是false,则该ContentProvider不允许其他应用调用。

(五)、UriMatcher:
继承ContentProvider类后发现,ContentProvider类中只有一个onCreate()生命周期方法——当其他应用程序通过ContentResolver第一次访问ContentProvider时,onCreate()会被回调。
其他应用在通过ContentResolver对象执行CRUD操作时,都需要一个重要的参数Uri。为了能顺利提供这个Uri参数,Android系统提供了一个UriMatcher工具类。

UriMatcher工具类提供了两个方法:
1、void addURI(String authority , String path , int code) : 该方法用于向UriMatcher对象注册Uri。其中authority和path是Uri中的重要部分。而code代表该Uri对应的标示符。
2、int match(Uri uri) : 根据注册的Uri来判断指定的Uri对应的标示符。如果找不到匹配的标示符,该方法返回-1。
 private static UriMatcher matcher = null;
static {

// 定义一个Uri匹配器。将UriMatcher.NO_MATCH,即-1作为参数。
matcher = new UriMatcher(UriMatcher.NO_MATCH);
// 定义一组匹配规则
matcher.addURI(AUTHORITY, "words", 1);
matcher.addURI(AUTHORITY, "newwords", 2);
}
备注:
ContentProvider是单例模式的,当多个应用程序通过使用ContentResolver 来操作使用ContentProvider 提供的数据时,ContentResolver 调用的数据操作会委托给同一个ContentProvider 来处理。这样就能保证数据的一致性。

(六)下面通过一个实例来讲解自定义ContentProvider,需要创建两个工程,一个工程用来自定义ContentProvider,然后把uri在程序清单中暴漏出去,那么此工程就相当于一个服务器端,可以供其他的工程来访问其数据;另外一个工程相当于客户端,通过使用ContentResolver 来操作使用ContentProvider 提供的数据。在测试时要先启动服务端,再启动客服端。

1,自定义ContentProvider的工程:

①创建一个继承ContentProvider类:

package com.qf.mycontentprovide;

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;
import android.support.annotation.Nullable;

/**
 * 多个应用共用同一个数据库,数据库就是一个db文件
 * 数据库支持直接操作db文件,为什么还要用内容提供者?
 * 答:出于安全的考虑,内容提供者可以根据的自己的意愿选择性的提供数据给客户端使用。
 *
 * 服务器和客户端,安卓有jdbc的api,意味着安卓是可以直接访问数据库的,
 * 但是实际中我们使用接口访问远程服务器,这个接口提供给数据给我们的同时也是对远程数据库提供了保护,
 * 可以选择性的返回数据给客户端
 */
public class MyContentProvider extends ContentProvider{

    private SQLiteDatabase database;
    private UriMatcher uriMatcher;

    /**
     * 安装之后会执行,以后app启动不会执行
     * 并且应用没有启动的时候,其他应用也是可以访问自己提供的数据的
     * 和数据库的openhelper中的oncreate不一样,数据库是卸载再安装才会执行,
     * 而MyContentProvider只要重新安装就会执行
     */
    @Override
    public boolean onCreate() {
        DbHelper dbHelper = new DbHelper(getContext());
        database = dbHelper.getReadableDatabase();

        //获取到一个uri匹配对象
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        /**
         *  addURI是把authority和path进行拼接
         *  code是给path设置了一个id
         */
        uriMatcher.addURI("com.qf.mycontentprovide.MyContentProvider","person",1);
        uriMatcher.addURI("com.qf.mycontentprovide.MyContentProvider","teacher",2);
        return true;
    }

    /**
     * 操作数据库时,因为数据库有多张表,需要判断操作那张表,
     * 这时addURI的参数code就有了用武之地
     */
    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor cursor = null;
        int match = uriMatcher.match(uri);
        switch (match){
            //person表
            case 1:
                cursor = database.query("person", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            //teacher表
            case 2:
                cursor = database.query("teacher", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    /**
     * ContentValues里封装了一个HashMap<String, Object> mValues;
     * 和普通的map的区别:键只能为string
     * @param uri  客户端传过来的uri路径
     * @param values  客户端封装的数据传过来
     * @return
     */
    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        int match = uriMatcher.match(uri);
        long num = 0; //插入数据受影响的行数
        switch (match){
            case 1:
                num = database.insert("person", null, values);
                break;
            case 2:
                num = database.insert("teacher",null,values);
                break;
            default:
                break;
        }
        //把受影响的行数拼接到uri后面
        Uri uri1 = ContentUris.withAppendedId(uri, num);
        return uri1;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int match = uriMatcher.match(uri);
        int num = 0;
        switch (match){
            case 1:
                num = database.delete("person", selection, selectionArgs);
                break;
            case 2:
                num = database.delete("person", selection, selectionArgs);
                break;
        }
        return num;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        int match = uriMatcher.match(uri);
        int num = 0;
        switch (match){
            case 1:
                num = database.update("person", values, selection, selectionArgs);
                break;
            case 2:
                num = database.update("teacher",values,selection,selectionArgs);
                break;
        }
        return num;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

}


②创建一个数据库操作类,用来向外提供数据:

package com.qf.mycontentprovide;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * Created by Administrator on 2016/9/20.
 * 支持内容提供者向外提供数据的数据库
 */
public class DbHelper extends SQLiteOpenHelper {

    public DbHelper(Context context) {
        super(context, "Person.db", null, 2);
    }

    /**
     * 创建数据库时创建一张person表,并给表初始化一条数据
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table person(_id integer primary key autoincrement,name text,pass text)");
        db.execSQL("insert into person(name,pass)values('张飞','123')");
    }

    /**
     * 更新数据库时,再创建一张teacher表,并初始化一条数据
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("create table teacher(_id integer primary key autoincrement,name text,pass text)");
        db.execSQL("insert into person(name,pass)values('老张','12334')");
    }
}

③在清单文件中暴漏此内容提供者:

<span style="color:#464646;"><?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.qf.mycontentprovide">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- authorities:向外提供数据的接口,即uri,一般为包名+类名
             exported:是否支持其他应用访问自己的数据,设置为true  -->
        </span><span style="color:#ff0000;"><provider
            android:authorities="com.qf.mycontentprovide.MyContentProvider"
            android:name=".MyContentProvider"
            android:exported="true">
        </provider></span><span style="color:#464646;">
    </application>

</manifest></span>
由于MainActivity和activity_main.xml中不需要写代码,所以就不再给出。


2.再创建一个去共享内容提供者中的数据的工程:

①布局文件activity_main.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.client.MainActivity">

    <Button
        android:id="@+id/query_person"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="查询person表" />
    <Button
        android:id="@+id/query_teacher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="查询teacher表" />

    <Button
        android:id="@+id/insert_person"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="插入数据到person表" />
    <Button
        android:id="@+id/insert_teacher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="插入数据到teacher表" />

    <Button
        android:id="@+id/delete_person"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="删除person表中的数据" />
    <Button
        android:id="@+id/delete_teacher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="删除teacher表中的数据" />

    <Button
        android:id="@+id/updata_person"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="修改person表中的数据" />
    <Button
        android:id="@+id/updata_teacher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="修改teacher表中的数据" />

</LinearLayout>


②MainActivity中的代码:

package com.example.client;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ContentResolver contentResolver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化控件,并给控件设置监听
        findViewById(R.id.query_person).setOnClickListener(this);
        findViewById(R.id.query_teacher).setOnClickListener(this);
        findViewById(R.id.insert_person).setOnClickListener(this);
        findViewById(R.id.insert_teacher).setOnClickListener(this);
        findViewById(R.id.delete_person).setOnClickListener(this);
        findViewById(R.id.delete_teacher).setOnClickListener(this);
        findViewById(R.id.updata_person).setOnClickListener(this);
        findViewById(R.id.updata_teacher).setOnClickListener(this);

        //获得ContentResolver对象来操作数据库
        contentResolver = getContentResolver();
    }

    @Override
    public void onClick(View v) {
        //person表的uri
        Uri personUri = Uri.parse("content://com.qf.mycontentprovide.MyContentProvider/person");
        //teacher表的uri
        Uri teacherUri = Uri.parse("content://com.qf.mycontentprovide.MyContentProvider/teacher");
        switch (v.getId()){
            //查询person表
            case R.id.query_person:
                Cursor cursor = contentResolver.query(personUri, new String[]{"name", "pass"}, null, null, null,null);
                while(cursor.moveToNext()){
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    String pass = cursor.getString(cursor.getColumnIndex("pass"));
                    Log.e("name",name);
                    Log.e("pass",pass);
                }
                break;

            //查询teacher表
            case R.id.query_teacher:
                Cursor cursor2 = contentResolver.query(teacherUri, new String[]{"name", "pass"}, null, null, null,null);
                while(cursor2.moveToNext()){
                    String name = cursor2.getString(cursor2.getColumnIndex("name"));
                    String pass = cursor2.getString(cursor2.getColumnIndex("pass"));
                    Log.e("name",name);
                    Log.e("pass",pass);
                }
                break;

            case R.id.insert_person:
                ContentValues values = new ContentValues();
                values.put("name","关羽");
                values.put("pass","456");
                Uri insert = contentResolver.insert(personUri, values);
                long num = ContentUris.parseId(insert);
                Log.e("num",num + "");
                break;

            case R.id.insert_teacher:
                ContentValues values2 = new ContentValues();
                values2.put("name","曹操");
                values2.put("pass","321");
                Uri insert2 = contentResolver.insert(teacherUri, values2);
                long num2 = ContentUris.parseId(insert2);
                Log.e("num",num2 + "");
                break;

            case R.id.delete_person:
                int num3 = contentResolver.delete(personUri, "name = ?", new String[]{"张飞"});
                Log.e("num3",num3 + "");
                break;

            case R.id.delete_teacher:
                int num4 = contentResolver.delete(teacherUri, "name = ?", new String[]{"老张"});
                Log.e("num4",num4 + "");
                break;

            case R.id.updata_person:
                ContentValues values3 = new ContentValues();
                values3.put("name","关羽");
                values3.put("pass","789");
                int num5 = contentResolver.update(personUri, values3, null, null);
                Log.e("num5",num5 + "");
                break;

            case R.id.updata_teacher:
                ContentValues values4 = new ContentValues();
                values4.put("name","曹操");
                values4.put("pass","246");
                int num6 = contentResolver.update(personUri, values4, null, null);
                Log.e("num6",num6 + "");
                break;
        }
    }
}









  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值