自定义ContentProvider以及ContentObserver的使用完整详细示例

示例说明:

该示例中一共包含两个工程。其中一个工程完成了自定义ContentProvider,另外一个工程用于测试该自定义ContentProvider且在该工程中使用了ContentObserver监听自定义ContentProvider的数据变化

以下代码为工程TestContentProvider

ContentProviderTest如下:

package cn.testcontentprovider;
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;
/**
 * Demo描述:
 * 自定义ContentProvider的实现
 * ContentProvider主要用于在不同的应用程序之间共享数据,这也是官方推荐的方式.
 * 
 * 注意事项:
 * 1 在AndroidManifest.xml中注册ContentProvider时的属性
 *   android:exported=true表示允许其他应用访问.
 * 2 注意*和#这两个符号在Uri中的作用
 *   其中*表示匹配任意长度的字符
 *   其中#表示匹配任意长度的数据
 *   所以:
 *   一个能匹配所有表的Uri可以写成:
 *   content://cn.bs.testcontentprovider/*
 *   一个能匹配person表中任意一行的Uri可以写成:
 *   content://cn.bs.testcontentprovider/person/#
 *   
 */
public class ContentProviderTest extends ContentProvider {
    private SQLiteDatabaseOpenHelper mSQLiteDatabaseOpenHelper;
    private final static String  AUTHORITY=cn.bs.testcontentprovider;
    private  static UriMatcher mUriMatcher;
    private static final int PERSON_DIR = 0;
    private static final int PERSON = 1;
     
    /**
     * 利用静态代码块初始化UriMatcher
     * 在UriMatcher中包含了多个Uri,每个Uri代表一种操作
     * 当调用UriMatcher.match(Uri uri)方法时就会返回该uri对应的code;
     * 比如此处的PERSONS和PERSON
     */
    static {
        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        // 该URI表示返回所有的person,其中PERSONS为该特定Uri的标识码
        mUriMatcher.addURI(AUTHORITY, person, PERSON_DIR);
        // 该URI表示返回某一个person,其中PERSON为该特定Uri的标识码
        mUriMatcher.addURI(AUTHORITY, person/#, PERSON);
    }
     
     
    /**
     * 在自定义ContentProvider中必须覆写getType(Uri uri)方法.
     * 该方法用于获取Uri对象所对应的MIME类型.
     * 
     * 一个Uri对应的MIME字符串遵守以下三点:
     * 1  必须以vnd开头
     * 2  如果该Uri对应的数据可能包含多条记录,那么返回字符串应该以vnd.android.cursor.dir/开头
     * 3  如果该Uri对应的数据只包含一条记录,那么返回字符串应该以vnd.android.cursor.item/开头
     */
    @Override
    public String getType(Uri uri) {
        switch (mUriMatcher.match(uri)) {
        case PERSON_DIR:
            return vnd.android.cursor.dir/+AUTHORITY+.persons;
        case PERSON:
            return vnd.android.cursor.item/+AUTHORITY+.person;
        default:
            throw new IllegalArgumentException(unknown uri+uri.toString());
        }
    }   
 
     
    @Override
    public boolean onCreate() {
        mSQLiteDatabaseOpenHelper=new SQLiteDatabaseOpenHelper(getContext());
        return true;
    }
     
 
    /**
     * 插入操作:
     * 插入操作只有一种可能:向一张表中插入
     * 返回结果为新增记录对应的Uri
     * 方法db.insert()返回结果为新增记录对应的主键值
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
        switch (mUriMatcher.match(uri)) {
        case PERSON_DIR:
            long newId = db.insert(person, name,phone,salary, values);
            //向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应 
            getContext().getContentResolver().notifyChange(uri, null);  
            return ContentUris.withAppendedId(uri, newId);
        default:
            throw new IllegalArgumentException(unknown uri + uri.toString());
        }
    }
     
    /**
     * 更新操作:
     * 更新操作有两种可能:更新一张表或者更新某条数据
     * 在更新某条数据时原理类似于查询某条数据,见下.
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {
        SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
        int updatedNum = 0;
        switch (mUriMatcher.match(uri)) {
        // 更新表
        case PERSON_DIR:
            updatedNum = db.update(person, values, selection, selectionArgs);
            break;
        // 按照id更新某条数据
        case PERSON:
            long id = ContentUris.parseId(uri);
            String where = personid= + id;
            if (selection != null && !.equals(selection.trim())) {
                where = selection +  and  + where;
            }
            updatedNum = db.update(person, values, where, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException(unknown uri + uri.toString());
        }
        //向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应 
        getContext().getContentResolver().notifyChange(uri, null);  
        return updatedNum;
    }
     
    /**
     * 删除操作:
     * 删除操作有两种可能:删除一张表或者删除某条数据
     * 在删除某条数据时原理类似于查询某条数据,见下.
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
        int deletedNum = 0;
        switch (mUriMatcher.match(uri)) {
        // 删除表
        case PERSON_DIR:
            deletedNum = db.delete(person, selection, selectionArgs);
            break;
        // 按照id删除某条数据
        case PERSON:
            long id = ContentUris.parseId(uri);
            String where = personid= + id;
            if (selection != null && !.equals(selection.trim())) {
                where = selection +  and  + where;
            }
            deletedNum = db.delete(person, where, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException(unknown uri + uri.toString());
        }
        //向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应 
        getContext().getContentResolver().notifyChange(uri, null);  
        return deletedNum;
    }
 
    /**
     * 查询操作:
     * 查询操作有两种可能:查询一张表或者查询某条数据
     * 
     * 注意事项:
     * 在查询某条数据时要注意--因为此处是按照personid来查询
     * 某条数据,但是同时可能还有其他限制.例如:
     * 要求personid为2且name为xiaoming1
     * 所以在查询时分为两步:
     * 第一步:
     * 解析出personid放入where查询条件
     * 第二步:
     * 判断是否有其他限制(如name),若有则将其组拼到where查询条件.
     * 
     * 详细代码见下.
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
        Cursor cursor =null;
        switch (mUriMatcher.match(uri)) {
        // 查询表
        case PERSON_DIR:
            cursor = db.query(person, projection, selection, selectionArgs,null, null, sortOrder);
            break;
        // 按照id查询某条数据
        case PERSON:
            // 第一步:
            long id = ContentUris.parseId(uri);
            String where = personid= + id;
            // 第二步:
            if (selection != null && !.equals(selection.trim())) {
                where = selection +  and  + where;
            }
            cursor = db.query(person, projection, where, selectionArgs, null, null, sortOrder);
            break;
        default:
            throw new IllegalArgumentException(unknown uri + uri.toString());
        }
        return cursor;
    }
     
 
}


SQLiteDatabaseOpenHelper如下:

package cn.testcontentprovider;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class SQLiteDatabaseOpenHelper extends SQLiteOpenHelper {
    public SQLiteDatabaseOpenHelper(Context context) {
        super(context, contentprovidertest.db, null, 1);
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(create table person(personid integer primary key autoincrement,name varchar(20),phone varchar(12),salary  Integer(12)));     
    }
 
    //当数据库版本号发生变化时调用该方法
    @Override
    public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
        //db.execSQL(ALTER TABLE person ADD phone varchar(12) NULL);
        //db.execSQL(ALTER TABLE person ADD salary  Integer NULL);
    }
 
}


MainActivity如下:

package cn.testcontentprovider;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
 
}


AndroidManifest.xml如下:

<!--?xml version=1.0 encoding=utf-8?-->
<manifest android:versioncode="1" android:versionname="1.0" package="cn.testcontentprovider" xmlns:android="http://schemas.android.com/apk/res/android">
 
    <uses-sdk android:minsdkversion="8" android:targetsdkversion="8">
 
    <uses-permission android:name="android.permission.INTERNET">
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS">
     
     
         
            <intent-filter>
                 
 
                <category android:name="android.intent.category.LAUNCHER">
            </category></action></intent-filter>
        </activity>
         
         <provider android:authorities="cn.bs.testcontentprovider" android:exported="true" android:name="cn.testcontentprovider.ContentProviderTest">
    </provider></application>
 
</uses-permission></uses-permission></uses-permission></uses-permission></uses-sdk></manifest>


main.xml如下:

<relativelayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"><button android:layout_centerinparent="true" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="该应用包含一个自定义的ContentProvider" android:textsize="15sp"></button></relativelayout>


以下代码为工程TestBaidu

 

MainActivity如下:

package cn.testbaidu;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.ContentObserver;
import android.database.Cursor;
/**
 * Demo描述:
 * 应用A(TestBaidu)调用另外一个应用(TestContentProvider)中的自定义ContentProvider,即:
 * 1 自定义ContentProvider的使用
 * 2 其它应用调用该ContentProvider
 * 3 ContentObserver的使用
 * 
 * 备注说明:
 * 1 该例子在以前版本的基础上整理了代码
 * 2 该例子在以前版本的基础上融合了ContentObserver的使用
 *   利用ContentObserver随时监听ContentProvider的数据变化.
 *   为实现该功能需要在自定义的ContentProvider的insert(),update(),delete()
 *   方法中调用getContext().getContentResolver().notifyChange(uri, null); 
 *   向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应  
 * 
 * 测试方法:
 * 1 依次测试ContentProvider的增查删改(注意该顺序)!!
 * 2 其它应用查询该ContentProvider的数据
 *
 */
public class MainActivity extends Activity {
    private Button mAddButton;
    private Button mDeleteButton;
    private Button mUpdateButton;
    private Button mQueryButton;
    private Button mTypeButton;
    private long lastTime=0;
    private ContentResolver mContentResolver;
    private ContentObserverSubClass mContentObserverSubClass;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        init();
        initContentObserver();
    }
 
    private void init() {
        mContentResolver=this.getContentResolver();
         
        mAddButton=(Button) findViewById(R.id.addButton);
        mAddButton.setOnClickListener(new ClickListenerImpl());
         
        mDeleteButton=(Button) findViewById(R.id.deleteButton);
        mDeleteButton.setOnClickListener(new ClickListenerImpl());
         
        mUpdateButton=(Button) findViewById(R.id.updateButton);
        mUpdateButton.setOnClickListener(new ClickListenerImpl());
         
        mQueryButton=(Button) findViewById(R.id.queryButton);
        mQueryButton.setOnClickListener(new ClickListenerImpl());
         
        mTypeButton=(Button) findViewById(R.id.typeButton);
        mTypeButton.setOnClickListener(new ClickListenerImpl());
         
    }
     
     
 
    // 注册一个针对ContentProvider的ContentObserver用来观察内容提供者的数据变化
    private void initContentObserver() {
        Uri uri = Uri.parse(content://cn.bs.testcontentprovider/person);
        mContentObserverSubClass=new ContentObserverSubClass(new Handler());
        this.getContentResolver().registerContentObserver(uri, true,mContentObserverSubClass);
    }
     
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mContentObserverSubClass!=null) {
            this.getContentResolver().unregisterContentObserver(mContentObserverSubClass);
        }
    }
 
     
     
     
    // 自定义一个内容观察者ContentObserver
    private class ContentObserverSubClass extends ContentObserver {
 
        public ContentObserverSubClass(Handler handler) {
            super(handler);
        }
 
        //采用时间戳避免多次调用onChange( )
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            System.out.println(ContentObserver onChange() selfChange=+ selfChange);
            if (System.currentTimeMillis()-lastTime>2000) {
                ContentResolver resolver = getContentResolver();
                Uri uri = Uri.parse(content://cn.bs.testcontentprovider/person);
                // 获取最新的一条数据
                Cursor cursor = resolver.query(uri, null, null, null,personid desc limit 1);
                while (cursor.moveToNext()) {
                    int personid = cursor.getInt(cursor.getColumnIndex(personid));
                    System.out.println(内容提供者中的数据发生变化,现数据中第一条数据的personid=+ personid);
                }
                cursor.close();
                lastTime=System.currentTimeMillis();
            }else{
                System.out.println(时间间隔过短,忽略此次更新);
            }
             
             
        }
         
        @Override
        public boolean deliverSelfNotifications() {
            return true;
        }
         
    }
     
     
     
     
 
    private class ClickListenerImpl implements OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.addButton:
                Person person = null;
                for (int i = 0; i < 5; i++) {
                    person = new Person(xiaoming + i, 9527 + i, (8888 + i));
                    testInsert(person);
                }
                break;
            case R.id.deleteButton:
                testDelete(1);
                break;
            case R.id.updateButton:
                testUpdate(3);
                break;
            case R.id.queryButton:
                // 查询表
                // queryFromContentProvider(-1);
 
                // 查询personid=2的数据
                testQuery(2);
                break;
            case R.id.typeButton:
                testType();
                break;
            default:
                break;
            }
 
        }
 
    }
    private void testInsert(Person person) {
        ContentValues contentValues=new ContentValues();
        contentValues.put(name, person.getName());
        contentValues.put(phone, person.getPhone());
        contentValues.put(salary,person.getSalary());
        Uri insertUri=Uri.parse(content://cn.bs.testcontentprovider/person);
        Uri returnUri=mContentResolver.insert(insertUri, contentValues);
        System.out.println(新增数据:returnUri=+returnUri);
    }
     
    private void testDelete(int index){
        Uri uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
        mContentResolver.delete(uri, null, null);
    }
     
    private void testUpdate(int index){
        Uri uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
        ContentValues values=new ContentValues();
        values.put(name, hanmeimei);
        values.put(phone, 1234);
        values.put(salary, 333);
        mContentResolver.update(uri, values, null, null);
    }
 
    private void testQuery(int index) {
        Uri uri=null;
        if (index<=0) {
            //查询表
            uri=Uri.parse(content://cn.bs.testcontentprovider/person);
        } else {
             //按照id查询某条数据
            uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
        }
         
        //对应上面的:查询表
        //Cursor cursor= mContentResolver.query(uri, null, null, null, null);
         
        //对应上面的:查询personid=2的数据
        //注意:因为name是varchar字段的,所以应该写作name='xiaoming1'
        //     若写成name=xiaoming1查询时会报错
        Cursor cursor= mContentResolver.query(uri, null, name='xiaoming1', null, null);
         
        while(cursor.moveToNext()){
            int personid=cursor.getInt(cursor.getColumnIndex(personid));
            String name=cursor.getString(cursor.getColumnIndex(name));
            String phone=cursor.getString(cursor.getColumnIndex(phone));
            int salary=cursor.getInt(cursor.getColumnIndex(salary));
            System.out.println(查询得到:personid= + personid+,name=+name+,phone=+phone+,salary=+salary);
        }
        cursor.close();
    }
     
    private void testType(){
        Uri dirUri=Uri.parse(content://cn.bs.testcontentprovider/person);
        String dirType=mContentResolver.getType(dirUri);
        System.out.println(dirType:+dirType);
         
        Uri itemUri=Uri.parse(content://cn.bs.testcontentprovider/person/3);
        String itemType=mContentResolver.getType(itemUri);
        System.out.println(itemType:+itemType);
    }
 
}


Person如下:

package cn.testbaidu;
 
public class Person {
    private Integer id;
    private String name;
    private String phone;
    private Integer salary;
    public Person(String name, String phone,Integer salary) {
        this.name = name;
        this.phone = phone;
        this.salary=salary;
    }
    public Person(Integer id, String name, String phone,Integer salary) {
        this.id = id;
        this.name = name;
        this.phone = phone;
        this.salary=salary;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public Integer getSalary() {
        return salary;
    }
    public void setSalary(Integer salary) {
        this.salary = salary;
    }
    @Override
    public String toString() {
        return Person [id= + id + , name= + name + , phone= + phone+ , salary= + salary + ];
    }
     
     
     
}


 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值