1.什么是ContentProvider
数据库在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。
不能将数据库设为WORLD_READABLE,每个数据库都只能创建它的包访问,
这意味着只有由创建数据库的进程可访问它。如果需要在进程间传递数据,
则可以使用AIDL/Binder或创建一个ContentProvider,但是不能跨越进程/包边界直接来使用数据库。
一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。
也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,
重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,
当然,中间也会涉及一些权限的问题。下边列举一些较常见的接口,这些接口如下所示。
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
· insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
2.什么是ContentResolver
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用的 ContentResolver实例。
ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个。
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
· insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
3.ContentProvider和ContentResolver中用到的Uri
在ContentProvider和 ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。
我们看下面的例子。
· content://contacts/people/ 这个Uri指定的就是全部的联系人数据。
· content://contacts/people/1 这个Uri指定的是ID为1的联系人的数据。
在上边两个类中用到的Uri一般由3部分组成。
· 第一部分是方案:"content://" 这部分永远不变
· 第二部分是授权:"contacts"
· 第二部分是路径:"people/","people/1"(如果没有指定ID,那么表示返回全部)。
由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码:
· Contacts.People.CONTENT_URI (联系人的URI)。
在我们的实例MyProvider中是如下定义的:
public static final String AUTHORITY="com.teleca.PeopleProvider";
public static final String PATH_SINGLE="people/#";
public static final String PATH_MULTIPLE="people";
public static final Uri content_URI=Uri.parse("content://"+AUTHORITY+"/"+PATH_MULTIPLE);
实例1:
文件MyProvider.java
package com.teleca.provider;
import java.util.HashMap;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class MyProvider extends ContentProvider {
public static final String MIME_DIR_PREFIX="vnd.android.cursor.dir";
public static final String MIME_ITEM_PREFIX="vnd.android.cursor.item";
public static final String MIME_ITEM="vnd.msi.people";
public static final String MIME_TYPE_SINGLE=MIME_ITEM_PREFIX+"/"+MIME_ITEM;
public static final String MIME_TYPE_MULTIPLE=MIME_DIR_PREFIX+"/"+MIME_ITEM;
public static final String AUTHORITY="com.teleca.PeopleProvider";
public static final String PATH_SINGLE="people/#";
public static final String PATH_MULTIPLE="people";
public static final Uri content_URI=Uri.parse("content://"+AUTHORITY+"/"+PATH_MULTIPLE);
public static final String DEFAULT_SORT_ORDER="name DESC";
public static final String _ID="_id";
public static final String NAME="name";
public static final String PHONE="phone";
public static final String AGE="age";
public static final int PEOPLE=1;
public static final int PEOPLES=2;
private static UriMatcher URI_MATCHER;
private static HashMap<String,String> PROJECTION_MAP;
public static String DB_NAME="peopledb";
public static String DB_TABLE_NAME="people";
SQLiteDatabase db;
DBOpenHelper dbOpenHelper;
static
{
URI_MATCHER=new UriMatcher(UriMatcher.NO_MATCH);
URI_MATCHER.addURI(AUTHORITY, PATH_MULTIPLE, PEOPLES);
URI_MATCHER.addURI(AUTHORITY, PATH_SINGLE, PEOPLE);
PROJECTION_MAP=new HashMap<String,String>();
PROJECTION_MAP.put(_ID, "_id");
PROJECTION_MAP.put(NAME, "name");
PROJECTION_MAP.put(PHONE, "phone");
PROJECTION_MAP.put(AGE, "age");
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count=0;
switch(URI_MATCHER.match(uri))
{
case PEOPLES:
count=db.delete(DB_TABLE_NAME, selection, selectionArgs);
break;
case PEOPLE:
String segment =uri.getPathSegments().get(1);
String where="";
if(!TextUtils.isEmpty(selection))
{
where=" AND ("+selection+")";
}
count=db.delete(DB_TABLE_NAME, "_id="+segment+where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unkonw URI"+uri);
}
getContext().getContentResolver().notifyChange(uri, null);//@2
return count;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
switch(URI_MATCHER.match(uri))
{
case PEOPLES:
return MIME_TYPE_MULTIPLE;
case PEOPLE:
return MIME_TYPE_SINGLE;
default:
throw new IllegalArgumentException("Unkown URI "+uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
long rowId=0L;
if(URI_MATCHER.match(uri)!=PEOPLES)
{
throw new IllegalArgumentException("Unkown URI"+uri);
}
rowId=db.insert(DB_TABLE_NAME, null, values);
if(rowId>0)
{
Uri result=ContentUris.withAppendedId(content_URI, rowId);
getContext().getContentResolver().notifyChange(result, null);//@2
return result;
}
else
throw new SQLException("Failed to insert row into "+uri);
}
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
dbOpenHelper=new DBOpenHelper(this.getContext(),DB_NAME,1);
db=dbOpenHelper.getWritableDatabase();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
SQLiteQueryBuilder queryBuilder=new SQLiteQueryBuilder();
queryBuilder.setTables(DBInfo.DB_TABLE_NAME);
queryBuilder.setProjectionMap(PROJECTION_MAP);
switch(URI_MATCHER.match(uri))
{
case PEOPLES:
break;
case PEOPLE:
queryBuilder.appendWhere("_id="+uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unkonw URI"+uri);
}
String orderBy=null;
if(TextUtils.isEmpty(sortOrder))
{
orderBy=DEFAULT_SORT_ORDER;
}
else
orderBy=sortOrder;
Cursor c=queryBuilder.query(db, projection, selection, selectionArgs, null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);//@1
return c;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
int count=0;
switch(URI_MATCHER.match(uri))
{
case PEOPLES:
count=db.update(DB_TABLE_NAME, values, selection, selectionArgs);
break;
case PEOPLE:
String segment =uri.getPathSegments().get(1);
String where="";
if(!TextUtils.isEmpty(selection))
{
where=" AND ("+selection+")";
}
count=db.update(DB_TABLE_NAME, values, "_id="+segment+where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unkonw URI"+uri);
}
getContext().getContentResolver().notifyChange(uri, null);//@2
return count;
}
}
class DBOpenHelper extends SQLiteOpenHelper
{
private static final String DB_CREATE="CREATE TABLE "
+DBInfo.DB_TABLE_NAME
+" (_id INTEGER PRIMARY KEY,name TEXT UNIQUE NOT NULL,"
+"phone TEXT,age INTEGER);";
final static String tag="hubin";
public DBOpenHelper(Context context,String dbName,int version)
{
super(context,dbName,null,version);
}
public void onCreate(SQLiteDatabase db)
{
try{
db.execSQL(DB_CREATE);
}
catch(SQLException e )
{
Log.e(tag,"",e);
}
}
public void onOpen(SQLiteDatabase db)
{
super.onOpen(db);
}
public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS "+DBInfo.DB_TABLE_NAME);
this.onCreate(db);
}
}
class DBInfo
{
public static String DB_NAME="peopledb";
public static String DB_TABLE_NAME="people";
}
注意1:c.setNotificationUri(getContext().getContentResolver(), uri);
这里是把Cursor C添加到ContentResolver的监督对象组中去。
一旦有与uri相关的变化,ContentResolver就回通知Cursor C.
可能Cursor有个私有的内部类ContentObserver的实现。ContentResolver是通过该类来通知Cursor的。
public abstract void setNotificationUri (ContentResolver cr, Uri uri)
Register to watch a content URI for changes. This can be the URI of a specific data row (for example, "content://my_provider_type/23"),
or a a generic URI for a content type.
Parameters
cr The content resolver from the caller's context. The listener attached to this resolver will be notified.
uri The content URI to watch.
注意2: getContext().getContentResolver().notifyChange(uri, null)
通知数据发生了变化。
public void notifyChange (Uri uri, ContentObserver observer)
Notify registered observers that a row was updated. To register, call registerContentObserver(). By default, CursorAdapter objects will get this notification.
Parameters
observer The observer that originated the change, may be null
这里为null的意思可能就是调用在ContentResolver中注册的ContentObserver,反之则是调用参数指定的
文件People.java
package com.teleca.provider;
public class People {
public long id;
public String name;
public String phone;
public int age;
}
文件
Hello.java
package com.teleca.provider;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class Hello extends Activity {
/** Called when the activity is first created. */
final static String tag="hubin";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button) findViewById(R.id.Button01);
OnClickListener listener = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_ADD;
doAction();
}
};
button.setOnClickListener(listener);
Button button2 = (Button) findViewById(R.id.Button02);
OnClickListener listener2 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_UPDATE;
doAction();
}
};
button2.setOnClickListener(listener2);
Button button3 = (Button) findViewById(R.id.Button03);
OnClickListener listener3 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_QUERY;
doAction();
}
};
button3.setOnClickListener(listener3);
Button button4 = (Button) findViewById(R.id.Button04);
OnClickListener listener4 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_QUERY_ALL;
doAction();
}
};
button4.setOnClickListener(listener4);
Button button5 = (Button) findViewById(R.id.Button05);
OnClickListener listener5 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_DELETE;
doAction();
}
};
button5.setOnClickListener(listener5);
Button button6 = (Button) findViewById(R.id.Button06);
OnClickListener listener6 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_DELETE_ALL;
doAction();
}
};
button6.setOnClickListener(listener6);
mHandler = new Handler();
}
int cnt = 0;
private Handler mHandler;
int cmd = 0;
final int CMD_ADD = 1;
final int CMD_UPDATE = 2;
final int CMD_QUERY= 3;
final int CMD_QUERY_ALL = 4;
final int CMD_DELETE = 5;
final int CMD_DELETE_ALL = 6;
People people=new People();
final static String projection[]=new String[]
{"_id","name","phone","age"};
class DatabaseThread implements Runnable {
public void run() {
if (cmd == CMD_ADD) {
people.name="robin"+System.currentTimeMillis()%100;
people.phone=""+System.currentTimeMillis();
people.age=1;
ContentValues values=new ContentValues();
values.put("name", people.name);
values.put("phone", people.phone);
values.put("age", people.age);
Uri uri=getContentResolver().insert(MyProvider.content_URI, values);
people.id=ContentUris.parseId(uri);
Log.i("hubin",uri.toString());
} else if (cmd == CMD_UPDATE) {
ContentValues values=new ContentValues();
people.phone=""+System.currentTimeMillis();
values.put("phone", people.phone);
Uri uri=ContentUris.withAppendedId(MyProvider.content_URI, people.id);
getContentResolver().update(uri,values,null,null);
} else if (cmd == CMD_QUERY) {
Uri uri=ContentUris.withAppendedId(MyProvider.content_URI, people.id);
Cursor c=getContentResolver().query(uri, projection, null, null, null);
People p=get(c);
printPeople(p);
} else if (cmd == CMD_QUERY_ALL) {
Uri uri=MyProvider.content_URI;
Cursor c=getContentResolver().query(uri, projection, null, null, null);
List<People> list=getAll(c);
int total=list.size();
for(int i=0;i<total;i++)
{
printPeople(list.get(i));
}
}
else if (cmd==CMD_DELETE)
{
Uri uri=ContentUris.withAppendedId(MyProvider.content_URI, people.id);
getContentResolver().delete(uri, null, null);
}
else if (cmd==CMD_DELETE_ALL)
{
Uri uri=MyProvider.content_URI;
getContentResolver().delete(uri, null, null);
}
cnt++;
}
}
void printPeople(People p)
{
Log.i(tag, "id:"+p.id);
Log.i(tag, "name:"+p.name);
Log.i(tag,"phone:"+p.phone);
Log.i(tag,"age:"+p.age);
}
DatabaseThread dataDealer=new DatabaseThread();
void doAction() {
mHandler.post(dataDealer);
}
public People get(Cursor c)
{
People people=new People();
try{
Log.i(tag,"count:"+c.getCount());
if(c.getCount()>0)
{
c.moveToFirst();
people=new People();
people.id=c.getLong(0);
people.name=c.getString(1);
people.phone=c.getString(2);
people.age=c.getInt(3);
}
}catch(SQLException e)
{
Log.i(tag,"",e);
}
finally
{
if(c!=null&&!c.isClosed())
{
c.close();
}
}
return people;
}
public List<People> getAll(Cursor c)
{
ArrayList<People> ret=new ArrayList<People>();
try
{
int count=c.getCount();
c.moveToFirst();
People people;
for(int i=0;i<count;i++)
{
people=new People();
people.id=c.getLong(0);
people.name=c.getString(1);
people.phone=c.getString(2);
people.age=c.getInt(3);
ret.add(people);
c.moveToNext();
}
}catch(SQLException e)
{
Log.i(tag,"",e);
}
finally
{
if(c!=null&&!c.isClosed())
{
c.close();
}
}
return ret;
}
}
注意:Cursor c不用时要关掉。
文件AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android "
package="com.teleca.provider"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Hello"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider android:syncable="true" android:name="MyProvider" android:authorities="com.teleca.PeopleProvider"></provider>
</application>
<uses-sdk android:minSdkVersion="7" />
</manifest>
数据库在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。
不能将数据库设为WORLD_READABLE,每个数据库都只能创建它的包访问,
这意味着只有由创建数据库的进程可访问它。如果需要在进程间传递数据,
则可以使用AIDL/Binder或创建一个ContentProvider,但是不能跨越进程/包边界直接来使用数据库。
一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。
也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,
重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,
当然,中间也会涉及一些权限的问题。下边列举一些较常见的接口,这些接口如下所示。
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
· insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
2.什么是ContentResolver
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用的 ContentResolver实例。
ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个。
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
· insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
3.ContentProvider和ContentResolver中用到的Uri
在ContentProvider和 ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。
我们看下面的例子。
· content://contacts/people/ 这个Uri指定的就是全部的联系人数据。
· content://contacts/people/1 这个Uri指定的是ID为1的联系人的数据。
在上边两个类中用到的Uri一般由3部分组成。
· 第一部分是方案:"content://" 这部分永远不变
· 第二部分是授权:"contacts"
· 第二部分是路径:"people/","people/1"(如果没有指定ID,那么表示返回全部)。
由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码:
· Contacts.People.CONTENT_URI (联系人的URI)。
在我们的实例MyProvider中是如下定义的:
public static final String AUTHORITY="com.teleca.PeopleProvider";
public static final String PATH_SINGLE="people/#";
public static final String PATH_MULTIPLE="people";
public static final Uri content_URI=Uri.parse("content://"+AUTHORITY+"/"+PATH_MULTIPLE);
实例1:
文件MyProvider.java
package com.teleca.provider;
import java.util.HashMap;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class MyProvider extends ContentProvider {
public static final String MIME_DIR_PREFIX="vnd.android.cursor.dir";
public static final String MIME_ITEM_PREFIX="vnd.android.cursor.item";
public static final String MIME_ITEM="vnd.msi.people";
public static final String MIME_TYPE_SINGLE=MIME_ITEM_PREFIX+"/"+MIME_ITEM;
public static final String MIME_TYPE_MULTIPLE=MIME_DIR_PREFIX+"/"+MIME_ITEM;
public static final String AUTHORITY="com.teleca.PeopleProvider";
public static final String PATH_SINGLE="people/#";
public static final String PATH_MULTIPLE="people";
public static final Uri content_URI=Uri.parse("content://"+AUTHORITY+"/"+PATH_MULTIPLE);
public static final String DEFAULT_SORT_ORDER="name DESC";
public static final String _ID="_id";
public static final String NAME="name";
public static final String PHONE="phone";
public static final String AGE="age";
public static final int PEOPLE=1;
public static final int PEOPLES=2;
private static UriMatcher URI_MATCHER;
private static HashMap<String,String> PROJECTION_MAP;
public static String DB_NAME="peopledb";
public static String DB_TABLE_NAME="people";
SQLiteDatabase db;
DBOpenHelper dbOpenHelper;
static
{
URI_MATCHER=new UriMatcher(UriMatcher.NO_MATCH);
URI_MATCHER.addURI(AUTHORITY, PATH_MULTIPLE, PEOPLES);
URI_MATCHER.addURI(AUTHORITY, PATH_SINGLE, PEOPLE);
PROJECTION_MAP=new HashMap<String,String>();
PROJECTION_MAP.put(_ID, "_id");
PROJECTION_MAP.put(NAME, "name");
PROJECTION_MAP.put(PHONE, "phone");
PROJECTION_MAP.put(AGE, "age");
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count=0;
switch(URI_MATCHER.match(uri))
{
case PEOPLES:
count=db.delete(DB_TABLE_NAME, selection, selectionArgs);
break;
case PEOPLE:
String segment =uri.getPathSegments().get(1);
String where="";
if(!TextUtils.isEmpty(selection))
{
where=" AND ("+selection+")";
}
count=db.delete(DB_TABLE_NAME, "_id="+segment+where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unkonw URI"+uri);
}
getContext().getContentResolver().notifyChange(uri, null);//@2
return count;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
switch(URI_MATCHER.match(uri))
{
case PEOPLES:
return MIME_TYPE_MULTIPLE;
case PEOPLE:
return MIME_TYPE_SINGLE;
default:
throw new IllegalArgumentException("Unkown URI "+uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
long rowId=0L;
if(URI_MATCHER.match(uri)!=PEOPLES)
{
throw new IllegalArgumentException("Unkown URI"+uri);
}
rowId=db.insert(DB_TABLE_NAME, null, values);
if(rowId>0)
{
Uri result=ContentUris.withAppendedId(content_URI, rowId);
getContext().getContentResolver().notifyChange(result, null);//@2
return result;
}
else
throw new SQLException("Failed to insert row into "+uri);
}
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
dbOpenHelper=new DBOpenHelper(this.getContext(),DB_NAME,1);
db=dbOpenHelper.getWritableDatabase();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
SQLiteQueryBuilder queryBuilder=new SQLiteQueryBuilder();
queryBuilder.setTables(DBInfo.DB_TABLE_NAME);
queryBuilder.setProjectionMap(PROJECTION_MAP);
switch(URI_MATCHER.match(uri))
{
case PEOPLES:
break;
case PEOPLE:
queryBuilder.appendWhere("_id="+uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unkonw URI"+uri);
}
String orderBy=null;
if(TextUtils.isEmpty(sortOrder))
{
orderBy=DEFAULT_SORT_ORDER;
}
else
orderBy=sortOrder;
Cursor c=queryBuilder.query(db, projection, selection, selectionArgs, null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);//@1
return c;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
int count=0;
switch(URI_MATCHER.match(uri))
{
case PEOPLES:
count=db.update(DB_TABLE_NAME, values, selection, selectionArgs);
break;
case PEOPLE:
String segment =uri.getPathSegments().get(1);
String where="";
if(!TextUtils.isEmpty(selection))
{
where=" AND ("+selection+")";
}
count=db.update(DB_TABLE_NAME, values, "_id="+segment+where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unkonw URI"+uri);
}
getContext().getContentResolver().notifyChange(uri, null);//@2
return count;
}
}
class DBOpenHelper extends SQLiteOpenHelper
{
private static final String DB_CREATE="CREATE TABLE "
+DBInfo.DB_TABLE_NAME
+" (_id INTEGER PRIMARY KEY,name TEXT UNIQUE NOT NULL,"
+"phone TEXT,age INTEGER);";
final static String tag="hubin";
public DBOpenHelper(Context context,String dbName,int version)
{
super(context,dbName,null,version);
}
public void onCreate(SQLiteDatabase db)
{
try{
db.execSQL(DB_CREATE);
}
catch(SQLException e )
{
Log.e(tag,"",e);
}
}
public void onOpen(SQLiteDatabase db)
{
super.onOpen(db);
}
public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS "+DBInfo.DB_TABLE_NAME);
this.onCreate(db);
}
}
class DBInfo
{
public static String DB_NAME="peopledb";
public static String DB_TABLE_NAME="people";
}
注意1:c.setNotificationUri(getContext().getContentResolver(), uri);
这里是把Cursor C添加到ContentResolver的监督对象组中去。
一旦有与uri相关的变化,ContentResolver就回通知Cursor C.
可能Cursor有个私有的内部类ContentObserver的实现。ContentResolver是通过该类来通知Cursor的。
public abstract void setNotificationUri (ContentResolver cr, Uri uri)
Register to watch a content URI for changes. This can be the URI of a specific data row (for example, "content://my_provider_type/23"),
or a a generic URI for a content type.
Parameters
cr The content resolver from the caller's context. The listener attached to this resolver will be notified.
uri The content URI to watch.
注意2: getContext().getContentResolver().notifyChange(uri, null)
通知数据发生了变化。
public void notifyChange (Uri uri, ContentObserver observer)
Notify registered observers that a row was updated. To register, call registerContentObserver(). By default, CursorAdapter objects will get this notification.
Parameters
observer The observer that originated the change, may be null
这里为null的意思可能就是调用在ContentResolver中注册的ContentObserver,反之则是调用参数指定的
文件People.java
package com.teleca.provider;
public class People {
public long id;
public String name;
public String phone;
public int age;
}
文件
Hello.java
package com.teleca.provider;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class Hello extends Activity {
/** Called when the activity is first created. */
final static String tag="hubin";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button) findViewById(R.id.Button01);
OnClickListener listener = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_ADD;
doAction();
}
};
button.setOnClickListener(listener);
Button button2 = (Button) findViewById(R.id.Button02);
OnClickListener listener2 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_UPDATE;
doAction();
}
};
button2.setOnClickListener(listener2);
Button button3 = (Button) findViewById(R.id.Button03);
OnClickListener listener3 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_QUERY;
doAction();
}
};
button3.setOnClickListener(listener3);
Button button4 = (Button) findViewById(R.id.Button04);
OnClickListener listener4 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_QUERY_ALL;
doAction();
}
};
button4.setOnClickListener(listener4);
Button button5 = (Button) findViewById(R.id.Button05);
OnClickListener listener5 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_DELETE;
doAction();
}
};
button5.setOnClickListener(listener5);
Button button6 = (Button) findViewById(R.id.Button06);
OnClickListener listener6 = new OnClickListener() {
@Override
public void onClick(View v) {
cmd = CMD_DELETE_ALL;
doAction();
}
};
button6.setOnClickListener(listener6);
mHandler = new Handler();
}
int cnt = 0;
private Handler mHandler;
int cmd = 0;
final int CMD_ADD = 1;
final int CMD_UPDATE = 2;
final int CMD_QUERY= 3;
final int CMD_QUERY_ALL = 4;
final int CMD_DELETE = 5;
final int CMD_DELETE_ALL = 6;
People people=new People();
final static String projection[]=new String[]
{"_id","name","phone","age"};
class DatabaseThread implements Runnable {
public void run() {
if (cmd == CMD_ADD) {
people.name="robin"+System.currentTimeMillis()%100;
people.phone=""+System.currentTimeMillis();
people.age=1;
ContentValues values=new ContentValues();
values.put("name", people.name);
values.put("phone", people.phone);
values.put("age", people.age);
Uri uri=getContentResolver().insert(MyProvider.content_URI, values);
people.id=ContentUris.parseId(uri);
Log.i("hubin",uri.toString());
} else if (cmd == CMD_UPDATE) {
ContentValues values=new ContentValues();
people.phone=""+System.currentTimeMillis();
values.put("phone", people.phone);
Uri uri=ContentUris.withAppendedId(MyProvider.content_URI, people.id);
getContentResolver().update(uri,values,null,null);
} else if (cmd == CMD_QUERY) {
Uri uri=ContentUris.withAppendedId(MyProvider.content_URI, people.id);
Cursor c=getContentResolver().query(uri, projection, null, null, null);
People p=get(c);
printPeople(p);
} else if (cmd == CMD_QUERY_ALL) {
Uri uri=MyProvider.content_URI;
Cursor c=getContentResolver().query(uri, projection, null, null, null);
List<People> list=getAll(c);
int total=list.size();
for(int i=0;i<total;i++)
{
printPeople(list.get(i));
}
}
else if (cmd==CMD_DELETE)
{
Uri uri=ContentUris.withAppendedId(MyProvider.content_URI, people.id);
getContentResolver().delete(uri, null, null);
}
else if (cmd==CMD_DELETE_ALL)
{
Uri uri=MyProvider.content_URI;
getContentResolver().delete(uri, null, null);
}
cnt++;
}
}
void printPeople(People p)
{
Log.i(tag, "id:"+p.id);
Log.i(tag, "name:"+p.name);
Log.i(tag,"phone:"+p.phone);
Log.i(tag,"age:"+p.age);
}
DatabaseThread dataDealer=new DatabaseThread();
void doAction() {
mHandler.post(dataDealer);
}
public People get(Cursor c)
{
People people=new People();
try{
Log.i(tag,"count:"+c.getCount());
if(c.getCount()>0)
{
c.moveToFirst();
people=new People();
people.id=c.getLong(0);
people.name=c.getString(1);
people.phone=c.getString(2);
people.age=c.getInt(3);
}
}catch(SQLException e)
{
Log.i(tag,"",e);
}
finally
{
if(c!=null&&!c.isClosed())
{
c.close();
}
}
return people;
}
public List<People> getAll(Cursor c)
{
ArrayList<People> ret=new ArrayList<People>();
try
{
int count=c.getCount();
c.moveToFirst();
People people;
for(int i=0;i<count;i++)
{
people=new People();
people.id=c.getLong(0);
people.name=c.getString(1);
people.phone=c.getString(2);
people.age=c.getInt(3);
ret.add(people);
c.moveToNext();
}
}catch(SQLException e)
{
Log.i(tag,"",e);
}
finally
{
if(c!=null&&!c.isClosed())
{
c.close();
}
}
return ret;
}
}
注意:Cursor c不用时要关掉。
文件AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android
package="com.teleca.provider"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Hello"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider android:syncable="true" android:name="MyProvider" android:authorities="com.teleca.PeopleProvider"></provider>
</application>
<uses-sdk android:minSdkVersion="7" />
</manifest>
list_row.xml文件
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" android:id="@+id/list_row" />
main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" >
<ListView android:id="@id/android:list" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00FF00" android:layout_weight="1" android:drawSelectorOnTop="false"/>
<Button android:text="@+string/Add" android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="@+string/DeleteAll" android:id="@+id/Button02" android:layout_width="wrap_content"></Button>
</LinearLayout>
需要的权限:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
真的需要这些权限?为什么需要呢?也许是我以前写错了吧