Android开发之实现自己的ContentProvider

本文详细介绍了如何实现和使用自定义ContentProvider,利用SQLite进行数据存储,并提供了实例代码和使用方法。

一般我们很少自己实现ContentProvider,都是用的Android内置的Contentprovider。

下面的这个例子说明如何实现自己的ContentProvider,并且如何使用它。

数据存储采用的是SQLite。

 

实现ContentProvider涉及的东西很多,不一一概述,代码中有相应注释。

 

MyContentPorviderMetaData.java:

这个类中封装了实现自己的ContentProvider用到的常量。

 

import android.net.Uri;
import android.provider.BaseColumns;

public final class MyContentProviderMetaData {
	// 数据库名称
	public static final String DATABASE_NAME = "lucky_star.db";
	// 数据库版本
	public static final int DATABASE_VERSION = 1;
	// ContentProvider的完全限定名
	public static final String AUTHOIRTY = "com.and.test.contentprovider.MyContentProvider";
	
	public static final class  UserTableMetaData implements BaseColumns{
		// BaseColumns中已经有_ID和_COUNT2个字段了。
		// 访问该ContentProvider的URI
		// CONTENT_URI必须是一个唯一的值,推荐的方式使用ContentProvider的完全限定名。
		public static final Uri CONTENT_URI = 
			Uri.parse("content://"+AUTHOIRTY+"/user");
		
		// 该ContentProvider返回的数据类型的定义。
		// 下面这种方式是参考Android联系人里面定义的方式。
		// 访问所有记录的数据类型
		public static final String CONTENT_TYPE_ITMES = "vnd.android.cursor.dir/vnd.mycontentprovider.user";
		// 访问某条记录的数据类型
		public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.mycontentprovider.user";
		// 表名
		public static final String TABLE_NAME = "user";
		// 字段名
		public static final String USER_NAME = "name";
		public static final String USER_AGE = "age";
		public static final String USER_ADDRESS = "address";
		// 默认排序规则,按name升序排列。
		public static final String DEFAULT_ORDER = "name asc";
		
	}
}


MyContentProvider.java:

 

 

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
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;

import com.and.test.DatabaseHelper;

/**
 * 要实现自己的ContentProvider,需要继承ContentProvider,并实现onCreate(),insert(),query(),update(),delete(),getType()这些方法。<br>
 * onCreate()方法在初始化ContentProvider时被调用。
 * @author babylove
 *
 */
public class MyContentProvider extends ContentProvider{
	public static UriMatcher uriMatcher = null;
	// 1:表示访问所有的记录。
	public static final int FETCH_ALL_ROWS = 1;
	// 2:表示访问1条记录。
	public static final int FETCH_ONE_ROW = 2;
	
	private SQLiteOpenHelper soh = null;
	private static Map<String, String> columnMap = new HashMap<String, String>();
	
	static {
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		// user:表示访问user表的所有记录
		uriMatcher.addURI(MyContentProviderMetaData.AUTHOIRTY, "user", FETCH_ALL_ROWS);
		// user/#:表示访问user表的某条记录。
		uriMatcher.addURI(MyContentProviderMetaData.AUTHOIRTY, "user/#", FETCH_ONE_ROW);
	}
	
	// 给查询字段取的别名。在用SQLiteQueryBuilder是用到。
	static {
		columnMap.put(MyContentProviderMetaData.UserTableMetaData._ID, MyContentProviderMetaData.UserTableMetaData._ID);
		columnMap.put(MyContentProviderMetaData.UserTableMetaData.USER_NAME, MyContentProviderMetaData.UserTableMetaData.USER_NAME);
		columnMap.put(MyContentProviderMetaData.UserTableMetaData.USER_AGE, MyContentProviderMetaData.UserTableMetaData.USER_AGE);
		columnMap.put(MyContentProviderMetaData.UserTableMetaData.USER_ADDRESS, MyContentProviderMetaData.UserTableMetaData.USER_ADDRESS);
	}
	
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase sdb = soh.getWritableDatabase();
		int count = sdb.delete(MyContentProviderMetaData.UserTableMetaData.TABLE_NAME, selection, selectionArgs);
		// 通知监听器,数据已更改
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	// 根据传入的URI,返回该URI表示的数据类型。
	@Override
	public String getType(Uri uri) {
		// UriMatcher中通过addURI()方法将一个URI跟一个数字绑定
		// 这里通过UriMatcher的match()方法,如果有匹配的URI,则将该URI对应的数字返回。
		switch (uriMatcher.match(uri)) {
		case FETCH_ALL_ROWS:
			return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITMES;
		case FETCH_ONE_ROW:
			return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;
		default:
			throw new IllegalArgumentException("Unknown Uri " + uri);
		}
	}

	// 返回成功插入数据的对应的URi。
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase sdb = soh.getWritableDatabase();
		// 返回的是成功插入数据库后的生成的rowId,如果插入失败返回-1.
		long rowId = sdb.insert(MyContentProviderMetaData.UserTableMetaData.TABLE_NAME, null, values);
		if (rowId > 0) {
			// 刚刚插入数据库的记录对应的URI
			Uri insertedUri = ContentUris.withAppendedId(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, rowId);
			System.out.println(insertedUri.toString());
			// 通知监听器,数据已更改。
			getContext().getContentResolver().notifyChange(insertedUri, null);
			return insertedUri;
		}
		throw new SQLException("Failed insert data into " + uri);
	}

	// 这是一个回调方法,在ContentProvider创建时调用。
	@Override
	public boolean onCreate() {
		// 这里在初始化ContentProvider时打开数据库
		soh = new DatabaseHelper(getContext(), MyContentProviderMetaData.DATABASE_NAME);
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
		// 设置要查询的表名
		builder.setTables(MyContentProviderMetaData.UserTableMetaData.TABLE_NAME);
		// 设置查询的字段别名
		builder.setProjectionMap(columnMap);
		
		switch (uriMatcher.match(uri)) {
		case FETCH_ALL_ROWS: // 查询所有记录
			break;
		case FETCH_ONE_ROW: // 查询某条记录
			// uri.getPathSegments():decoded path segments, each without a leading or trailing '/' 
			// 如:content://com.and.test.contentprovider/user/23返回的是:user,23。那么get(1)得到的就是23,这个就是要查询的记录的_ID。
			List<String> list = uri.getPathSegments();
			for (int i=0;i<list.size();i++) {
				Log.d(MyContentProvider.class.getName(),"path[" + "]:" + list.get(i));
			}
			builder.appendWhere(MyContentProviderMetaData.UserTableMetaData._ID + " = " + uri.getPathSegments().get(1));
		default:
			throw new SQLException("Unknown Uri for query:" + uri);
		}
		
		// 排序规则,如果调用者传入了orderBy,则使用调用者的orderBy;否则使用默认的排序规则。
		String orderBy = null;
		
		if (TextUtils.isEmpty(sortOrder)) {
			orderBy = MyContentProviderMetaData.UserTableMetaData.DEFAULT_ORDER;
		}
		else {
			orderBy = sortOrder;
		}
		
		SQLiteDatabase sdb = soh.getReadableDatabase();
		Cursor cursor = builder.query(sdb, projection, selection, selectionArgs, null, null, orderBy);
		cursor.setNotificationUri(getContext().getContentResolver(), uri);
		return cursor;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		SQLiteDatabase sdb = soh.getWritableDatabase();
		// 返回受影响的行数。
		int count = sdb.update(MyContentProviderMetaData.UserTableMetaData.TABLE_NAME, values, selection, selectionArgs);
		// 通知监听器,数据已改变。
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

}


ContentProviderActivity.java:

 

这是使用我们自己实现的ContentProvider的一个例子

 

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.and.test.R;

public class ContentProviderActivity extends Activity {
	private Button insertBtn = null;
	private Button queryBtn = null;
	private Button updateBtn = null;
	private Button deleteBtn = null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.contentprovider);
		
		insertBtn = (Button) findViewById(R.id.contentprovider_insert_btn);
		queryBtn = (Button) findViewById(R.id.contentprovider_query_btn);
		updateBtn = (Button) findViewById(R.id.contentprovider_update_btn);
		deleteBtn = (Button) findViewById(R.id.contentprovider_delete_btn);
		
		insertBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				ContentValues values = new ContentValues();
				values.put(MyContentProviderMetaData.UserTableMetaData.USER_NAME, "content provider");
				values.put(MyContentProviderMetaData.UserTableMetaData.USER_AGE, 25);
				values.put(MyContentProviderMetaData.UserTableMetaData.USER_ADDRESS,"china");
				Uri uri = getContentResolver().insert(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, values);
				System.out.println("数据插入成功,对应的Uri:" + uri);
			}
		});
		
		queryBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Cursor c = getContentResolver().query(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, null, null, null, null);
				while (c.moveToNext()) {
					// _ID
					int _id = c.getInt(c.getColumnIndex(MyContentProviderMetaData.UserTableMetaData._ID));
					// name
					String name = c.getString(c.getColumnIndex(MyContentProviderMetaData.UserTableMetaData.USER_NAME));
					// age
					int age = c.getInt(c.getColumnIndex(MyContentProviderMetaData.UserTableMetaData.USER_AGE));
					// address
					String address = c.getString(c.getColumnIndex(MyContentProviderMetaData.UserTableMetaData.USER_ADDRESS));
					String data = ",_id:" + _id + ",name:" + name + ",age:" + age + ",address:" + address;
					System.out.println(data);
					Toast.makeText(ContentProviderActivity.this, data, Toast.LENGTH_SHORT).show();
				}
			}
		});
		
		updateBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				ContentValues values = new ContentValues();
				values.put(MyContentProviderMetaData.UserTableMetaData.USER_NAME, "updated_name");
				values.put(MyContentProviderMetaData.UserTableMetaData.USER_AGE, 22);
				int count = getContentResolver().update(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, values, MyContentProviderMetaData.UserTableMetaData.USER_NAME+"=?", new String[]{"content provider"});
				if (count > 0) {
					Toast.makeText(ContentProviderActivity.this, "数据已修改!", Toast.LENGTH_SHORT).show();
				}
			}
		});
		
		deleteBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				getContentResolver().delete(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, MyContentProviderMetaData.UserTableMetaData.USER_NAME+"=?", new String[]{"content provider"});
				Toast.makeText(ContentProviderActivity.this, "数据删除!", Toast.LENGTH_SHORT).show();
			}
		});
	}
	
}


DatabaseHelper.java:

 

 

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

/**
 * SQLiteOpenHelper是一个抽象类,不能直接使用,需要自己写一个类继承它。
 * 然后可以通过getReadableDatabase()获取一个可读的database;<br>
 * 通过getWritableDatabase()方法获取一个可读写的Database。<br>
 * 如果只有查询操作,就用getReadableDatabase()就可以了。
 * @author lucky star
 *
 */
public class DatabaseHelper extends SQLiteOpenHelper {
	// 数据库版本号,从1开始,必须是大于0的整数。
	public final static int VERSION = 1;
	
	// 需要复写SQLiteOpenHelper的这个构造方法。
	public DatabaseHelper(Context context, String name, CursorFactory factory,
			int version) {
		super(context, name, factory, version);
		// TODO Auto-generated constructor stub
	}
	
	public DatabaseHelper(Context context,String name,int version) {
		this(context,name,null,version);
	}
	
	public DatabaseHelper(Context context,String name) {
		this(context,name,null,VERSION);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		Log.d("DatabaseHelper", "onCreate()");
		// 创建数据时就创建好表结构。
		db.execSQL("create table user(_id integer primary key autoincrement,name varchar(32),age int,address varchar(100))");
		Log.d("DatabaseHelper","create table user.");
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.d("DatabaseHelper","onUpgrade()");
	}

}


使用的布局文件contentprovider.xml:

 

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
    
    

	<Button 
    	android:id="@+id/contentprovider_insert_btn"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:text="@string/insertData"
    />
    <Button 
    	android:id="@+id/contentprovider_query_btn"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:text="@string/queryData"
    />
    <Button 
    	android:id="@+id/contentprovider_update_btn"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:text="@string/updateData"
    />
    <Button 
    	android:id="@+id/contentprovider_delete_btn"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:text="@string/deleteData"
    />
    
</LinearLayout>


最后在AndroidManifest.xml中注册时这个ContentProvider。

 

 

<provider android:name="com.and.test.contentprovider.MyContentProvider" 
				  android:authorities="com.and.test.contentprovider.MyContentProvider">
		</provider>

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值