一:contentProvider?(优势和劣势)
优势:
1.android储存数据的方式 sqlite,sharepreferrence,文件形式。其中sqlite是私有的,要想共享数据,你有两个选择:你可以创建你自己的内容提供器(一个ContentProvider子类)或者你可以给已有的提供器添加数据-如果存在一个控制同样类型数据的内容提供器且你拥有写的权限。
2.虽然后两者也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是:统一了数据的访问形式
3. ContentProvider是Android提供的一个贯穿各个模块的通信接口,通信模式。有各种权限机制。可以有更高安全性。 数据访问是通过URI模式访问。返回Cursor数据。这个模式在SearchView,CursorLoader,CursorAdapter,云同步AsyncAdapter等地方用到了。不用ContentProvider这些要手动去实现。
4、ContentObserver模型。当数据发生改变的时候去通知某个URI 该数据发生改变。实时的一个监听效果。
1、如果结构不好。dataBase的数据表项发生改变。需要重写很多代码。(借鉴android系统的方式,比如contactProvider等)每张表单独写出表项。这样重构会减少这个缺点。但是相对代码量比较大。
2、速度会比较慢一点。因为uri机制。getContentResolver()会先去系统查找一遍。这个查询过程是缓慢一点,性能受影响不大。
二:涉及到的类:
1. Uri类简介
Uri代表了要操作的数据,Uri主要包含了两部分信息:1.需要操作的ContentProvider ,2.对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:
1.scheme:ContentProvider(内容提供者)的scheme已经由Android所规定为:content://。
2.主机名(或Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
3.路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
• 要操作contact表中id为10的记录,可以构建这样的路径:/contact/10
• 要操作contact表中id为10的记录的name字段, contact/10/name
• 要操作contact表中的所有记录,可以构建这样的路径:/contact
要操作的数据不一定来自数据库,也可以是文件等他存储方式,如下:
要操作xml文件中contact节点下的name节点,可以构建这样的路径:/contact/name
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")
在Content Provider中使用的查询字符串有别于标准的SQL查询。很多诸如select, add, delete, modify等操作我们都使用一种特殊的URI来进行,这种URI由3个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的ID。以下是一些示例URI:
content://media/internal/images 这个URI将返回设备上存储的所有图片
content://contacts/people/ 这个URI将返回设备上的所有联系人信息
content://contacts/people/45 这个URI返回单个结果(联系人信息中ID为45的联系人记录)
尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android提供一系列的帮助类(在android.provider包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,参见下例:
MediaStore.Images.Media.INTERNAL_CONTENT_URI
Contacts.People.CONTENT_URI
因此,如上面content://contacts/people/45这个URI就可以写成如下形式:
Uri person = ContentUris.withAppendedId(People.CONTENT_URI,
然后执行数据查询:
Cursor cur = managedQuery(person, null, null, null);
2、UriMatcher、ContentUrist和ContentResolver简介
因为Uri代表了要操作的数据,所以我们很经常需要解析Uri,并从Uri中 获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。掌握它们的使用,会便于我们的开发工作。
UriMatcher:用于匹配Uri,它的用法如下:
1.首先把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回的匹配码为1。
ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
• withAppendedId(uri, id)用于为路径加上ID部分
• parseId(uri)方法用于从路径中获取ID部分
ContentResolver:当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver使用insert、delete、update、query方法,来操作数据。
当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:
- public class PersonContentProvider extends ContentProvider{
- public boolean onCreate()
- public Uri insert(Uri uri, ContentValues values)
- public int delete(Uri uri, String selection, String[] selectionArgs)
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
- public String getType(Uri uri)}
第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider ,ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:
- <manifest .... >
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <provider android:name=".PersonContentProvider" android:authorities="cncsdn.provider.personprovider"/>
- </application>
- </manifest>
public booleanonCreate()
该方法在ContentProvider创建后就会被调用,Android开机后, ContentProvider在其它应用第一次访问它时才会被创建。
public Uri insert(Uri uri,ContentValuesvalues)
该方法用于供外部应用往ContentProvider添加数据。
public intdelete(Uri uri,String selection, String[] selectionArgs)
该方法用于供外部应用从ContentProvider删除数据。
public intupdate(Uri uri,ContentValuesvalues, String selection, String[] selectionArgs)
该方法用于供外部应用更新ContentProvider中的数据。
public Cursor query(Uri uri,String[] projection, String selection, String[] selectionArgs,String sortOrder)
该方法用于供外部应用从ContentProvider中获取数据。
public String getType(Uriuri)
该方法用于返回当前Url所代表数据的MIME类型。如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,例如:要得到所有person记录的Uri为content://cn.itcast.provider.personprovider/person,那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,例如:得到id为10的person记录,Uri为content://cn.itcast.provider.personprovider/person/10,那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。
四、使用ContentResolver操作ContentProvider中的数据
当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver 类提供了与ContentProvider类相同签名的四个方法:
public Uri insert(Uri uri,ContentValuesvalues)
该方法用于往ContentProvider添加数据。
public intdelete(Uri uri,String selection, String[] selectionArgs)
该方法用于从ContentProvider删除数据。
public intupdate(Uri uri,ContentValuesvalues, String selection, String[] selectionArgs)
该方法用于更新ContentProvider中的数据。
public Cursor query(Uri uri,String[] projection, String selection, String[] selectionArgs,String sortOrder)
该方法用于从ContentProvider中获取数据。
这些方法的第一个参数为Uri,代表要操作的是哪个ContentProvider和对其中的什么数据进行操作,假设给定的是:Uri.parse(“content://cn.csdn.provider.personprovider/person/10”),那么将会对主机名为cn.itcast.provider.personprovider的ContentProvider进行操作,操作的数据为person表中id为10的记录。
五、监听ContentProvider中数据的变化
如果ContentProvider的访问者需要知道ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下:
- public class PersonContentProviderextends ContentProvider{
- publicUri insert(Uri uri, ContentValuesvalues) {
- db.insert("person","personid",values);
- getContext().getContentResolver().notifyChange(uri,null);
- }
- }
如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:
- getContentResolver().registerContentObserver(Uri.parse("content://cn.itcast.providers.personprovider/person"),
- true,new PersonObserver(newHandler()));
- public class PersonObserverextends ContentObserver{
- publicPersonObserver(Handlerhandler) {
- super(handler);
- }
- publicvoid onChange(booleanselfChange){
- //此处可以进行相应的业务处理
- }
六 例子:
一:创建ContentProvider
以下是UserContentProvider.java的实现代码
- package www.gxw.com.sqlit.provider;
- import www.gxw.com.sqlit.dataBase.DataBaseHelper;
- import android.R.integer;
- 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 UserContentProivder extends ContentProvider {
- private static final String AUTHORITIES = "www.gxw.com.sqlit.provider.usercontentproivder";
- private DataBaseHelper dataBaseHelper;
- private static UriMatcher uriMatcher;
- private static final int USERSCODE = 1;
- private static final int USERCODE = 2;
- private String USERS_DIR = "vnd.android.cursor.dir/";
- private String USERS_ITEM = "vnd.android.cursor.item/";
- static {
- uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- // www.gxw.com.sqlit.provider.usercontentproivder/user代表用户信息操作
- uriMatcher.addURI(AUTHORITIES, "users", USERSCODE);
- // www.gxw.com.sqlit.provider.usercontentproivder/user/1代表查询用户信息
- uriMatcher.addURI(AUTHORITIES, "users/#", USERCODE);
- }
- @Override
- public boolean onCreate() {
- // TODO Auto-generated method stub
- this.dataBaseHelper = new DataBaseHelper(getContext(), 1);
- return false;
- }
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
- Cursor c = null;
- switch (uriMatcher.match(uri)) {
- case USERCODE:
- long id = ContentUris.parseId(uri);
- c = db.query("users", projection, "userid",
- new String[] { "" + id }, null, null, sortOrder);
- break;
- case USERSCODE:
- c = db.query("users", projection, selection, selectionArgs, null,
- null, sortOrder);
- break;
- default:
- throw new IllegalArgumentException("Unkonw uri" + uri);
- }
- return c;
- }
- @Override
- public String getType(Uri uri) {
- // TODO Auto-generated method stub
- /**
- * 操作的条目是多个还是一个
- */
- String value = null;
- switch (uriMatcher.match(uri)) {
- case USERSCODE:
- value = USERS_DIR;
- break;
- case USERCODE:
- value = USERS_ITEM;
- break;
- }
- return value;
- }
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- // TODO Auto-generated method stub
- if (uriMatcher.match(uri) != USERSCODE) {
- throw new IllegalArgumentException("Unkonw uri" + uri);
- }
- SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
- long rowId = db.insert("users", "username", values);
- //db.close();
- //注册监听 通知所有注册在uri上的监听者
- getContext().getContentResolver().notifyChange(uri, null);
- return ContentUris.withAppendedId(uri, rowId);
- }
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- // TODO Auto-generated method stub
- SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
- int rows = -1;
- switch (uriMatcher.match(uri)) {
- case USERCODE:
- long id = ContentUris.parseId(uri);
- rows = db.delete("users", "userid=?", new String[] { "" + id });
- db.close();
- break;
- case USERSCODE:
- rows = db.delete("users", selection, selectionArgs);
- break;
- default:
- throw new IllegalArgumentException("Unkonw uri" + uri);
- }
- return rows;
- }
- @Override
- public int update(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
- int rows = -1;
- switch (uriMatcher.match(uri)) {
- case USERCODE:
- long id = ContentUris.parseId(uri);
- rows = db.update("users", values, "userid=?",
- new String[] { "" + id });
- db.close();
- break;
- case USERSCODE:
- rows = db.update("users", values, selection, selectionArgs);
- break;
- default:
- throw new IllegalArgumentException("Unkonw uri" + uri);
- }
- return rows;
- }
- }
4、创建其他应用other
添加Instrumentation以及Application Nodes权限
以下是MainActivity.java实现代码:
- package www.gxw.com.other2;
- import android.app.Activity;
- import android.content.ContentResolver;
- import android.database.ContentObserver;
- import android.database.Cursor;
- import android.net.Uri;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.support.v4.widget.SimpleCursorAdapter;
- import android.view.View;
- import android.widget.AbsListView;
- import android.widget.AbsListView.OnScrollListener;
- import android.widget.CursorAdapter;
- import android.widget.ListView;
- import android.widget.TextView;
- public class MainActivity extends Activity implements OnScrollListener {
- private boolean flag = false;
- private boolean isLastRow = false;
- private ListView lv_users;
- private TextView tv_tip;
- private SimpleCursorAdapter adapter;
- private ContentResolver contentResolver;
- private static final String URL = "content://www.gxw.com.sqlit.provider.usercontentproivder/users";
- private static final int INSERT = 1;
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- super.handleMessage(msg);
- switch (msg.what) {
- case INSERT:
- tv_tip.setVisibility(View.VISIBLE);
- flag = true;
- tv_tip.setText("有新信息");
- break;
- default:
- break;
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- tv_tip = (TextView) findViewById(R.id.tv_tip);
- tv_tip.setVisibility(View.GONE);// 不占用位置,彻底隐藏
- lv_users = (ListView) findViewById(R.id.lv_users);
- // 获取解析内容
- contentResolver = getContentResolver();
- initData();
- lv_users.setOnScrollListener(this);
- // 注册监听器Observer
- /**
- * 如果为true 模糊匹配
- */
- getContentResolver().registerContentObserver(Uri.parse(URL), true,
- new UserContentObserver(handler));
- }
- private void initData() {
- Cursor c = contentResolver.query(Uri.parse(URL), new String[] {
- "userid as _id", "username", "userphone" }, null, null,
- "userid desc");
- // 控制层
- adapter = new SimpleCursorAdapter(this, R.layout.list_item_user, c,
- new String[] { "username", "userphone" }, new int[] {
- R.id.tv_name, R.id.tv_phone },
- CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
- lv_users.setAdapter(adapter);
- }
- //
- class UserContentObserver extends ContentObserver {
- private Handler handler;
- public UserContentObserver(Handler handler) {
- super(handler);
- // TODO Auto-generated constructor stub
- this.handler = handler;
- }
- @Override
- public void onChange(boolean selfChange) {
- // TODO Auto-generated method stub
- super.onChange(selfChange);
- System.out.println("有新信息插入");
- /*
- * System.out.println("iiiiiiii"); Toast.makeText(MainActivity.this,
- * "fasdf", Toast.LENGTH_LONG) .show();
- */
- handler.sendEmptyMessage(INSERT);
- tv_tip.setText("有新信息");
- }
- }
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- // TODO Auto-generated method stub
- /* Toast.makeText(this, "========" + scrollState, Toast.LENGTH_LONG)
- .show();*/
- // 有新消息 更新的时候去加载数据
- if (flag && scrollState == OnScrollListener.SCROLL_STATE_FLING) {
- initData();
- flag = false;
- tv_tip.setVisibility(View.GONE);
- }
- if (isLastRow && scrollState == OnScrollListener.SCROLL_STATE_FLING) {
- // 去加载数据
- }
- }
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
- // TODO Auto-generated method stub
- /* Toast.makeText(
- this,
- firstVisibleItem + "========" + visibleItemCount + "========="
- + totalItemCount, Toast.LENGTH_LONG).show();*/
- if ((firstVisibleItem + visibleItemCount) >= totalItemCount
- && totalItemCount > 0) {
- // 发送请求处理
- isLastRow = true;
- }
- }
- }