使用Content Providers方式共享数据

Content Providers简介

  在Android系统中,不存在一个公共的数据存储区供所有的应用程序访问,也就是说数据在各个应用程序中是私有的。那么,如何在一个应用程序中访问另一个应用程序中的数据,实现应用程序之间的数据共享(甚至擦写)呢?

  当然,你可以通过传统的文件读写方式,让别的应用程序可以读写该应用程序中的文件。但是,使用这种方式的弊端也是显而易见的,不仅需要知道该文件的存储路径,而且会将该文件内容完全的暴露出去,对于内容提供者和内容访问者来说都是不方便和不安全的。

  为此,Android系统提供了Content Providers,用以方便安全的实现应用程序间的数据共享。

  本人在编写本文时发现,网上其他众多讲述Content Provider的博客,都是先讲如何获取Content Provider内容,再讲如何提供属于自己的Content Provider,而在讲述获取内容方法所用的是Android联系人的例子。我在学习的时候发现这样非常的吃力,主要是因为不知道内容的提供形式(即内容是如何进行提供的)。当然每个人都会有自己的学习方法与理解方式,本人完全不对任何人的讲解方式持任何态度,因此我提倡浏览多个博客进行学习,网上许多大神的博文水平会让你大吃一惊。同时,不要局限于某一个人或者博客所传达的思维方式,这是作为一名优秀的开发者所绝不允许出现的情况。


ContentResolver

  所有的Content Providers都会实现一些共同的接口,包括数据的查询、添加、更改和删除。在应用程序中,我们可以通过使用getContent Resolver()方法来取得一个ContentResolver对象,然后就可以通过这个ContentResolver对象来操作你需要的Content Provider了。ContentResolver类提供的用来操作Content Provider的方法主要有insert()、delete()、update()和query()。

  通常,对于开发者而言,并不需要同ContentProvider对象直接打交道。Android系统运行时,会在底层将所有的ContentProvider对象实例化,每一种类型的ContentProvider只有一个实例。这个实例可以与在不同的程序或进程中的多个ContentResolver对象进行通信。而这些进程间的交互则是由ContentResolver和ContentProvider类进行处理的。简而言之,ContentProvider在Android启动之时已经准备好,开发者只需要自行创建ContentResolver,然后通过URI指定该ContentResolver所要操作的ContentProvider实例就可以了。

  想要操作ContentProviders,最重要的就是得知该ContentProvider的数据存储结构和URI。


原生providers与URI

  每一个Content Provider都对外提供一个能够唯一标识自己数据集的公开URI,如果一个Content Provider管理多个数据集,则需要为每一个数据集都分配一个独立的URI(暂不考虑这种复杂情况)。Android提供一些原生的content provider来访问系统中的视频、图像和联系人信息等等。例如联系人的URI为“content://com.android.contacts/contacts”(旧版的API不是这个),原生providers所提供的URI一般都有在ContactsContract这个类中定义,例如联系人的URI为ContactsContract.Contacts.CONTENT_URI。由于系统不同版本可能会存在差异,我们应尽可能使用系统的定义(如同上面联系人的URI,若你直接用字符串,那么新版与旧版之间就不兼容了)。另外ContactsContract提供的不仅是URI,还包括所提供的数据结构的列名等等,都能够在该类中获得。

  在android.provider这个包中,可以查看原生的provider以及获取URI,例如有AlarmClock、Browser、CalendarContract、CallLog、ContactsContract(包括有Contacts,Groups,PhoneLookup等)、MediaStore(Audio 『Albums、Artists、Genres、Playlists』、Files、Images、Video)和Setting。

  Android规定所有Content Provider的URI都必须遵循以下命名规则。以“content://com.android.contacts/contacts”为例,URI由3部分组成:

  part 1scheme部分,即“content://”,表明这是个content的Uri,而不是一个http://的网络Uri;

  part 2工程包名,如“com.android.contacts”,用于表明这个providers是谁提供的。该部分必须小写;

  part 3标识ID(可选,通常建议必选),一个应用程序可能提供两个或以上的providers,标识ID用于区分。同时,part 2与part 3之间用“/”连接。


提供自己的Content Provider内容

1、建立数据的存储系统

  很显然,要将自己应用程序中的数据共享给他人,肯定需要建立自己的数据存储系统。当然了,选择什么样的存储系统(文件存储系统、SQLite数据库等)完全由开发者决定。在这里,我们重用之前在学习《使用SQLite储存数据》(http://blog.csdn.net/theworldsong/article/details/9259225)中操纵SQLite数据库的代码。我们将通过Content Provider封装SQLite的数据访问接口,允许其他应用通过Uri与这个Content Provider建立连接以访问或修改数据。

2、扩展Content Provider类

  在该工程中,我们需要新建了一个继承自ContentProvider的类,并实现其中所需要的抽象方法。我们将这个类命名为“MyContentProvider”。

  在ContentProvider类中提供了6个抽象方法,分别为:

  (1)public abstract Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);

  (2)public abstract Uri insert (Uri uri, ContentValues values);

  (3)public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs);

  (4)public abstract int delete (Uri uri, String selection, String[] selectionArgs);

  (5)public abstract String getType (Uri uri);

  (6)public abstract boolean onCreate ();

  其中,query()方法用于将查询到的数据以Cursor对象的形式返回;insert()方法用于向Content Provider中插入新数据记录,该方法中的第二个参数ContentValues对象表示数据记录的列名和列值的映射;update()方法用于更新Content Provider中的已存在的数据记录;delete()方法用于从Content Provider中删除数据记录;getType()方法用于返回Content Provider中数据的(MIME)类型;onCreate()方法当Content Provider启动时被调用(该方法不需要手动调用)。

  以上的第(1)到(5)个方法将会在ContentResolver对象中被调用,所以很好的实现这些抽象方法就会为ContentResolver提供一个完善的外部接口。

  当然了,你可以根据自己应用程序的需要,有选择的实现上述6个方法,比如,你可以只实现query()方法,这样别的应用程序就自能对你提供的Content Provider进行查询操作,而无法对你提供的Content Provider进行添加、删除等操作,从而保证了数据的安全性。因为在Content Resolver中,那些想要调用Content Provider的方法,全部都是固定的参数,而所对应的Content Provider方法中,参数也是一一对应的。参数传到了Content Provider,此时Content Provider即拥有了所有参数的控制权。因此即使其他开发者调用了Content Resolver类中的insert (Uri uri, ContentValues values)方法,参数传到了ContentProvider中的insert (Uri uri, ContentValues values)并接受了参数,但是该方法是空的,那么结果是什么也执行不了。

  注意,你所继承下来的ContentProvider必须是public类型的,因为需要提供外部访问。因此,你可能要另外开辟一个新的java文件。本程序将MainActivity单独作为一个java文件,另有一个名为MyContentProvider.java的文件,将类MyContentProvider设为public,同时构建数据库的类Chapter22Db也存放于该文件下。此工程名为com.plusjun.hello23。详见代码:

  MainActivity.java

package com.plusjun.hello23;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
}
  MyContentProvider.java

package com.plusjun.hello23;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.hardware.SensorManager;
import android.net.Uri;

/*
 * 这个MyContentProvider必须是public的
 * 因为需要提供外部访问的权限
 * 如果不是public程序会出错
 */
public class MyContentProvider extends ContentProvider{

	SQLiteDatabase db = null;
	String TABLE_NAME = "mytable";

	/*
	 * 该方法会在MyContentProvider初始化的时候被调用
	 * 不需要手动调用
	 */
	public boolean onCreate() {
		//准备好数据库读写
		db = (new Chapter22Db (getContext())).getWritableDatabase();
		return false;
	}

	//实现query方法
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		Cursor cursor = db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
		return cursor;
	}

	/*
	 * 实现getType方法。这里就不去管它了
	 * 有兴趣的自行研究
	 */
	public String getType(Uri uri) {

		return null;
	}

	//实现insert方法
	public Uri insert(Uri uri, ContentValues values) {
		db.insert(TABLE_NAME, null, values);
		return null;
	}

	//实现delete方法
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		db.delete(TABLE_NAME, selection, selectionArgs);
		return 0;
	}

	//实现update方法
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		db.update(TABLE_NAME, values, selection, selectionArgs);
		return 0;
	}
}

/*
 * 数据库的构建方式重用
 * http://blog.csdn.net/theworldsong/article/details/9259225
 * 中的代码
 */
class Chapter22Db extends SQLiteOpenHelper{
	public static final String DATABASE_BAME ="bebook_db";
	public Chapter22Db(Context context){
		super(context,DATABASE_BAME,null,2);
	}
	
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("CREATE TABLE mytable(_id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT,Weight REAL); ");
		ContentValues cv = new ContentValues();
		cv.put("Name", "Gravity, Earth");
		cv.put("Weight", SensorManager.GRAVITY_EARTH);
		db.insert("mytable", null, cv);
		cv.put("Name", "Gravity, Mars");
		cv.put("Weight", SensorManager.GRAVITY_MARS);
		db.insert("mytable", null, cv);
		cv.put("Name", "Gravity, Moon");
		cv.put("Weight", SensorManager.GRAVITY_MOON);
		db.insert("mytable", null, cv);
	}

	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("DROP TABLE IF EXISTS mytable");
		onCreate(db);
	}
}

3、声明Content Provider的权限

  创建好的Content Provider必须在应用程序的AndroidManifest.xml文件中进行声明,否则,该Content Provider对于Android系统是不可见的。注意,在指定Content Provider的URI的时候,要确保该URI必须是唯一的,不能和系统的URI相同,更不能与其他应用程序提供的Content Provider的URI相同。这里我定义了Content Provider的URI为“content://com.plusjun.hello23/MyContentProvider”。

  具体的声明方式如下:

		<provider android:name="com.plusjun.hello23.MyContentProvider"
			android:authorities="com.plusjun.hello23.mycontentprovider" />

  其中,<provider></provider>标签位于<application></application>标签下。

  android:name属性用于指明MyContentProvider的全称类名;

  android:authorities属性唯一的标识了一个Content Provider,也就是声明url中的Part 2部分。注意,android:authorities部分要小写,也说明了Uri中Part 2部分必须为小写。

  至此,我们便在该工程中创建了自己的Content Provider,


通过Content Provider访问数据

package com.plusjun.hellonew;

import com.plusjun.hellonew.R;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.hardware.SensorManager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	Button bt1,bt2,bt3,bt4;
	TextView tv1;
	ContentResolver contentResolver;
	Uri uri;
	//表中的三个列名
	String[] allStr = {"_id","Name","Weight"};

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		init();

		contentResolver = getContentResolver();
		uri = Uri.parse("content://com.plusjun.hello23.mycontentprovider");

		bt1.setOnClickListener(new OnClickListener() {
			public void onClick(View arg0) {
				ContentValues values = null;
				values.put("Name", "tttttttttttest");
				values.put("Weight", 8899);
				contentResolver.insert(uri, values);
			}
		});
		
		bt2.setOnClickListener(new OnClickListener() {
			public void onClick(View arg0) {
				contentResolver.delete(uri, "Name", new String[]{"Gravity,Earth"});
			}
		});
		
		bt3.setOnClickListener(new OnClickListener() {
			public void onClick(View arg0) {
				Cursor cursor = contentResolver.query(uri, allStr, null, null, null);
				cursor.moveToFirst();
				StringBuffer strb = new StringBuffer("");
				while(!cursor.isAfterLast()){ 
					int id = cursor.getInt(0); 
					String name = cursor.getString(1);
					int weight = cursor.getInt(2);
					strb.append(id);
					strb.append(" ");
					strb.append(name);
					strb.append(" ");
					strb.append(weight);
					strb.append("\n");
					cursor.moveToNext();
				}
				cursor.close();  
				tv1.setText(strb);
			}
		});
		
		bt4.setOnClickListener(new OnClickListener() {
			public void onClick(View arg0) {

			}
		});
	}

	private void init(){
		bt1 = (Button)findViewById(R.id.bt1);
		bt2 = (Button)findViewById(R.id.bt2);
		bt3 = (Button)findViewById(R.id.bt3);
		bt4 = (Button)findViewById(R.id.bt4);
		tv1 = (TextView)findViewById(R.id.tv1);
	}

}

烂尾楼。

只能访问,不能擦写,到现在都不知道是什么问题啊。。。。


以上部分内容转载或参考来源如下:

http://www.cnblogs.com/menlsh/archive/2013/04/17/3027394.html

http://blog.csdn.net/flowingflying/article/details/7497307

http://blog.csdn.net/flowingflying/article/details/7579635

http://blog.csdn.net/flowingflying/article/details/7580505

http://www.eoeandroid.com/forum.php?mod=viewthread&tid=95326

在此表示感谢。
转载请注明来源,版权归原作者所有,未经同意严禁用于任何商业用途。
微博:http://weibo.com/theworldsong
邮箱:theworldsong@foxmail.com

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值