ContentProvider数据共享(附源码)

来源:互联网 发布:种族主义 知乎 编辑:程序博客网 时间:2024/06/01 07:40

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

 

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

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

 

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

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

 

下面一边看代码一边讲解

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

首先还是像之前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}@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stubString sql = "CREATE TABLE dota (_id INTEGER PRIMARY KEY AUTOINCREMENT, heroname VARCHAR,description VARCHAR)";db.execSQL(sql);}@Overridepublic 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);}// 删除数据@Overridepublic 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@Overridepublic 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());}}// 插入数据@Overridepublic 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创建的时候执行 @Overridepublic boolean onCreate() {dbHelper = new DbHelper(this.getContext(),"huahuadashen.db",null,1);return false;}// 查询数据@Overridepublic 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这个工具类解释出IDlong 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());}}// 更新数据@Overridepublic 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工具类得到IDlong 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;@Overrideprotected 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{@Overridepublic 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);}}}@Overridepublic 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;@Overrideprotected 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{@Overridepublic 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();}}}}@Overridepublic 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源码

 

原创粉丝点击