2011年12月28日 星期三

Android - ContentProvider - Sqlite應用 (3) ContentObserver

Android 提供一種「監聽者」的機制,利用ContentObserver可以隨時監聽某個Content是否有變化。我們對先前的Sqlite的DB裡面的 Table進行監聽,一旦新增資料時,就會呼叫秀出Log.d()。使用ContentObserver主要步驟如下:
(1) MyContentObserver.java:實作繼承ContentObserver,並完成
    public MyContentObserver(Handler handler) {
        super(handler);
    }
    public void onChange(boolean selfChange) {
        //當增加一筆資料時,想要做任何事,就寫在這裡
        Log.d("MyContentObserver", "Data insert!");
    }

(2) MyContentProvider.java中: 在query() 裡增加c.setNotificationUri();另外在insert() 裡增加getContext().getContentResolver().notifyChange(); 如此一旦資料新增時,就會去利用callback function呼叫到MyContentObserver裡面的onChange()

(3)Main.java: 利用registerContentObservers() 和 unregisterContentObservers()進行監聽資料和取消監聽資料的動作

程式碼如下:
MyContentObserver.java (和Main是放在同一支apk)
public class MyContentObserver extends ContentObserver {
    public MyContentObserver(Handler handler) {
        super(handler);
    }
    public void onChange(boolean selfChange) {
        Log.d("MyContentObserver", "Data is changed!");
    }
}


public class MyContentProvider extends ContentProvider {

    private MyOpenHelper myOpenHelper;
    private SQLiteDatabase myDB;

    public static final String AUTHORITY = "com.gill.test.authorities";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
            + "/MyTable");
    private static final String[] MYCOLUMN = new String[] { "mycolumn" };

    @Override
    public boolean onCreate() {
        myOpenHelper = new MyOpenHelper(getContext());
        myDB = myOpenHelper.getWritableDatabase();
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteDatabase myDatabase = myOpenHelper.getWritableDatabase();
        Cursor c = myDatabase.query(uri.getLastPathSegment(), MYCOLUMN, null,
                null, null, null, null);
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        long rowId = -1;

        //第二個參數隨便是該table內的任一個欄位,反正insert就是一整筆下去新增,系統會自動配對欄位和values
        rowId = myDB.insert(uri.getLastPathSegment(), "mycolumn", values);
        Uri newUri = Uri.withAppendedPath(CONTENT_URI, "" + rowId);
        getContext().getContentResolver().notifyChange(uri, null);
        return newUri;
    }
}

Main.java
public class Main extends Activity {
    private ContentResolver mResolver;
    private Cursor myCursor;
    private TextView txt;
    private EditText edit;
    private MyContentObserver myObserver;
    private Handler handler = new Handler();
   
    // AUTHORITY要看該Provider的AndroidManifest.xml中的定義
    public static final String AUTHORITY = "com.gill.test.authorities";

    // URI = AUTHORITY再加table name
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
            + "/MyTable");

    private static final String[] MYCOLUMN = new String[] { "mycolumn" };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mResolver = getContentResolver();

        // 透過getContentResolver由系統依據uri,決定要執行那一個ContentProvider
        // 所以這裡不用直接呼叫MyDataBaseProvider也可以取得資料
        myCursor = mResolver.query(CONTENT_URI, MYCOLUMN, null, null, null);

        myCursor.moveToFirst(); // 一定要寫,不然會出錯
        txt = (TextView) findViewById(R.id.txt);
        txt.setText(myCursor.getString(0));

        edit = (EditText) findViewById(R.id.edit);
        Button insert_btn = (Button) findViewById(R.id.insert);


        // 新增
        OnClickListener insert_lis = new OnClickListener() {
            public void onClick(View v) {
                ContentValues values = new ContentValues();
                // 如果table有很多欄位,就在這裡把所有的欄位利用values.put補齊
                values.put("mycolumn", edit.getText().toString());
                mResolver.insert(CONTENT_URI, values);
                txt.setText(myCursor.getString(0));
            }
        };
        insert_btn.setOnClickListener(insert_lis);
   
        registerContentObservers();
    }

    @Override
    protected void onStop() {
      super.onStop();
      unregisterContentObservers();
    }
   
    //註冊監聽
    private void registerContentObservers() {
        myObserver = new MyContentObserver(handler);
        getContentResolver().registerContentObserver(CONTENT_URI, true, myObserver);
    }

    //取消監聽
    private void unregisterContentObservers() {
        if (myObserver != null) {
            getContentResolver().unregisterContentObserver(myObserver);
            myObserver = null;
        }
    }

}

注 意registerContentObserver()第二個參數是true,表示對這個CONTENT_URI底下所有的uri都進行監聽。舉例如果該 值是true,則content://com.gill.test.authorities/MyTable/1的變化,也會監聽。這裡的1是指 table裡面的_id(也有可能是2、3、…等)。如果是false的話,就只對 content://com.gill.test.authorities/MyTable監聽,那就達不到我們要的效果了。content: //com.gill.test.authorities/MyTable就叫Base Uri,而content://com.gill.test.authorities/MyTable/1這種的就是Base Uri的descendanted uri。請參考原文:http://developer.android.com/reference/android/content/ContentResolver.html#registerContentObserver%28android.net.Uri,%20boolean,%20android.database.ContentObserver%29

沒有留言:

張貼留言