Android 中自定義ContentProvider與ContentObserver的使用簡(jiǎn)單實(shí)例
Android 中自定義ContentProvider與ContentObserver的使用簡(jiǎn)單實(shí)例
示例說(shuō)明:
該示例中一共包含兩個(gè)工程。其中一個(gè)工程完成了自定義ContentProvider,另外一個(gè)工程用于測(cè)試該自定義ContentProvider且在該工程中使用了ContentObserver監(jiān)聽(tīng)自定義ContentProvider的數(shù)據(jù)變化
以下代碼為工程TestContentProvider
ContentProviderTest如下:
package cn.testcontentprovider; 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; /** * Demo描述: * 自定義ContentProvider的實(shí)現(xiàn) * ContentProvider主要用于在不同的應(yīng)用程序之間共享數(shù)據(jù),這也是官方推薦的方式. * * 注意事項(xiàng): * 1 在AndroidManifest.xml中注冊(cè)ContentProvider時(shí)的屬性 * android:exported="true"表示允許其他應(yīng)用訪問(wèn). * 2 注意*和#這兩個(gè)符號(hào)在Uri中的作用 * 其中*表示匹配任意長(zhǎng)度的字符 * 其中#表示匹配任意長(zhǎng)度的數(shù)據(jù) * 所以: * 一個(gè)能匹配所有表的Uri可以寫(xiě)成: * content://cn.bs.testcontentprovider/* * 一個(gè)能匹配person表中任意一行的Uri可以寫(xiě)成: * content://cn.bs.testcontentprovider/person/# * */ public class ContentProviderTest extends ContentProvider { private SQLiteDatabaseOpenHelper mSQLiteDatabaseOpenHelper; private final static String AUTHORITY="cn.bs.testcontentprovider"; private static UriMatcher mUriMatcher; private static final int PERSON_DIR = 0; private static final int PERSON = 1; /** * 利用靜態(tài)代碼塊初始化UriMatcher * 在UriMatcher中包含了多個(gè)Uri,每個(gè)Uri代表一種操作 * 當(dāng)調(diào)用UriMatcher.match(Uri uri)方法時(shí)就會(huì)返回該uri對(duì)應(yīng)的code; * 比如此處的PERSONS和PERSON */ static { mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); // 該URI表示返回所有的person,其中PERSONS為該特定Uri的標(biāo)識(shí)碼 mUriMatcher.addURI(AUTHORITY, "person", PERSON_DIR); // 該URI表示返回某一個(gè)person,其中PERSON為該特定Uri的標(biāo)識(shí)碼 mUriMatcher.addURI(AUTHORITY, "person/#", PERSON); } /** * 在自定義ContentProvider中必須覆寫(xiě)getType(Uri uri)方法. * 該方法用于獲取Uri對(duì)象所對(duì)應(yīng)的MIME類(lèi)型. * * 一個(gè)Uri對(duì)應(yīng)的MIME字符串遵守以下三點(diǎn): * 1 必須以vnd開(kāi)頭 * 2 如果該Uri對(duì)應(yīng)的數(shù)據(jù)可能包含多條記錄,那么返回字符串應(yīng)該以"vnd.android.cursor.dir/"開(kāi)頭 * 3 如果該Uri對(duì)應(yīng)的數(shù)據(jù)只包含一條記錄,那么返回字符串應(yīng)該以"vnd.android.cursor.item/"開(kāi)頭 */ @Override public String getType(Uri uri) { switch (mUriMatcher.match(uri)) { case PERSON_DIR: return "vnd.android.cursor.dir/"+AUTHORITY+".persons"; case PERSON: return "vnd.android.cursor.item/"+AUTHORITY+".person"; default: throw new IllegalArgumentException("unknown uri"+uri.toString()); } } @Override public boolean onCreate() { mSQLiteDatabaseOpenHelper=new SQLiteDatabaseOpenHelper(getContext()); return true; } /** * 插入操作: * 插入操作只有一種可能:向一張表中插入 * 返回結(jié)果為新增記錄對(duì)應(yīng)的Uri * 方法db.insert()返回結(jié)果為新增記錄對(duì)應(yīng)的主鍵值 */ @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase(); switch (mUriMatcher.match(uri)) { case PERSON_DIR: long newId = db.insert("person", "name,phone,salary", values); //向外界通知該ContentProvider里的數(shù)據(jù)發(fā)生了變化 ,以便ContentObserver作出相應(yīng) getContext().getContentResolver().notifyChange(uri, null); return ContentUris.withAppendedId(uri, newId); default: throw new IllegalArgumentException("unknown uri" + uri.toString()); } } /** * 更新操作: * 更新操作有兩種可能:更新一張表或者更新某條數(shù)據(jù) * 在更新某條數(shù)據(jù)時(shí)原理類(lèi)似于查詢(xún)某條數(shù)據(jù),見(jiàn)下. */ @Override public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) { SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase(); int updatedNum = 0; switch (mUriMatcher.match(uri)) { // 更新表 case PERSON_DIR: updatedNum = db.update("person", values, selection, selectionArgs); break; // 按照id更新某條數(shù)據(jù) case PERSON: long id = ContentUris.parseId(uri); String where = "personid=" + id; if (selection != null && !"".equals(selection.trim())) { where = selection + " and " + where; } updatedNum = db.update("person", values, where, selectionArgs); break; default: throw new IllegalArgumentException("unknown uri" + uri.toString()); } //向外界通知該ContentProvider里的數(shù)據(jù)發(fā)生了變化 ,以便ContentObserver作出相應(yīng) getContext().getContentResolver().notifyChange(uri, null); return updatedNum; } /** * 刪除操作: * 刪除操作有兩種可能:刪除一張表或者刪除某條數(shù)據(jù) * 在刪除某條數(shù)據(jù)時(shí)原理類(lèi)似于查詢(xún)某條數(shù)據(jù),見(jiàn)下. */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase(); int deletedNum = 0; switch (mUriMatcher.match(uri)) { // 刪除表 case PERSON_DIR: deletedNum = db.delete("person", selection, selectionArgs); break; // 按照id刪除某條數(shù)據(jù) case PERSON: long id = ContentUris.parseId(uri); String where = "personid=" + id; if (selection != null && !"".equals(selection.trim())) { where = selection + " and " + where; } deletedNum = db.delete("person", where, selectionArgs); break; default: throw new IllegalArgumentException("unknown uri" + uri.toString()); } //向外界通知該ContentProvider里的數(shù)據(jù)發(fā)生了變化 ,以便ContentObserver作出相應(yīng) getContext().getContentResolver().notifyChange(uri, null); return deletedNum; } /** * 查詢(xún)操作: * 查詢(xún)操作有兩種可能:查詢(xún)一張表或者查詢(xún)某條數(shù)據(jù) * * 注意事項(xiàng): * 在查詢(xún)某條數(shù)據(jù)時(shí)要注意--因?yàn)榇颂幨前凑誴ersonid來(lái)查詢(xún) * 某條數(shù)據(jù),但是同時(shí)可能還有其他限制.例如: * 要求personid為2且name為xiaoming1 * 所以在查詢(xún)時(shí)分為兩步: * 第一步: * 解析出personid放入where查詢(xún)條件 * 第二步: * 判斷是否有其他限制(如name),若有則將其組拼到where查詢(xún)條件. * * 詳細(xì)代碼見(jiàn)下. */ @Override public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase(); Cursor cursor =null; switch (mUriMatcher.match(uri)) { // 查詢(xún)表 case PERSON_DIR: cursor = db.query("person", projection, selection, selectionArgs,null, null, sortOrder); break; // 按照id查詢(xún)某條數(shù)據(jù) case PERSON: // 第一步: long id = ContentUris.parseId(uri); String where = "personid=" + id; // 第二步: if (selection != null && !"".equals(selection.trim())) { where = selection + " and " + where; } cursor = db.query("person", projection, where, selectionArgs, null, null, sortOrder); break; default: throw new IllegalArgumentException("unknown uri" + uri.toString()); } return cursor; } }
SQLiteDatabaseOpenHelper如下:
package cn.testcontentprovider; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class SQLiteDatabaseOpenHelper extends SQLiteOpenHelper { public SQLiteDatabaseOpenHelper(Context context) { super(context, "contentprovidertest.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table person(personid integer primary key autoincrement,name varchar(20),phone varchar(12),salary Integer(12))"); } //當(dāng)數(shù)據(jù)庫(kù)版本號(hào)發(fā)生變化時(shí)調(diào)用該方法 @Override public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) { //db.execSQL("ALTER TABLE person ADD phone varchar(12) NULL"); //db.execSQL("ALTER TABLE person ADD salary Integer NULL"); } } MainActivity如下: [java] view plain copy package cn.testcontentprovider; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
AndroidManifest.xml如下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.testcontentprovider" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="cn.testcontentprovider.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name="cn.testcontentprovider.ContentProviderTest" android:authorities="cn.bs.testcontentprovider" android:exported="true" /> </application> </manifest>
main.xml如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="該應(yīng)用包含一個(gè)自定義的ContentProvider" android:textSize="15sp" android:layout_centerInParent="true" /> </RelativeLayout>
以下代碼為工程TestBaidu
MainActivity如下:
package cn.testbaidu; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.database.ContentObserver; import android.database.Cursor; /** * Demo描述: * 應(yīng)用A(TestBaidu)調(diào)用另外一個(gè)應(yīng)用(TestContentProvider)中的自定義ContentProvider,即: * 1 自定義ContentProvider的使用 * 2 其它應(yīng)用調(diào)用該ContentProvider * 3 ContentObserver的使用 * * 備注說(shuō)明: * 1 該例子在以前版本的基礎(chǔ)上整理了代碼 * 2 該例子在以前版本的基礎(chǔ)上融合了ContentObserver的使用 * 利用ContentObserver隨時(shí)監(jiān)聽(tīng)ContentProvider的數(shù)據(jù)變化. * 為實(shí)現(xiàn)該功能需要在自定義的ContentProvider的insert(),update(),delete() * 方法中調(diào)用getContext().getContentResolver().notifyChange(uri, null); * 向外界通知該ContentProvider里的數(shù)據(jù)發(fā)生了變化 ,以便ContentObserver作出相應(yīng) * * 測(cè)試方法: * 1 依次測(cè)試ContentProvider的增查刪改(注意該順序)!! * 2 其它應(yīng)用查詢(xún)?cè)揅ontentProvider的數(shù)據(jù) * */ public class MainActivity extends Activity { private Button mAddButton; private Button mDeleteButton; private Button mUpdateButton; private Button mQueryButton; private Button mTypeButton; private long lastTime=0; private ContentResolver mContentResolver; private ContentObserverSubClass mContentObserverSubClass; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); init(); initContentObserver(); } private void init() { mContentResolver=this.getContentResolver(); mAddButton=(Button) findViewById(R.id.addButton); mAddButton.setOnClickListener(new ClickListenerImpl()); mDeleteButton=(Button) findViewById(R.id.deleteButton); mDeleteButton.setOnClickListener(new ClickListenerImpl()); mUpdateButton=(Button) findViewById(R.id.updateButton); mUpdateButton.setOnClickListener(new ClickListenerImpl()); mQueryButton=(Button) findViewById(R.id.queryButton); mQueryButton.setOnClickListener(new ClickListenerImpl()); mTypeButton=(Button) findViewById(R.id.typeButton); mTypeButton.setOnClickListener(new ClickListenerImpl()); } // 注冊(cè)一個(gè)針對(duì)ContentProvider的ContentObserver用來(lái)觀察內(nèi)容提供者的數(shù)據(jù)變化 private void initContentObserver() { Uri uri = Uri.parse("content://cn.bs.testcontentprovider/person"); mContentObserverSubClass=new ContentObserverSubClass(new Handler()); this.getContentResolver().registerContentObserver(uri, true,mContentObserverSubClass); } @Override protected void onDestroy() { super.onDestroy(); if (mContentObserverSubClass!=null) { this.getContentResolver().unregisterContentObserver(mContentObserverSubClass); } } // 自定義一個(gè)內(nèi)容觀察者ContentObserver private class ContentObserverSubClass extends ContentObserver { public ContentObserverSubClass(Handler handler) { super(handler); } //采用時(shí)間戳避免多次調(diào)用onChange( ) @Override public void onChange(boolean selfChange) { super.onChange(selfChange); System.out.println("ContentObserver onChange() selfChange="+ selfChange); if (System.currentTimeMillis()-lastTime>2000) { ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://cn.bs.testcontentprovider/person"); // 獲取最新的一條數(shù)據(jù) Cursor cursor = resolver.query(uri, null, null, null,"personid desc limit 1"); while (cursor.moveToNext()) { int personid = cursor.getInt(cursor.getColumnIndex("personid")); System.out.println("內(nèi)容提供者中的數(shù)據(jù)發(fā)生變化,現(xiàn)數(shù)據(jù)中第一條數(shù)據(jù)的personid="+ personid); } cursor.close(); lastTime=System.currentTimeMillis(); }else{ System.out.println("時(shí)間間隔過(guò)短,忽略此次更新"); } } @Override public boolean deliverSelfNotifications() { return true; } } private class ClickListenerImpl implements OnClickListener { @Override public void onClick(View v) { switch (v.getId()) { case R.id.addButton: Person person = null; for (int i = 0; i < 5; i++) { person = new Person("xiaoming" + i, "9527" + i, (8888 + i)); testInsert(person); } break; case R.id.deleteButton: testDelete(1); break; case R.id.updateButton: testUpdate(3); break; case R.id.queryButton: // 查詢(xún)表 // queryFromContentProvider(-1); // 查詢(xún)personid=2的數(shù)據(jù) testQuery(2); break; case R.id.typeButton: testType(); break; default: break; } } } private void testInsert(Person person) { ContentValues contentValues=new ContentValues(); contentValues.put("name", person.getName()); contentValues.put("phone", person.getPhone()); contentValues.put("salary",person.getSalary()); Uri insertUri=Uri.parse("content://cn.bs.testcontentprovider/person"); Uri returnUri=mContentResolver.insert(insertUri, contentValues); System.out.println("新增數(shù)據(jù):returnUri="+returnUri); } private void testDelete(int index){ Uri uri=Uri.parse("content://cn.bs.testcontentprovider/person/"+String.valueOf(index)); mContentResolver.delete(uri, null, null); } private void testUpdate(int index){ Uri uri=Uri.parse("content://cn.bs.testcontentprovider/person/"+String.valueOf(index)); ContentValues values=new ContentValues(); values.put("name", "hanmeimei"); values.put("phone", "1234"); values.put("salary", 333); mContentResolver.update(uri, values, null, null); } private void testQuery(int index) { Uri uri=null; if (index<=0) { //查詢(xún)表 uri=Uri.parse("content://cn.bs.testcontentprovider/person"); } else { //按照id查詢(xún)某條數(shù)據(jù) uri=Uri.parse("content://cn.bs.testcontentprovider/person/"+String.valueOf(index)); } //對(duì)應(yīng)上面的:查詢(xún)表 //Cursor cursor= mContentResolver.query(uri, null, null, null, null); //對(duì)應(yīng)上面的:查詢(xún)personid=2的數(shù)據(jù) //注意:因?yàn)閚ame是varchar字段的,所以應(yīng)該寫(xiě)作"name='xiaoming1'" // 若寫(xiě)成"name=xiaoming1"查詢(xún)時(shí)會(huì)報(bào)錯(cuò) Cursor cursor= mContentResolver.query(uri, null, "name='xiaoming1'", null, null); while(cursor.moveToNext()){ int personid=cursor.getInt(cursor.getColumnIndex("personid")); String name=cursor.getString(cursor.getColumnIndex("name")); String phone=cursor.getString(cursor.getColumnIndex("phone")); int salary=cursor.getInt(cursor.getColumnIndex("salary")); System.out.println("查詢(xún)得到:personid=" + personid+",name="+name+",phone="+phone+",salary="+salary); } cursor.close(); } private void testType(){ Uri dirUri=Uri.parse("content://cn.bs.testcontentprovider/person"); String dirType=mContentResolver.getType(dirUri); System.out.println("dirType:"+dirType); Uri itemUri=Uri.parse("content://cn.bs.testcontentprovider/person/3"); String itemType=mContentResolver.getType(itemUri); System.out.println("itemType:"+itemType); } }
Person如下:
package cn.testbaidu; public class Person { private Integer id; private String name; private String phone; private Integer salary; public Person(String name, String phone,Integer salary) { this.name = name; this.phone = phone; this.salary=salary; } public Person(Integer id, String name, String phone,Integer salary) { this.id = id; this.name = name; this.phone = phone; this.salary=salary; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public Integer getSalary() { return salary; } public void setSalary(Integer salary) { this.salary = salary; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", phone=" + phone+ ", salary=" + salary + "]"; } }
main.xml如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/addButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="30dip" android:text="增加" android:textSize="20sp" /> <Button android:id="@+id/queryButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="30dip" android:layout_below="@id/addButton" android:text="查詢(xún)" android:textSize="20sp" /> <Button android:id="@+id/deleteButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="30dip" android:layout_below="@id/queryButton" android:text="刪除" android:textSize="20sp" /> <Button android:id="@+id/updateButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="30dip" android:layout_below="@id/deleteButton" android:text="修改" android:textSize="20sp" /> <Button android:id="@+id/typeButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="30dip" android:layout_below="@id/updateButton" android:text="類(lèi)型" android:textSize="20sp" /> </RelativeLayout>
如有疑問(wèn)請(qǐng)留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android 媒體庫(kù)數(shù)據(jù)更新方法總結(jié)
這篇文章主要介紹了Android 媒體庫(kù)數(shù)據(jù)更新方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-04-04Android編程實(shí)現(xiàn)屏幕自適應(yīng)方向尺寸與分辨率的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)屏幕自適應(yīng)方向尺寸與分辨率的方法,涉及Android屏幕分辨率、布局、橫豎屏切換等相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-12-12Android ServiceManager的啟動(dòng)和工作原理
這篇文章主要介紹了Android ServiceManager的啟動(dòng)和工作原理,幫助大家更好的理解和學(xué)習(xí)使用Android開(kāi)發(fā),感興趣的朋友可以了解下2021-03-03通過(guò)實(shí)例簡(jiǎn)單講解Android App中的Activity組件
這篇文章主要介紹了通過(guò)Android App中的Activity組件,包括Activity的定義和繼承以及啟動(dòng)等基本知識(shí),需要的朋友可以參考下2016-04-04擁抱kotlin之如何習(xí)慣使用kotlin高階函數(shù)
這篇文章主要給大家介紹了關(guān)于擁抱kotlin之如何習(xí)慣使用kotlin高階函數(shù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用kotlin具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Android實(shí)現(xiàn)動(dòng)態(tài)體溫計(jì)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)動(dòng)態(tài)體溫計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06Android自定義View實(shí)現(xiàn)五子棋游戲
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11