關於Android中使用Uri監聽資料庫的變化
網上原創的關於監聽資料庫變化的文章很少,基本沒找到有用的一篇,所以自己去看了一下藍芽傳輸的原始碼,寫了一個Demo,放在這裡給大家參考一下,看原始碼:
src裡面有三個檔案MyDataProvider、MainActivity和MyBean,看下面:
MyDataProvider.java:
public class MyDataProvider extends ContentProvider { // public static final String SCHEME = "test"; public static final String SCHEME = "content"; // 原始碼裡面規定這樣寫,所以這個地方改變不了 public static final String HOST = "com.zyj"; public static final String PORT = "497393102"; public static final String PATH = "simple"; public static final int ALARMS = 1; public static final String SHARE_LIST_TYPE = "com.zyj.test.dir/"; public static final int ALARMS_ID = 2; public static final String SHARE_TYPE = "com.zyj.test.item/"; private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); private SQLiteOpenHelper mDB = null; // ===content://com.zyj:497393102/simple public static final Uri CONTENT_URI = Uri.parse(SCHEME + "://" + HOST + ":" + PORT + "/" + PATH); // 新增Uri的匹配方式,返回的就是上面自定義的整數型別,1代表操作的是一個批量,2操作的是單獨的一個物件 static { sURIMatcher.addURI(HOST + ":" + PORT, PATH, ALARMS); sURIMatcher.addURI(HOST + ":" + PORT, PATH + "/#", ALARMS_ID); } @Override public boolean onCreate() { mDB = new MyDB(getContext()); // 獲取資料庫的引用 return mDB != null; } @Override public String getType(Uri uri) { // 得到我們自定義的Uri的型別,看上面你自己的定義 int match = sURIMatcher.match(uri); switch (match) { case ALARMS: { return SHARE_LIST_TYPE; } case ALARMS_ID: { return SHARE_TYPE; } default: { throw new IllegalArgumentException("Unknown URI: " + uri); } } } @Override public Uri insert(Uri uri, ContentValues values) { // 首先是看Uri和我們自定義的是否匹配,,匹配則將資料屬性插入到資料庫中並同志更新 SQLiteDatabase db = mDB.getWritableDatabase(); if (sURIMatcher.match(uri) != ALARMS) { throw new IllegalArgumentException("Unknown/Invalid URI " + uri); } ContentValues filteredValues = new ContentValues(); filteredValues.put(MyDB.BEAN_ID, values.getAsInteger(MyDB.BEAN_ID)); filteredValues.put(MyDB.MESSAGE, values.getAsString(MyDB.MESSAGE)); filteredValues.put(MyDB.TASK_PROGRESS, values.getAsFloat(MyDB.TASK_PROGRESS)); long rowID = db.insert(MyDB.TABLET, null, filteredValues); if (rowID != -1) { getContext().getContentResolver().notifyChange(uri, null); } return CONTENT_URI; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // 首先是看Uri和我們自定義的是否匹配,,匹配則進行刪除 SQLiteDatabase db = mDB.getWritableDatabase(); int count = 0; int match = sURIMatcher.match(uri); switch (match) { case ALARMS: case ALARMS_ID: String where = null; // 這裡對selection進行匹配操作,看你傳遞的是一個批量還是一個單獨的檔案 if (selection != null) { if (match == ALARMS) { where = "( " + selection + " )"; } else { where = "( " + selection + " ) AND "; } } else { where = ""; } if (match == ALARMS_ID) { // 如果你傳遞的是一個單獨的檔案,也就是Uri後面添加了/item的,那麼在這裡把該值與資料庫中的屬性段進行比較,返回sql語句中的where String segment = uri.getPathSegments().get(1); long rowId = Long.parseLong(segment); where += " ( " + MyDB.BEAN_ID + " = " + rowId + " ) "; } count = db.delete(MyDB.TABLET, where, selectionArgs); break; default: throw new UnsupportedOperationException("Cannot delete URI: " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // 基本同上了 SQLiteDatabase db = mDB.getWritableDatabase(); int count; long rowId = 0; int match = sURIMatcher.match(uri); switch (match) { case ALARMS: case ALARMS_ID: { String myWhere; if (selection != null) { if (match == ALARMS) { myWhere = "( " + selection + " )"; } else { myWhere = "( " + selection + " ) AND "; } } else { myWhere = ""; } if (match == ALARMS_ID) { String segment = uri.getPathSegments().get(1); rowId = Long.parseLong(segment); myWhere += " ( " + MyDB.BEAN_ID + " = " + rowId + " ) "; } if (values.size() > 0) { count = db.update(MyDB.TABLET, values, myWhere, selectionArgs); } else { count = 0; } break; } default: { throw new UnsupportedOperationException("Cannot update URI: " + uri); } } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = mDB.getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); //SQLiteQueryBuilder是一個構造SQL查詢語句的輔助類 int match = sURIMatcher.match(uri); switch (match) { case ALARMS: { qb.setTables(MyDB.TABLET); break; } case ALARMS_ID: { qb.setTables(MyDB.TABLET); qb.appendWhere(MyDB.BEAN_ID + "="); qb.appendWhere(uri.getPathSegments().get(1)); break; } default: throw new IllegalArgumentException("Unknown URI: " + uri); } Cursor ret = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); if (ret != null) { ret.setNotificationUri(getContext().getContentResolver(), uri); Log.d("zyj", "created cursor " + ret + " on behalf of "); } else { Log.d("zyj", "query failed in downloads database"); } return ret; } private static class MyDB extends SQLiteOpenHelper { // 這裡就是資料庫了,資料庫欄位、名稱、表名等... private static final String DATABASE = "test_database"; public static final String TABLET = "test_table"; public static String ID = "_id"; public static String BEAN_ID = "_bean_id"; public static String MESSAGE = "_message"; public static String TASK_PROGRESS = "_progress"; private SQLiteDatabase mDB = null; private final String msql = "CREATE TABLE IF NOT EXISTS " + TABLET + "( " + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + BEAN_ID + " TEXT, " + MESSAGE + " TEXT, " + TASK_PROGRESS + " TEXT )"; private MyDB(Context context) { super(context, DATABASE, null, 1); } @Override public void onCreate(SQLiteDatabase db) { mDB = db; mDB.execSQL(msql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 升級,自己可以去實現 } } }
MyBean.java一個例項物件
public class MyBean
{
public int id = 0;
public String message = null;
public float progress = 0.0f;
public MyBean(int id)
{
this.id = id;
}
}
MainActivity.java主介面了
public class MainActivity extends Activity { TextView mMessage = null; private ContentObserver mDatabaseListener = null; private Handler mHand = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMessage = (TextView) findViewById(R.id.message); init(); // 註冊資料庫的監聽,對應的是特定的Uri getContentResolver().registerContentObserver(MyDataProvider.CONTENT_URI, true, mDatabaseListener); } @Override protected void onDestroy() { super.onDestroy(); // 登出掉監聽 getContentResolver().unregisterContentObserver(mDatabaseListener); } private void init() { mHand = new Handler(); // 資料庫變動時的回撥 mDatabaseListener = new ContentObserver(mHand) { @Override public boolean deliverSelfNotifications() { System.out.println("deliverSelfNotifications ---------------- "); return super.deliverSelfNotifications(); } @Override public void onChange(boolean selfChange, Uri uri) { System.out.println("onChange ---------------- " + uri.toString()); super.onChange(selfChange, uri); } @Override public void onChange(boolean selfChange) { System.out.println("onChange ---------------- ..."); super.onChange(selfChange); } }; } private int count = 0; public void onViewClick(View view) { switch (view.getId()) { case R.id.add: // 插入資料 ContentValues calues = new ContentValues(); calues.put("_bean_id", count++); calues.put("_message", "AAAAAAAAAAAAAAAAAAAAA"); calues.put("_progress", 0.0f); getContentResolver().insert(MyDataProvider.CONTENT_URI, calues); break; case R.id.del: // 如果找不到指定的_bean_id=1、2、3的,則資料庫不進行增減,但還是會呼叫回撥方法 getContentResolver().delete(Uri.parse(MyDataProvider.CONTENT_URI.toString() + "/1"), null, null); getContentResolver().delete(Uri.parse(MyDataProvider.CONTENT_URI.toString() + "/2"), null, null); getContentResolver().delete(Uri.parse(MyDataProvider.CONTENT_URI.toString() + "/3"), null, null); break; case R.id.modify: ContentValues values = new ContentValues(); values.put("_message", "ZZZZZZZZZZZZZZZZZZZZZ"); // 這兩中方法一樣,這樣就可以更加明白Uri中在後面新增的/item了數字的意思了 getContentResolver() .update(Uri.parse(MyDataProvider.CONTENT_URI.toString() + "/5"), values, null, null); getContentResolver().update(MyDataProvider.CONTENT_URI, values, "_bean_id=?", new String[] { "6" }); break; case R.id.query: showMessage(getContentResolver().query(MyDataProvider.CONTENT_URI, null, null, null, null)); break; } } private void showMessage(Cursor c) { if (c == null) { return; } final StringBuffer sb = new StringBuffer(); if (c.getCount() > 0) { while (c.moveToNext()) { MyBean bean = new MyBean(c.getInt(c.getColumnIndex("_bean_id"))); bean.message = c.getString(c.getColumnIndex("_message")); bean.progress = c.getFloat(c.getColumnIndex("_progress")); sb.append(bean.id + "\t\t\t:" + bean.message + "\t\t\t,progress = " + bean.progress + "\n"); } } c.close(); mHand.post(new Runnable() { public void run() { mMessage.setText(sb.toString()); } }); } }
activity_main.xml 上面就是四個按鈕,下面就是一個TextView顯示控制元件
<LinearLayout 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" android:orientation="vertical" android:padding="10.0dip" > <LinearLayout android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:orientation="vertical" > <Button android:id="@+id/add" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="onViewClick" android:text="Add" /> <Button android:id="@+id/del" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="onViewClick" android:text="Delete" /> <Button android:id="@+id/modify" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="onViewClick" android:text="Modify" /> <Button android:id="@+id/query" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="onViewClick" android:text="Query" /> </LinearLayout> <ScrollView android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:maxHeight="20dip" > <TextView android:id="@+id/message" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="20sp" /> </ScrollView> </LinearLayout>
最後就是AndroidManifest.xml了,和平時一樣的,只不過在裡面將你自己定義的ContentProvider寫上,類似我的這樣:
<provider
android:name="com.example.databasetest.MyDataProvider"
android:authorities="com.zyj:497393102" />
上面的authorities屬性是一定要寫的,它就是上面MyDataProvider.java裡面的CONTENT_URI的HOST + ":" + PORT,可以看下面畫的,就比較清楚了。
content://com.example.project:200/folder/subfolder/etc
\---------/ \---------------------------/ \---/ \--------------------------/
scheme host port path
\--------------------------------/
authority
然後就沒有了,可以自己執行,感受一下Uri和資料庫的監聽。
有錯誤之處希望能夠之出來,謝謝!!!