一: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;
- }
- }
- }