現在有這樣一個應用A通過ContentProvider提供自己的數據給其他應用,應用B通過ContentResolver獲取應用A中提供的數據,並將其展示在ListView中,而應用C通過ContentResolver修改應用A中的數據,或者添加新的數據。現在的問題是應用C修改A中數據后,應用B的ListView中顯示的還是歷史數據……
具體程序如下:
ContentProvider和插入數據的應用分別復用上一篇中的兩個應用,然后新建一個應用,用於獲取ContentProvider中的數據,並在一個ListView中展示:
布局文件activity _main.xml中添加一個ListView:
1 <ListView 2 3 android:id="@+id/lv" 4 5 android:layout_width="match_parent" 6 7 android:layout_height="wrap_content"></ListView>
布局文件item_layout.xml用於顯示ListView中的條目:
1 <?xml version="1.0" encoding="utf-8"?> 2 3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 4 5 android:layout_width="match_parent" 6 7 android:layout_height="match_parent" 8 9 android:orientation="horizontal" > 10 11 <TextView 12 13 android:id="@+id/tv_id" 14 15 android:layout_width="wrap_content" 16 17 android:layout_height="wrap_content" 18 19 android:layout_weight="1"/> 20 21 <TextView 22 23 android:id="@+id/tv_name" 24 25 android:layout_width="wrap_content" 26 27 android:layout_height="wrap_content" 28 29 android:layout_weight="1"/> 30 31 <TextView 32 33 android:id="@+id/tv_gender" 34 35 android:layout_width="wrap_content" 36 37 android:layout_height="wrap_content" 38 39 android:layout_weight="1"/> 40 41 <TextView 42 43 android:id="@+id/tv_age" 44 45 android:layout_width="wrap_content" 46 47 android:layout_height="wrap_content" 48 49 android:layout_weight="1"/> 50 </LinearLayout>
MainActivity添加獲取數據並顯示到ListView的代碼:
1 protected void onCreate(Bundle savedInstanceState) { 2 3 super.onCreate(savedInstanceState); 4 5 setContentView(R.layout.activity_main); 6 7 uri = Uri.parse("content://cn.csc.content_provider/t_student"); 8 9 Cursor cursor = getContentResolver().query(uri, new String[]{"id as _id","name","gender","age"}, null, null, null); 10 11 lv = (ListView) findViewById(R.id.lv); 12 13 lv.setAdapter(new SimpleCursorAdapter(this,R.layout.item_layout,cursor, 14 15 new String[]{"_id","name","gender","age"},new int[]{R.id.tv_id,R.id.tv_name,R.id.tv_gender,R.id.tv_age})); 16 17 }
運行結果:
上面使用到了SimpleCursorAdapter這個Adapter類,其構造的參數說明:
第一個參數指明應用上下文實例
第二個參數指明ListView中每個條目顯示的布局id
第三個參數指明存放要顯示數據的Cursor結果集對象
第四和第五個參數共同指明Cursor結果集中每一個字段放在布局文件中的那個控件中,這兩個數組中的元素時按順序一一對應的。第四個參數是String[]類型的,每個元素為結果集中的字段名,第五個參數時int[]類型的,每個參數時布局文件中每個控件的資源id。
在這里需要特別注意的一點是,SimpleCursorAdapter要求表中必須存在名為_id的字段,否則會報錯,從而停止正常運行。
但是我之前所建立的表中並沒有_id字段,id字段倒是有一個,但是又不想去改變表的定義,這時,就可以在query()方法中,用於指定要查詢的字段的第二個參數中使用別名:如上面的寫法是:
Cursor cursor = getContentResolver().query(uri, new String[]{"id as _id","name","gender","age"}, null, null, null);
這樣結果集中的字段名就由id變為了_id,SimpleCursorAdapter就能正常使用了。
但是,此時由充當應用C角色的應用通過insert()方法添加數據,當前應用中的ListView並沒有變化,還是顯示不完整的歷史數據!!!
針對這個問題,android提供了一個名為ContentObserver的類,用於觀察特定uri的數據有無變化,有變化時,則可以根據自己需要做相應的處理。
ContentObserver的使用:
第一步:在應用B中注冊ContentObserver,聲明要觀察的uri,並重寫其onChange方法完成需要的業務邏輯。
具體代碼如下:
1 uri = Uri.parse("content://cn.csc.content_provider/t_student"); 2 3 getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) { 4 5 @Override 6 7 public void onChange(boolean selfChange) { 8 9 // TODO Auto-generated method stub 10 11 super.onChange(selfChange); 12 13 Log.i("Test","changed"); 14 15 Cursor cursor = getContentResolver().query(uri, new String[]{"id as _id","name","gender","age"}, null, null, null); 16 17 lv = (ListView) findViewById(R.id.lv); 18 19 lv.setAdapter(new SimpleCursorAdapter(MainActivity.this,R.layout.item_layout,cursor, 20 21 new String[]{"_id","name","gender","age"},new int[]{R.id.tv_id,R.id.tv_name,R.id.tv_gender,R.id.tv_age})); 22 23 Toast.makeText(MainActivity.this, "數據有變化,已然刷新ListView顯示", Toast.LENGTH_SHORT).show(); 24 25 } 26 27 28 29 });
第二步:在應用A中的insert()、update()、delete()方法中,通知觀察這些uri的內容觀察者,告知數據發生變化,會觸發其對應的onChange方法,完成數據變化的業務。
具體代碼如下:
1 getContext().getContentResolver().notifyChange(uri, null);
當前ListView狀態:
在應用C中測試增刪改方法,觀察應用B中的運行:
增:
1 public void testInsert(){ 2 3 Uri uri = Uri.parse("content://cn.csc.content_provider/t_student"); 4 5 ContentValues values = new ContentValues(); 6 7 values.put("name", "csc"); 8 9 values.put("gender", "male"); 10 11 values.put("age", 25); 12 13 Uri uri2 = getContext().getContentResolver().insert(uri, values); 14 15 Log.i("Test",uri2.toString()); 16 17 }
運行結果:
刪:
1 public void testDelete(){ 2 3 Uri uri = Uri.parse("content://cn.csc.content_provider/t_student"); 4 5 int i = getContext().getContentResolver().delete(uri, "id>?", new String[]{"7"}); 6 7 Log.i("Test",i+""); 8 9 }
運行結果:
改:
1 public void testUpdate(){ 2 3 Uri uri = Uri.parse("content://cn.csc.content_provider/t_student"); 4 5 ContentValues values = new ContentValues(); 6 7 values.put("name", "dqrcsc"); 8 9 values.put("gender", "male"); 10 11 values.put("age", 24); 12 13 int i = getContext().getContentResolver().update(uri, values, "id>?", new String[]{"3"}); 14 15 Log.i("Test",i+""); 16 17 }
運行結果:
以上,就是ContentObserver的簡單使用啦。