在前面的文章中,已经知道了文件读写、SharedPreferences、SQLite数据库三种方式来存储数据,这章是存储数据的另一种方式:ContentProvider.

一、知识储备

1.1 Uri介绍

Uri代表了要操作的数据,Uri主要包含了两部分信息:1》需要操作的ContentProvider ,2》对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:          

                 

ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:content://
主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它
路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
要操作person表中id为10的记录,可以构建这样的路径:/person/10
要操作person表中id为10的记录的name字段, person/10/name
要操作person表中的所有记录,可以构建这样的路径:/person
要操作xxx表中的记录,可以构建这样的路径:/xxx
当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:
要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person")

 

二、创建和测试ContentProvider

ContentProvider可以对外提过接口,使外部应用可以对数据进行增删改查,内容提供者类必须要继承与ContentProvider。下面是一个关于ContentProvider的例子。

UserProvider.java

package com.lk.sqlite;

import com.lk.util.SQLHelper;
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 UserProvider extends ContentProvider {
    private SQLHelper sqlHelper;
    //当不匹配时返回UriMatcher.NO_MATCH
    private final static UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    private final static int MATCH = 1;
    private final static int MATCH_ID = 2;
    
    static {
        // 当匹配"com.lk.sqlite.providers.userProvider/user"时返回MATCH
        URI_MATCHER.addURI("com.lk.sqlite.providers.userProvider", "user", MATCH);
        // 当匹配"com.lk.sqlite.providers.userProvider/user/数字"时返回MATCH_ID
        // "#"代表一个数字, "*"代表任何文本
        URI_MATCHER.addURI("com.lk.sqlite.providers.userProvider", "user/#", MATCH_ID);
    }

    @Override
    public boolean onCreate() {
        sqlHelper = new SQLHelper(getContext(), 1);
        return true;
    }

    /**
     * 提供查询接口
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase();
        Cursor cursor = null;
        switch (URI_MATCHER.match(uri)) {
        case MATCH_ID:
            long id = ContentUris.parseId(uri);
            String whereClause = "id = " + id;
            if(selection != null && !"".equals(selection)) {
                whereClause += " and " + selection;
            }
            cursor = sqLiteDatabase.query("user", projection, whereClause, selectionArgs, null, null, sortOrder);
            break;
        case MATCH:
            cursor = sqLiteDatabase.query("user", projection, selection, selectionArgs, null, null, sortOrder);
            break;
        default:
            throw new IllegalArgumentException("URI不合法");
        }
        return cursor;
    }

    /**
     * Uri代表单行操作的时候返回"vnd.android.cursor.item",多行操作时返回"vnd.android.cursor.dir/"
     */
    @Override
    public String getType(Uri uri) {
        switch (URI_MATCHER.match(uri)) {
        case MATCH:
            return "vnd.android.cursor.dir/";
        case MATCH_ID:
            return "vnd.android.cursor.item";
        default:
            throw new IllegalArgumentException("URI不合法");
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        switch (URI_MATCHER.match(uri)) {
        case MATCH:
            SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase();
            long rowId = sqLiteDatabase.insert("user", null, values);
            sqLiteDatabase.close();
            return ContentUris.withAppendedId(uri, rowId);
        default:
            throw new IllegalArgumentException("URI不合法");
        }
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase();
        int effectRow = 0;
        switch (URI_MATCHER.match(uri)) {
        case MATCH_ID:
            long id = ContentUris.parseId(uri);
            String whereClause = "id = " + id;
            if(selection != null && !"".equals(selection)) {
                whereClause += " and " + selection;
            }
            effectRow = sqLiteDatabase.delete("user", whereClause, selectionArgs);
            sqLiteDatabase.close();
            break;
        case MATCH:
            effectRow = sqLiteDatabase.delete("user", selection, selectionArgs);
            sqLiteDatabase.close();
            break;
        default:
            throw new IllegalArgumentException("URI不合法");
        }
        return effectRow;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase();
        int effectRow = 0;
        switch (URI_MATCHER.match(uri)) {
        case MATCH_ID:
            long id = ContentUris.parseId(uri);
            String whereClause = "id = " + id;
            if(selection != null && !"".equals(selection)) {
                whereClause += " and " + selection;
            }
            effectRow = sqLiteDatabase.update("user", values, whereClause, selectionArgs);
            sqLiteDatabase.close();
            break;
        case MATCH:
            effectRow = sqLiteDatabase.update("user", values, selection, selectionArgs);
            sqLiteDatabase.close();
            break;
        default:
            throw new IllegalArgumentException("URI不合法");
        }
        return effectRow;
    }

}

 在配置文件中添加ContentProvider组件

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lk.sqlite"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="10" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.lk.sqlite.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <!-- android:authorities是标示ContentProvider的唯一标示,必须唯一 -->
        <provider android:name="com.lk.sqlite.UserProvider"
            android:authorities="com.lk.sqlite.providers.userProvider" />
        <uses-library android:name="android.test.runner"/>
    </application>

    <instrumentation 
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="com.lk.sqlite" />
</manifest>

 

 

将这个应用运行到Android模拟机中,在创建另一个Android 项目,测试是否可以利用ContentProvider提供的接口增删改查ContentProvider所在应用。

AndroidTest项目: ContentResolverTest.java

package com.lk.test;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.util.Log;

public class ContentResolverTest extends AndroidTestCase {
    private final static String TAG = "db";
    
    public void testInsert() {
        ContentResolver contentResolver = getContext().getContentResolver();
        Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user");
        ContentValues values = new ContentValues();
        values.put("name", "王五");
        values.put("email", "wangwu@sina.com");
        values.put("account", 5000);
        contentResolver.insert(url , values );
    }
    
    public void testDelete() {
        ContentResolver contentResolver = getContext().getContentResolver();
        Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user/10");
        contentResolver.delete(url, null, null);
    }
    
    public void testDeleteByQuery() {
        ContentResolver contentResolver = getContext().getContentResolver();
        Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user");
        contentResolver.delete(url, "name = ?", new String[]{"李四"});
    }
    
    public void testUpdate() {
        ContentResolver contentResolver = getContext().getContentResolver();
        Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user/16");
        ContentValues values = new ContentValues();
        values.put("name", "张飞");
        values.put("email", "zhangfei@sina.com");
        values.put("account", 2000);
        contentResolver.update(url, values, null, null);
    }
    
    public void testUpdateByQuery() {
        ContentResolver contentResolver = getContext().getContentResolver();
        Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user");
        ContentValues values = new ContentValues();
        values.put("name", "关羽");
        values.put("email", "guanyu@sina.com");
        values.put("account", 2500);
        contentResolver.update(url, values, "id >= ? and id <= ?", new String[]{"12", "14"});
    }
    
    public void testQuery() {
        ContentResolver contentResolver = getContext().getContentResolver();
        Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user/15");
        Cursor cursor = contentResolver.query(url, null, null, null, null);
        while(cursor.moveToNext()) {
            Log.i(TAG, "id = " + cursor.getInt(cursor.getColumnIndex("id"))
                    + ", name = " + cursor.getString(cursor.getColumnIndex("name")));
        }
        cursor.close();
    }

}

 

在每个方法测试完后,就可以查看一下SQLite数据库是否改变。

 

三、监听ContentProvider数据变化

  但ContentProvider中的数据发送变化时,其他应用可以监听它,前提是监听它的应用的activity必须要是开着的

首先要在ContentProvider中写代码通知监听者数据变化

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        switch (URI_MATCHER.match(uri)) {
        case MATCH:
            SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase();
            long rowId = sqLiteDatabase.insert("user", null, values);
            sqLiteDatabase.close();
            //通知注册在此URI上的访问者,数据发生了变化
            getContext().getContentResolver().notifyChange(uri, null);
            return ContentUris.withAppendedId(uri, rowId);
        default:
            throw new IllegalArgumentException("URI不合法");
        }
    }

 

在另一个应用的activity中想内容提供者中添加数据

    public void save(View view) {
        ContentResolver contentResolver = this.getContentResolver();
        Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user");
        
        // 注册监听"content://com.lk.sqlite.providers.userProvider/user"代表的数据
        contentResolver.registerContentObserver(url, true, new ContentObserver(new Handler()) {
            @Override
            public void onChange(boolean selfChange) {
                Uri uri = Uri.parse("content://com.lk.sqlite.providers.userProvider/user");
                // 当"content://com.lk.sqlite.providers.userProvider/user"代表的数据发送变化时,系统就会调用这个方法
                Cursor cursor = getContentResolver().query(uri , null, null, null, " id desc limit 0, 1");
                if(cursor.moveToFirst()) {
                    Log.i("db", "id = " + cursor.getInt(cursor.getColumnIndex("id"))
                            + ", name = " + cursor.getString(cursor.getColumnIndex("name")));
                }
                super.onChange(selfChange);
            }
        });
        
        ContentValues values = new ContentValues();
        values.put("name", "马超");
        values.put("email", "zhaoyun@sina.com");
        values.put("account", 10000);
        contentResolver.insert(url , values );
    }

 

当触发save方法后,就会调用onChange方法。

 

 

posted on 2014-03-21 21:21  寒岁青松  阅读(101)  评论(0)    收藏  举报