ContentProvider数据共享(附源码)

ContentProvider是Android四大组件之一,其重要性就不言而喻了。在学习Android的过程中,个人认为这个组件比另外3个组件Activity,Service,Broadcastreceiver更难上手,这篇文章也是参考了诸多的资料和分析代码而对ContentProvider有了自己的认识和理解,本文中如有理解错误和不对的地方,大家提出来一起讨论。

 

首先什么是ContentProvider?这个ContentProvider又是用来干什么的?

ContentProvider也叫内容提供者,说白了就是将自己程序的一些数据共享出来给其他应用程序读写,也可以给自己程序的其他组件(如:Activity)来读写。

 

有朋友可能会问了,为什么不能把一个程序中单独的Sqlite(.db文件)和SharePreference(.xml文件)直接共享出来给其他程序用了?

其实使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用SharedPreferences共享数据,需要使用SharedPreferencesAPI读写数据等(这些方法我暂时也没掌握)。而使用ContentProvider共享数据的好处是统一了数据访问方式。

 

下面一边看代码一边讲解

先写一个提供数据共享的应用程序,本章需要先了解Sqlite相关知识,还不了解的朋友可以参考下面给的连接

扫描二维码关注公众号,回复: 11120535 查看本文章

首先还是像之前Android数据库基本操作中那样,写一个类继承SQLiteOpenHelper,并在OnCreate方法中创建表。

 DbHelper

package com.huahuadashen.mycontentprovideractivity;

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

public class DbHelper extends SQLiteOpenHelper{

	public DbHelper(Context context, String name, CursorFactory factory,
			int version) {
		super(context, name, factory, version);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		// TODO Auto-generated method stub
		String sql = "CREATE TABLE dota (_id INTEGER PRIMARY KEY AUTOINCREMENT, heroname VARCHAR,description VARCHAR)";
		db.execSQL(sql);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub
		
	}

}

接着我们就写本文最关键的类MyContentProvider,此类继承ContentProvider

定义一个UriMatcher类

private static final UriMatcher MATCHER = new UriMatcher(
			UriMatcher.NO_MATCH);
	private static final int DOTAS = 1;
	private static final int DOTA = 2;
	static {
		MATCHER.addURI("com.huahuadashen.mycontentprovideractivity.mycontentprovider", "dota", DOTAS);
		MATCHER.addURI("com.huahuadashen.mycontentprovideractivity.mycontentprovider", "dota/#", DOTA);
	}


好,这里我们先理解Uri,UriMatcher,ContentUris3个类的概念与使用方法

什么是Uri?

 Uri代表了要操作的数据,Uri主要包含了两部分信息:

1.需要操作的ContentProvider 

上面代码中要操作的就是"com.huahuadashen.mycontentprovideractivity.mycontentprovider"

固定标识(Authority)为 "包名.定义的类名"


2.对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:

1)scheme:ContentProvider(内容提供者)的scheme已经由Android所规定为:content://。
2)标识(Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它,然后去读写数据。
3)路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
要操作dota表中id为10的记录,可以构建这样的路径:/dota/10
要操作dota表中id为10的记录的heroname字段,可以构建这样的路径:dota/10/heroname
要操作dota表中的所有记录,可以构建这样的路径:/dota
要操作的数据不一定来自数据库,也可以是文件等他存储方式,如下:
要操作xml文件中dota节点下的heroname节点,可以构建这样的路径:/dota/heroname
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri uri = Uri.parse("content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota")


什么是UriMatcher和ContentUris

 我们很经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。掌握它们的使用,会便于我们的开发工作。

UriMatcher:用于匹配Uri,它的用法如下:
1.首先把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
UriMatcher  uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

//添加需要匹配uri,如果匹配就会返回匹配码

UriMatcher.addURI(“com.huahuadashen.mycontentprovideractivity.mycontentprovider”, “dota”, 1);

UriMatcher.addURI(“com.huahuadashen.mycontentprovideractivity.mycontentprovider”, “dota/#”, 2);//#号为通配符

           
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数

//如果用UriMatcher.match()方法匹配

content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota路径,返回匹配码为1      
//如果UriMatcher.match()方法匹配content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota/10路径,返回匹配码为2


ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分
parseId(uri)方法用于从路径中获取ID部分


这里参考了contentprovider的学习实例总结的博文


直接贴MyContentProvider的代码

package com.huahuadashen.mycontentprovideractivity;

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 MyContentProvider extends ContentProvider{
	private DbHelper dbHelper;
	
	// 定义一个UriMatcher类
	private static final UriMatcher MATCHER = new UriMatcher(
			UriMatcher.NO_MATCH);
	private static final int DOTAS = 1;
	private static final int DOTA = 2;
	static {
		MATCHER.addURI("com.huahuadashen.mycontentprovideractivity.mycontentprovider", "dota", DOTAS);
		MATCHER.addURI("com.huahuadashen.mycontentprovideractivity.mycontentprovider", "dota/#", DOTA);
	}
	
	// 删除数据
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int count = 0;
		switch (MATCHER.match(uri)) {
		case DOTAS:
			count = db.delete("dota", selection, selectionArgs);
			return count;

		case DOTA:
			long id = ContentUris.parseId(uri);
			String where = "_id=" + id;
			if (selection != null && !"".equals(selection)) {
				where = selection + " and " + where;
			}
			count = db.delete("dota", where, selectionArgs);
			return count;

		default:
			throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
		}
	}

	// 返回当前操作的数据的mimeType
	@Override
	public String getType(Uri uri) {
		switch (MATCHER.match(uri)) {
		case DOTAS:
			return "vnd.android.cursor.dir/dota";
		case DOTA:
			return "vnd.android.cursor.item/dota";
		default:
			throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
		}
	}
	
	// 插入数据
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		Uri insertUri = null;
		switch (MATCHER.match(uri)) {
		case DOTAS:
			long rowid = db.insert("dota", "", values);
			insertUri = ContentUris.withAppendedId(uri, rowid);
			break;
			
		default:
			throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
		}
		return insertUri;
	}
	
	//在ContentProvider创建的时候执行 
	@Override
	public boolean onCreate() {
		dbHelper = new DbHelper(this.getContext(),"huahuadashen.db",null,1);
		return false;
	}

	// 查询数据
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		switch (MATCHER.match(uri)) {
		case DOTAS:
			// 查询所有的数据
			return db.query("dota", projection, selection, selectionArgs,
					null, null, sortOrder);

		case DOTA:
			// 查询某个ID的数据
			// 通过ContentUris这个工具类解释出ID
			long id = ContentUris.parseId(uri);
			String where = " _id=" + id;
			if (!"".equals(selection) && selection != null) {
				where = selection + " and " + where;

			}
			return db.query("dota", projection, where, selectionArgs, null,
					null, sortOrder);
			
		default:
			throw new IllegalArgumentException("unknow uri" + uri.toString());
		}

	}
	
	// 更新数据
	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int count = 0;
		switch (MATCHER.match(uri)) {
		case DOTAS:
			count = db.update("dota", values, selection, selectionArgs);
			break;
		case DOTA:
			// 通过ContentUri工具类得到ID
			long id = ContentUris.parseId(uri);
			String where = "_id=" + id;
			if (selection != null && !"".equals(selection)) {
				where = selection + " and " + where;
			}
			count = db.update("dota", values, where, selectionArgs);
			break;
		default:
			throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
		}
		return count;
	}

}

MyContentProvider需要重写ContentProvider的6个方法,然后我们可以在自己的程序里通过Activity往数据库写入一些数据

package com.huahuadashen.mycontentprovideractivity;

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

public class MainActivity extends Activity {
	private Button btn1;
	private Button btn2;
	private Button btn3;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		btn1 = (Button)findViewById(R.id.btn1);
		btn1.setOnClickListener(new BtnClick());
		btn2 = (Button)findViewById(R.id.btn2);
		btn2.setOnClickListener(new BtnClick());
		btn3 = (Button)findViewById(R.id.btn3);
		btn3.setOnClickListener(new BtnClick());
		btn3.setVisibility(View.GONE);
	}
	
	private class BtnClick implements View.OnClickListener{

		@Override
		public void onClick(View v) {
			//增加数据
			if(v.getId() == R.id.btn1)
			{
				ContentResolver cr = MainActivity.this.getContentResolver();

				Uri uri = Uri.parse("content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota");
				ContentValues values = new ContentValues();
				values.put("heroname", "幽鬼");
				values.put("description", "1号位");
				Uri result = cr.insert(uri, values);

				if (ContentUris.parseId(result)>0) {
					Toast.makeText(MainActivity.this, "增加成功", Toast.LENGTH_LONG).show();
				}
			}
			//查询数据
			else if(v.getId() == R.id.btn2)
			{
				ContentResolver cr = MainActivity.this.getContentResolver();
				Uri uri = Uri.parse("content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota");
				
				Cursor c = cr.query(uri,null, null, null, null);
				
				//循环显示
				for(c.moveToFirst();!c.isAfterLast();c.moveToNext()){
					Toast.makeText(MainActivity.this,
							"第"+c.getInt(0)+"条记录,英雄是"+c.getString(1)+",位置是"+c.getString(2),
							Toast.LENGTH_SHORT).show();
				}
				
				c.close();
			}
			//清空数据
			else if(v.getId() == R.id.btn3)
			{
				ContentResolver cr = MainActivity.this.getContentResolver();
				Uri uri = Uri.parse("content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota");
				
				cr.delete(uri, null, null);
			}
			
		}
		
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}


可以看到有一个新类ContentResolver,这个是干嘛的呢?

当外部应用程序或者内部应用程序的其他组件(如Activity)需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,就要使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver使用insert、delete、update、query方法,来操作数据

既然是这样,其实我们在操作ContentProvider提供的数据时,仅仅只用得到ContentResolver 对象,然后知道需要操作ContentProvider的Uri就可以了.


我们知道ContentProvider是4大组件之一,那么他一样一定要在AndroidManifest.xml添加声明才行

        <provider
            android:authorities="com.huahuadashen.mycontentprovideractivity.mycontentprovider"
            android:name=".MyContentProvider" 

            android:exported="true">//这句很关键,表示可以被其他应用程序读写
        </provider>


第一个程序就写好了,实现的功能:

1,提供了一个ContentProvider可以供其他程序去读写数据

2,程序内部组件Activity通过ContentResolver向数据库中写入数据和查询数据


下面实现通过外部应用来访问修改上面的数据库,就只有一个acticity

package com.huahua.readprovider;


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

public class MainActivity extends Activity {
	private Button btn1;
	private Button btn2;
	private Button btn3;
	private Button btn4;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		btn1 = (Button)findViewById(R.id.btn1);
		btn1.setOnClickListener(new BtnClick());
		btn2 = (Button)findViewById(R.id.btn2);
		btn2.setOnClickListener(new BtnClick());
		btn3 = (Button)findViewById(R.id.btn3);
		btn3.setOnClickListener(new BtnClick());
		btn4 = (Button)findViewById(R.id.btn4);
		btn4.setOnClickListener(new BtnClick());
	}
	
	private class BtnClick implements View.OnClickListener{

		@Override
		public void onClick(View v) {
			//增加数据
			if(v.getId() == R.id.btn1)
			{
				ContentResolver cr = MainActivity.this.getContentResolver();

				Uri uri = Uri.parse("content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota");
				ContentValues values = new ContentValues();
				values.put("heroname", "修补匠");
				values.put("description", "2号位");
				Uri result = cr.insert(uri, values);
				
				if (ContentUris.parseId(result)>0) {
					Toast.makeText(MainActivity.this, "增加成功", Toast.LENGTH_SHORT).show();
				}
			}
			//查询数据
			else if(v.getId() == R.id.btn2)
			{
				ContentResolver cr = MainActivity.this.getContentResolver();
				Uri uri = Uri.parse("content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota");
				
				Cursor c = cr.query(uri,null, null, null, null);
				
				//循环显示
				for(c.moveToFirst();!c.isAfterLast();c.moveToNext()){
					Toast.makeText(MainActivity.this,
							"第"+c.getInt(0)+"条记录,英雄是"+c.getString(1)+",位置是"+c.getString(2),
							Toast.LENGTH_SHORT).show();
				}
				
				c.close();
			}
			//删除数据
			else if(v.getId() == R.id.btn3)
			{
				ContentResolver cr = MainActivity.this.getContentResolver();
				//这里删除的是表中第一行数据
				Uri uri = Uri.parse("content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota/1");
				
				int result =cr.delete(uri, null, null);
				
				if (result >= 1) {
					Toast.makeText(MainActivity.this, "删除成功", Toast.LENGTH_LONG).show();
				}else{
					Toast.makeText(MainActivity.this, "这行数据不存在了!", Toast.LENGTH_LONG).show();
				}
			}
			//更新数据
			else if(v.getId() == R.id.btn4)
			{
				ContentResolver cr = MainActivity.this.getContentResolver();
				//这里更改的是表中第二行数据
				Uri uri = Uri.parse("content://com.huahuadashen.mycontentprovideractivity.mycontentprovider/dota/2");
				
				ContentValues values = new ContentValues();
				values.put("heroname", "随机英雄");
				values.put("description","6号位");
				int result = cr.update(uri, values, null, null);
				if (result >= 1) {
					Toast.makeText(MainActivity.this, "更新成功", Toast.LENGTH_SHORT).show();
				}else{
					Toast.makeText(MainActivity.this, "这行数据不存在了!", Toast.LENGTH_LONG).show();
				}
			}
		}
		
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

实现4个功能:

1,往数据库添加数据,

2,查询数据库中内容

3,修改数据库某一行的数据

4,删除数据库某一行的数据

增,删,改,查方法跟之前的activity一样.


下面是源码的下载地址

ContentProvider源码

 

发布了21 篇原创文章 · 获赞 8 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/zhangxuebing2/article/details/12624559