2011年12月28日 星期三

Android - ContentProvider - Sqlite應用 (1)

對於ContentProvider的原理,請看原文:
http://developer.android.com/guide/topics/providers/content-providers.html

網 路上也有很多人解釋ContentProvider,這裡不多說。我的實作測試程式和網路上不同,從Android Source Code可以發現/packages/providers底下有很多個資料夾。這些大部份都不會有Acitvity,因為這類型的apk單純就只是一個介 面給其它的apk呼叫用的。所以如果實作程式將Content Provider和含Activity合成同一支apk,就比較感覺不到效果。因此測試含有2支apk

(1)MyDataBaseProvider.apk:內含2個java檔
     1. MyContentProvider.java : 繼承ContentProvider,需要實作6個function
         query()
         insert()
         update()
         delete()
         getType()
         onCreate()  
     2. MyOpenHelper.java : 繼承 SQLiteOpenHelper,這是為了方便產生DB用途

(2)UseProvider.apk :裡面有一個Main.java。這支apk和MyDataBaseProvider分開獨立的。利用系統的ContentResolver找出適合的ContentProvider溝通(本範例也就是存取DB資料)

理論上,應該是先有ContentProvider介面設計好之後,未來有需要的apk就都可以透過它去存取資料。

MyContentProvider.java的程式碼:
public class MyContentProvider extends ContentProvider {
    private MyOpenHelper myOpenHelper;
    private static final String[] MYCOLUMN = new String[]{ "mycolumn"};
   
    @Override
    public boolean onCreate() {
        myOpenHelper = new MyOpenHelper(getContext());
        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);    
        return c;
    }
    @Override
    public String getType(Uri uri) {
        return null;
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        return 0;
    }

}

其中的getLastPathSegment目的是取出uri最後一個字段,有可能是Table的名稱也有可能是單筆資料的row id。因此可以確保異動的資料對象到底是整張Table還是某一筆資料。

MyOpenHelper.java的程式碼:
public class MyOpenHelper extends SQLiteOpenHelper {

    // 定義DB相關資訊
    private static final String DATABASE_NAME = "MyDatabase";
    private static final int DATABASE_VERSION = 1;
    private static final String TABLE_NAME = "MyTable";

    public MyOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 這一個onCreate是一個callback function
        // 之後程式呼叫到SQLiteOpenHelper裡的getWritableDatabase()時,
        // 會檢查有無DB,沒有就先create "MyDatabase"的DB
        // 然後再檢查是否要callback這個onCreate()而產生一個"MyTable"的table,並塞入一筆資料
        db.execSQL("CREATE TABLE " + TABLE_NAME
                + " (id integer primary key autoincrement, mycolumn text);");
        String mySql = "insert into " + TABLE_NAME
                + " (mycolumn) values('gill');";
        db.execSQL(mySql);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 如果DB版本更新時需要進行什麼動作,就寫在這裡
    }
}

AndroidManifest.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.test.mydatabaseprovider"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <provider android:name="MyContentProvider"
            android:authorities="com.gill.test.authorities">
        </provider>   
    </application>
</manifest>

這邊的authorities值隨便你取,其它apk要透過這個authorities和要存取的table name組出一個URI,才可以存取DB內該table資料。

public class Main extends Activity {
    // 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);

        // 透過getContentResolver由系統依據uri,決定要執行那一個ContentProvider
        // 所以這裡不用直接呼叫MyDataBaseProvider也可以取得資料
        Cursor myCursor = getContentResolver().query(CONTENT_URI, MYCOLUMN, null,
                null, null);
       
        myCursor.moveToFirst(); //一定要寫,不然會出錯
        TextView txt = (TextView) findViewById(R.id.txt);
        txt.setText(myCursor.getString(0));
    }
}

安裝這兩支apk後,執行UseMyProvider,畫面如下:

gill是來自於DB




















如果想知道DB位置和裡面的table及資料,可以利用adb shell進入後,
# cd data/data/com.test.mydatabaseprovider/databases
# ls    
MyDatabase
# sqlite3 MyDatabase
SQLite version 3.6.22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .table
MyTable           android_metadata
sqlite> .header on
sqlite> select * from MyTable;
id|mycolumn
1|gill

就可以看到欄位名稱和裡面的值了。

沒有留言:

張貼留言