使用ContentProvider 实现数据共享
2014-12-01 20:01
597 查看
使用ContentProvider 实现数据共享
一 ContentProvider简介
ContentProvider(数据提供者)是在应用程序间共享数据的一种接口机制。 它提供了更为高级的数据共享方法,应用程序可以指定需要共享的数据,而其他应用程序则可以在不知数据来源、路径的情况下,对共享数据进行查询、添加、删除和更新等操作。许多Android系统的内置数据也通过ContentProvider提供给用户使用,例如通讯录、音视频文件和图像文件等。在创建ContentProvider时,需要首先使用数据库、文件系统或网络实现底层存储功能,然后在继承ContentProvider的类中实现基本数据操作的接口函数,包括添加、删除、查找和更新等功能。调用者不能够直接调用ContentProvider的接口函数,而是通过使用ContentResolver对象,使用URI间接调用ContentProvider。
使用ContentProvider可以在不同的应用程序之间共享数据。 它为存储和获取数据提供了统一的接口;ContentProvide对数据进行封装,不用关心数据存储的细节。ContentProvider不管底层数据的实际存储方式,对外统一使用表的形式来组织数据 。
二 Uri简介
由于ContentProvider的使用,离不开URI,因此单独找一个小节,介绍一下URI。Android平台,URI主要分三个部分:scheme, authority and path。其中authority又分为host和port。
格式如下:
scheme://host:port/path
举个实际的例子:
content://com.example.project:200/folder/subfolder/etc
\---------/ \---------------------------/ \---/ \--------------------------/
scheme host port path
\--------------------------------/
authority
其中,content:// 这个部分是android的contentprovider 规定的,就像上网的协议默认是http:// 一样。
com.example.project:200 这个部分就是contentProvider的authority。系统就是由这个部分摘到要操作哪一个contentProvider。
folder/subfolder/etc 资源部分,当需要访问不同的资源时,这个部分是动态改变的。
我们在程序中一般是不直接用URI来标识contentProvider的;我们通常见到的用定义的常量来标识。例如standard contentProvider中的Contacts,我们就用Contacts.People.CONTENT_URI来标识Contacts contentProvider中People这个表。那么要标识某个具体的人怎么办呢? 这就用到了ContentUris.withAppendedId() 和 Uri.withAppendedPath()。例如我们要表示content://contacts/people/20,那么我们就可以用如下语句:
Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, 20); 或者
Uri uri = Uri.withAppendedPath(People.CONTENT_URI, "20");
三 开发ContentProvider
1. ContentProvider与 ContentResolver的关系
从ContentProvider与 ContentResolver, url 三者的关系看;uri 是 ContentProvider与 ContentResolver 进行数据交换的标示。ContentResolver 通过uri 操作数据会委托给相应的ContentProvider 来实现。2. 开发ContentProvider
开发ContentProider 只需要两步:(1)开发ContentProider的子类,该子类需要实现增删改查的方法。
(2)在AndroidManifest.java 文件中注册该 ContentProider,指定 android:authorities 属性。
注意:上面实现的ContentProider 的 query() , insert(), update(), delete() 方法,不是给应用本身使用的,而是给其他应用使用的。至于这四个方法如何实现,完全由开发者本身决定。一般是数据库。
3.配置 ContentProider
android 要求必须为 四大组件显示配置(activity,service ,ContentProider,BroadcastReceiver)。ContentProider 的配置一般如下:
<!-- 注册contentProvider --> <provider android:name=".InfoProvider" android:authorities="com.example.infos.infoprovider" android:exported="true" />
通常的属性如下:
name: 指定该 ContentProider 的实现类的类名;
authorities: 指定该ContentProider 对应的uri,
exported:指定该 ContentProider 是否允许其他应用调用。一般设置为true。
4. ContentResolver 的使用介绍
Context 提供了 getContentResolver() 方法,这表示 activity 以及 service 等组件都可以通过getContentResolver() 方法获取ContentResolver。获取了ContentResolver 之后,就可以调用ContentResolver 的 query() , insert(), update(), delete() 方法了----实际上是调用的 对应的ContentProider 的query() , insert(), update(), delete() 方法。
5. 创建 ContentProider 的注意事项
因为经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris。UriMatcher:用于匹配Uri,它的用法如下:
1.首先把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.ex.sqlite.provider.contactprovider/contact,返回匹配码为1
uriMatcher.addURI(“com.ex.sqlite.provider.contactprovider”, “contact”, 1);
//如果match()方法匹配 content://com.ex.sqlite.provider.contactprovider/contact/12,返回匹配码为2
uriMatcher.addURI(“com.ex.sqlite.provider.contactprovider”, “contact/#”, 2); //其中#号为通配符
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,
如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,
ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
(1)withAppendedId(uri, id)用于为路径加上ID部分
(2)parseId(uri)方法用于从路径中获取ID部分
四 示例ContentProider 的使用
我们这里设计了一个记录个人信息的ContentProider,可以通过其他的进程来添加个人信息,包括姓名,年龄。也可以通过名字来查询当前姓名的年龄。
1. 设计了一个工具类,用来把 ContentProider 的 Uri,以及数据列等信息以常量的形式公布出来,便于访问。其实它的作用就是告诉其他的应用程序怎么访问该ContentProider。
public class Infos { //define the authority of the contentProvider public static final String AUTHORITY= "com.example.infos.infoprovider"; //define the inner class ,which contents the data and name of columns public static final class Info implements BaseColumns{ //define the name of columns that will be operate public static final String _ID = "id"; public static final String NAME = "name"; public static final String AGE = "age"; public static final Uri INFOS_URI = Uri.parse( "content://" + AUTHORITY + "/infos"); public static final Uri INFO_URI = Uri.parse( "content://" + AUTHORITY + "/info"); } }
2.接下来,设计了一个 ContentProider 的子类,重写它的增删改查方法,代码如下:
public class InfoProvider extends ContentProvider { private static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); private InfoDatabaseHelper mInfoDatabaseHelper = null; private static final int NAMES = 1; private static final int NAME = 2; static{ //为uriMatcher 注册两个uri mUriMatcher.addURI(Infos.AUTHORITY, "/infos", 1); mUriMatcher.addURI(Infos.AUTHORITY, "/info/#", 2); } @Override public int delete(Uri uri, String where, String[] whereArgs) { SQLiteDatabase db = mInfoDatabaseHelper.getWritableDatabase(); //记录删除的数目 int num = 0; switch (mUriMatcher.match(uri)) { case NAMES: num = db.delete("info", where, whereArgs); break; case NAME: long id = ContentUris.parseId(uri); String wheretemp = Infos.Info._ID + id; if (where != null && "".equals(where)) { where = wheretemp + " and " + where; } num = db.delete("info", where, whereArgs); default: break; } getContext().getContentResolver().notifyChange(uri, null); return num; } @Override public String getType(Uri arg0) { // TODO Auto-generated method stub return null; } @Override public Uri insert(Uri uri, ContentValues contentValues) { SQLiteDatabase db = mInfoDatabaseHelper.getReadableDatabase(); switch (mUriMatcher.match(uri)) { case NAMES: long id = db.insert("info", Info._ID, contentValues); //如果插入成功,返回uri if (id > 0) { //在已有的uri后面添加id Uri infoUri = ContentUris.withAppendedId(uri, id); getContext().getContentResolver().notifyChange(infoUri, null); return infoUri; } break; default: break; } return null; } @Override public boolean onCreate() { mInfoDatabaseHelper = new InfoDatabaseHelper(this.getContext(), "peopleinfo.db", null, 1); if (mInfoDatabaseHelper != null) { return true; } return false; } @Override public Cursor query(Uri uri, String[] projection, String where, String[] whereArgs, String sortorder) { SQLiteDatabase db = mInfoDatabaseHelper.getReadableDatabase(); switch (mUriMatcher.match(uri)) { case NAMES: return db.query("info", projection, where, whereArgs, null, null, sortorder); case NAME: //解析出想要查询的ID long id = ContentUris.parseId(uri); String wheretemp = Infos.Info._ID + id; if (where != null && "".equals(where)) { where = wheretemp + " and " + where; } return db.query("info", projection, where, whereArgs, null, null, sortorder); default: break; } return null; } @Override public int update(Uri uri, ContentValues contentValues, String where, String[] whereArgs) { SQLiteDatabase db = mInfoDatabaseHelper.getReadableDatabase(); //记录修改的数目 int num =0; switch (mUriMatcher.match(uri)) { case NAMES: num = db.update("info", contentValues, where, whereArgs); case NAME: //解析出想要查询的ID long id = ContentUris.parseId(uri); String wheretemp = Infos.Info._ID + id; if (where != null && "".equals(where)) { where = wheretemp + " and " + where; } num = db.update("info", contentValues, where, whereArgs); break; default: break; } // notify the data has been changed getContext().getContentResolver().notifyChange(uri, null); return num; } }
它里面用到的 InfoDatabaseHelper 类如下:
public class InfoDatabaseHelper extends SQLiteOpenHelper { public InfoDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } final String CREATE_TABLE_SQL = "create table info(_id integer primary key autoincrement ,name , age)"; @Override public void onCreate(SQLiteDatabase db) { // 创建数据库 db.execSQL(CREATE_TABLE_SQL); } @Override public void onUpgrade(SQLiteDatabase arg0, int oldVersion, int newVersion) { System.out.println("--------onUpdate Called--------" + oldVersion + "--->" + newVersion); } }
上面的InfoProvider 类很简单,它除了继承 ContentProvider 之外,还实现了增删改查方法。当其他的应用通过 ContentResolver 来调用InfoProvider 的这四个方法的时候,就可以真正的访问这里面创建的数据库了,从而达到对个人信息的处理。
3.
接下来,注册 InfoProvider,片段如下:
<provider android:name=".InfoProvider" android:authorities="com.example.infos.infoprovider" android:exported="true" />
4. 为了测试这个 InfoProvider 可以使用,我们编写了一个简单的测试demo:
这个测试demo 可以添加个人信息,也可以查询个人信息,而且都是通过前面我们创建的 InfoProvider 来完成的。
代码如下:
public class MainActivity extends Activity { Button insert = null; Button search = null; ContentResolver mContentResolver = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); insert = (Button)findViewById(R.id.insert); search = (Button)findViewById(R.id.search); mContentResolver = getContentResolver(); insert.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { String name = ((EditText)findViewById(R.id.name)).getText().toString(); String age = ((EditText)findViewById(R.id.age)).getText().toString(); ContentValues contentValues = new ContentValues(); contentValues.put(Info.NAME, name); contentValues.put(Info.AGE, age); Uri uri = mContentResolver.insert(Infos.Info.INFOS_URI, contentValues); if (uri != null) { Toast.makeText(getApplicationContext(), uri.toString(), Toast.LENGTH_LONG).show(); } } }); search.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { String searchString = ((EditText)findViewById(R.id.key)).getText().toString(); String selection = "name = ?"; String[] selectionArgs = {searchString}; Cursor cursor = mContentResolver.query(Infos.Info.INFOS_URI, null, selection, selectionArgs, null); ArrayList<Map<String, String>> infoList = convertCursorToList(cursor); //创建一个Bundle Bundle bundle = new Bundle(); //bundle.putStringArrayList("data", infoList); bundle.putSerializable("data", infoList); //启动显示的activity Intent intent = new Intent(getApplicationContext(), ResultActivity.class); intent.putExtras(bundle); startActivity(intent); } }); } private ArrayList<Map<String, String>> convertCursorToList(Cursor cursor) { ArrayList<Map<String, String>> result = new ArrayList<Map<String,String>>(); if (cursor == null) { return result; } while (cursor.moveToNext()) { //set the info to the list Map<String, String> maptmp = new HashMap<String, String>(); maptmp.put(Info.NAME, cursor.getString(1)); maptmp.put(Info.AGE, cursor.getString(2)); result.add(maptmp); } return result; } @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; } }
如果是查询的话,我们会新创建一个actiivty,用来显示查询到的数据,activity实现如下:
public class ResultActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.popup); ListView listView = (ListView) findViewById(R.id.show); Intent intent = getIntent(); Bundle data = intent.getExtras(); @SuppressWarnings("unchecked") List<Map<String, String>> list = (List<Map<String, String>>) data.getSerializable("data"); SimpleAdapter adapter = new SimpleAdapter(ResultActivity.this, list, R.layout.line , new String[] { Infos.Info.NAME, Infos.Info.AGE } , new int[] { R.id.name, R.id.age }); listView.setAdapter(adapter); } }
这个简单的demo能够实现 个人信息(姓名,年龄的添加和查询),说明了ContenterProvider 的一般使用方法和注意事项。
五总结
简单的理解, ContenterProvider 就是android里面实现不同进程间数据共享的一种方法,为app开发者提供了一种开发的行为规范和标准。严格按照这个规则开发,就能够设计出简单明了的应用程序。
相关文章推荐
- Android学习之 使用ContentProvider实现数据共享
- Android疑惑之使用ContentProvider实现数据共享
- 第9章 使用ContentProvider实现数据共享
- Android学习笔记十七.使用ContentProvider实现数据共享(四).操作系统(联系人)的ContentProvider
- 使用ContentProvider实现数据共享
- Android四大组件之使用ContentProvider实现数据共享
- 使用ContentProvider实现数据共享
- 第九章使用ContentProvider实现数据共享
- Android学习笔记十六.使用ContentProvider实现数据共享(二).URI...工具类
- 使用ContentProvider实现数据共享
- Android开发学习之使用ContentProvider实现数据共享
- Android学习19--使用ContentProvider实现数据共享
- Android学习笔记十五.使用ContentProvider实现数据共享(一)
- Android学习笔记十八.使用ContentProvider实现数据共享(五).监听ContentProvider的数据改变
- 使用ContentProvider实现数据共享
- Android之使用Contentprovider对外共享数据和实现数据监听变化
- 使用contentProvider实现数据共享
- Android疑惑之使用ContentProvider实现数据共享
- 使用ContentProvider和ContentResolver实现应用程序间的数据共享
- 使用共享变量实现主报表及子报表之间的数据共享