先吐槽下:最近幾周壓力老大了,前面咱們三個ios開發人員花了一個多月開發的應用要移植到android上,因爲應用相對比較複雜,有拖拽排序、離線下載、二維碼掃描,而此次,另外一個ios開發人員離職,剩下的另外一個還沒作個android,得由我帶,因而,我估計了下開發時間,大約34天,我將計劃發給領導,領導卻說最多給我15天,我半天沒說一個字:*&%¥#%**&⋯⋯&& java
哎,出來混的,無論怎樣,儘可能吧。 android
這個開發,咱們四張表要操做,相對於通常的移動應用,仍是比較大的,故數據庫實現確定是整個開發的第一個和相當重要的一步,因而,我整理了下面這個demo。 ios
其實網上有不少比較不錯的orm移動開發框架,相對仍是比較優秀,能夠節省大把的開發時間,但我我的認爲,如今這個還不是很穩定,並且,在效率上,我持保守態度,移動開發上,應相對的少用反射和抽象接口,而網絡上的一些第三方的框架,卻抓住這個大用特用,我的以爲,不大好,另外,採用android原生的數據庫實現方案,還有個極大的好處是:很容易實現產品生態系統,這個是其餘的orm所不易達到的。也即,能夠垂手可得地實現咱們的幾個應用間的數據共享和交互,URI訪問便可,從而造成一個生態圈的目的,具體的,就很少說了。 sql
這個demo是一個完整的db操做,但只給出了一個實體的db實現,不過,足以: shell
程序結構圖: 數據庫
後面的全部db操做,都會被封裝到DBManager中,也即,之後應用的db操做,只需經過DBManager進行交互接口,具體的看TestDBAct中實現: json
一、抽象實體基類 BasicEntity.java: 網絡
package blockcheng.android.model; import java.io.Serializable; import org.json.JSONObject; import android.content.ContentValues; import android.database.Cursor; import android.os.Parcelable; import android.provider.BaseColumns; public abstract class BasicEntity implements BaseColumns,Serializable, Parcelable{ private static final long serialVersionUID = 7169909425485915669L; protected String entityName; protected String entityId; protected long lastUpdateTime; public static final String sDEFAULTSORT = " lastUpdateTime desc"; public static final String sKEY_LASTUPDATETIMEADID = "lastUpdateTime"; /** * demo methods * @param obj * @return */ public static BasicEntity translateJson2Object(JSONObject obj){ //TODO:this method must be override by subclass. return null; } public String getEntityName() { return entityName; } public void setEntityName(String entityName) { this.entityName = entityName; } public String getEntityId() { return entityId; } public void setEntityId(String entityId) { this.entityId = entityId; } public long getLastUpdateTime() { return lastUpdateTime; } public void setLastUpdateTime(long lastUpdateTime) { this.lastUpdateTime = lastUpdateTime; } public abstract ContentValues getContentValues(); /** * demo method :should be copy by subclass * @param c * @return */ public static BasicEntity fromCursor(Cursor c) { return null; } @Override public String toString() { return "id=" + entityId + ", name=" + entityName + ", lastUpdateTime=" + lastUpdateTime + ""; } }
二、廣告實體類AdvertisementEntity.java: app
package blockcheng.android.model; import java.io.Serializable; import org.json.JSONObject; import android.content.ContentValues; import android.database.Cursor; import android.os.Parcel; public class AdvertisementEntity extends BasicEntity implements Serializable{ private static final long serialVersionUID = 3045767248752772598L; /** * 廣告圖片路徑 **/ private String adPic; /** * 廣告類型 1主題 2 單個商品(後可擴充,客戶端可根據廣告類型設定廣告的點擊狀態) **/ private int adType; /** * 廣告對象地址(主題ID、單個商品ID或者其餘) **/ private String adTarget; //'廣告位置:1.banner(後擴充位置) private int adPosition; private String data; /** * demo methods * @param obj * @return */ public static AdvertisementEntity translateJson2Object(JSONObject obj){ //TODO:implement it later. AdvertisementEntity aEntity = new AdvertisementEntity(); return aEntity; } public String getAdPic() { return adPic; } public void setAdPic(String adPic) { this.adPic = adPic; } public int getAdType() { return adType; } public void setAdType(int adType) { this.adType = adType; } public String getAdTarget() { return adTarget; } public void setAdTarget(String adTarget) { this.adTarget = adTarget; } public int getAdPosition() { return adPosition; } public void setAdPosition(int adPosition) { this.adPosition = adPosition; } public String getData() { return data; } public void setData(String data) { this.data = data; } @Override public int describeContents() { // TODO Auto-generated method stub return 0; } /******************************************************************/ /*********************Database config information********************/ /******************************************************************/ public static final String sTABLE_NAME = "AdvertisementEntity"; public static final String sKEY_ADID = "adId"; public static final String sKEY_ADNAME = "adName"; public static final String sKEY_ADTYPE = "adType"; public static final String sKEY_ADPIC = "adPic"; public static final String sKEY_ADTARGET = "adTarget"; /* * 要查詢的所有字段 */ public static String[] ADS_PROJECTION_ALL = new String[] { "adId","adName","lastUpdateTime", "adType","adPic","adTarget", }; @Override public void writeToParcel(Parcel dest, int flags) { // TODO Auto-generated method stub } @Override public ContentValues getContentValues() { // TODO Auto-generated method stub final ContentValues values = new ContentValues(); values.put(sKEY_ADID, getEntityId()); values.put(sKEY_ADNAME, getEntityName()); values.put(sKEY_ADTYPE, adType); values.put(sKEY_ADPIC, adPic); values.put(sKEY_ADTARGET, adTarget); return values; } public static AdvertisementEntity fromCursor(Cursor c) { AdvertisementEntity aEntity = new AdvertisementEntity(); aEntity.entityId = c.getString(c.getColumnIndexOrThrow(sKEY_ADID)); aEntity.entityName = c.getString(c.getColumnIndexOrThrow(sKEY_ADNAME)); aEntity.adPic = c.getString(c.getColumnIndexOrThrow(sKEY_ADTARGET)); aEntity.adType = c.getInt(c.getColumnIndexOrThrow(sKEY_ADTYPE)); aEntity.lastUpdateTime = c.getLong(c.getColumnIndexOrThrow(BasicEntity.sKEY_LASTUPDATETIMEADID)); return aEntity; } @Override public String toString() { return "AdvertisementEntity [" + super.toString()+ "adPic= "+ adPic + ", adType=" + adType + ", adTarget=" + adTarget + ", adPosition=" + adPosition + ", data=" + data + "]" + super.toString(); } }三、DB的一些常量配置 DBConfig.java:
package blockcheng.android.service.database; import android.net.Uri; public interface DBConfig { public static final String DATABASE_NAME = "blockchengDB"; public static final int DATABASE_VERSION = 1; public static final String DB_TAG = "blockcheng"; public static final String AUTHORITY = "blockcheng.android"; // content url public static final Uri CONTENT_URI_AdvertisementEntity = Uri .parse("content://" +AUTHORITY+ "/AdvertisementEntity"); //search value public static final int sSEARCH = 10; public static final int sADVERTISEMENT = 100; public static final int sADVERTISEMENT_ID = 101; }四、建表類DBHelper.java:
package blockcheng.android.service.database; import blockcheng.android.model.AdvertisementEntity; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class DBHelper extends SQLiteOpenHelper implements DBConfig{ public DBHelper(Context context) { super(context, DBConfig.DATABASE_NAME, null, DBConfig.DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub StringBuffer createAdSql = new StringBuffer("CREATE TABLE'"); createAdSql.append(AdvertisementEntity.sTABLE_NAME); createAdSql.append( "'('adId' TEXT NOT NULL," ); createAdSql.append( "'adName' TEXT,"); createAdSql.append("'lastUpdateTime' real DEFAULT 0,"); createAdSql.append("'adType' INTEGER,"); createAdSql.append("'adPic' TEXT,"); createAdSql.append( "'adTarget' TEXT,"); createAdSql.append( "PRIMARY KEY ('adId')" ); createAdSql.append( ");");; Log.i(DBConfig.DB_TAG, "createAdSql::"+createAdSql.toString()); db.execSQL(createAdSql.toString()); String createIndexSql_0 = "CREATE INDEX 'id' ON '" + AdvertisementEntity.sTABLE_NAME + "' ('adId' ASC);"; db.execSQL(createIndexSql_0); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub Log.w(DBConfig.DB_TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS " + AdvertisementEntity.sTABLE_NAME); onCreate(db); } }五、contentProvider實現類DemoContentProvider.java:
package blockcheng.android.service.database; import java.util.HashMap; import blockcheng.android.model.AdvertisementEntity; import blockcheng.android.model.BasicEntity; import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; import android.util.Log; public class DemoContentProvider extends ContentProvider { private SQLiteOpenHelper dbHelper; private static final UriMatcher URI_MATCHER; static {//TODO 須要整合下面的地址: URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); URI_MATCHER.addURI(DBConfig.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, DBConfig.sSEARCH); URI_MATCHER.addURI(DBConfig.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", DBConfig.sSEARCH); //advertisement uri config URI_MATCHER.addURI(DBConfig.AUTHORITY, AdvertisementEntity.sTABLE_NAME, DBConfig.sADVERTISEMENT); URI_MATCHER.addURI(DBConfig.AUTHORITY, AdvertisementEntity.sTABLE_NAME+"/*", DBConfig.sADVERTISEMENT_ID); } // TODO: need more investigate. private static final HashMap<String, String> SUGGESTION_PROJECTION_MAP; static { SUGGESTION_PROJECTION_MAP = new HashMap<String, String>(); SUGGESTION_PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "topicName " + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1); SUGGESTION_PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "goodName " + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_2); SUGGESTION_PROJECTION_MAP.put( SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, AdvertisementEntity._ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); SUGGESTION_PROJECTION_MAP.put(AdvertisementEntity._ID, AdvertisementEntity._ID); } @Override public boolean onCreate() { // TODO Auto-generated method stub dbHelper = new DBHelper(getContext()); return true; } // TODO: update the manifest accordingly. public String getType(Uri uri) { switch (URI_MATCHER.match(uri)) { case DBConfig.sADVERTISEMENT: case DBConfig.sADVERTISEMENT_ID: return "vnd.android.cursor.dir/" + DBConfig.AUTHORITY + "." + AdvertisementEntity.sTABLE_NAME; default: throw new IllegalArgumentException("Unknown URI " + uri); } } @Override public Uri insert(Uri uri, ContentValues initialValues) { // TODO Auto-generated method stub Log.i(DBConfig.DB_TAG, "insert::"+uri.toString()); ContentValues values; SQLiteDatabase db = dbHelper.getWritableDatabase(); switch(URI_MATCHER.match(uri)){ case DBConfig.sADVERTISEMENT: if (initialValues != null) { values = new ContentValues(initialValues); values.put(BasicEntity.sKEY_LASTUPDATETIMEADID, System.currentTimeMillis()); } else { values = new ContentValues(); } final long rowId = db.insert(AdvertisementEntity.sTABLE_NAME, AdvertisementEntity.sKEY_ADPIC, values); if (rowId > 0) { Uri insertUri = ContentUris.withAppendedId(DBConfig.CONTENT_URI_AdvertisementEntity, rowId); getContext().getContentResolver().notifyChange(uri, null); return insertUri; } throw new SQLException("Failed to insert row into " + uri); default: throw new IllegalArgumentException("Unknown URI " + uri); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub Log.i(DBConfig.DB_TAG, "delete::"+uri.toString()); SQLiteDatabase db = dbHelper.getWritableDatabase(); int count; switch (URI_MATCHER.match(uri)) { case DBConfig.sADVERTISEMENT: count = db.delete(AdvertisementEntity.sTABLE_NAME, selection, selectionArgs); break; case DBConfig.sADVERTISEMENT_ID: String adId = uri.getPathSegments().get(1); count = db.delete(AdvertisementEntity.sTABLE_NAME, AdvertisementEntity.sKEY_ADID + "=" + adId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException(" Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { Log.i(DBConfig.DB_TAG, "update::"+uri.toString() +": value="+values.toString()); SQLiteDatabase db = dbHelper.getWritableDatabase(); int count = -1; switch (URI_MATCHER.match(uri)) { case DBConfig.sADVERTISEMENT_ID: Log.i(DBConfig.DB_TAG, "update::sADVERTISEMENT_ID"); String adverstisementId = uri.getPathSegments().get(1); count = db.update( AdvertisementEntity.sTABLE_NAME, values, AdvertisementEntity.sKEY_ADID + "='" + adverstisementId + "'" + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; case DBConfig.sADVERTISEMENT: Log.i(DBConfig.DB_TAG, "update::sADVERTISEMENT"); String aidString = values.getAsString(AdvertisementEntity.sKEY_ADID); values.remove(AdvertisementEntity.sKEY_ADID); count = db.update( AdvertisementEntity.sTABLE_NAME, values, AdvertisementEntity.sKEY_ADID + "='" + aidString + "'" + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } return count; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.i(DBConfig.DB_TAG, "query::"+uri.toString()); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String orderBy; switch (URI_MATCHER.match(uri)) { case DBConfig.sADVERTISEMENT: qb.setTables(AdvertisementEntity.sTABLE_NAME); break; case DBConfig.sADVERTISEMENT_ID: qb.setTables(AdvertisementEntity.sTABLE_NAME); qb.appendWhere(AdvertisementEntity.sKEY_ADID + "='" + uri.getPathSegments().get(1) +"'"); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } // If no sort order is specified use the default if (TextUtils.isEmpty(sortOrder)) { orderBy = BasicEntity.sDEFAULTSORT; } else { orderBy = sortOrder; } SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } }測試界面佈局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="15dip" > <EditText android:id="@+id/et_condition" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:text="1" > <requestFocus /> </EditText> <Button android:id="@+id/db_button1" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:onClick="handleEvent" android:text="add " /> <Button android:id="@+id/db_button2" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:onClick="handleEvent" android:text="delete" /> <Button android:id="@+id/db_button3" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:onClick="handleEvent" android:text="update" /> <Button android:id="@+id/db_button4" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:onClick="handleEvent" android:text="query" /> </LinearLayout>測試類TestDBAct.java:
package cn.helloclq.android.activity; import blockcheng.android.R; import blockcheng.android.model.AdvertisementEntity; import blockcheng.android.service.database.DBManager; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class TestDBAct extends Activity { EditText etCondition; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.dbtest_activity); etCondition = (EditText)findViewById(R.id.et_condition); } public void handleEvent(View view){ String adIdString = etCondition.getText().toString(); AdvertisementEntity aEntity = new AdvertisementEntity(); aEntity.setEntityId(adIdString); aEntity.setEntityName("ad"+adIdString); aEntity.setAdType(Integer.parseInt(adIdString)); aEntity.setAdTarget(adIdString); //public WebTaskTest(Context context, boolean showDialog, // boolean cancelAble, String dialogLoadingStr, // WebRequestCallbackInfc cb) switch(view.getId()) { case R.id.db_button1://add DBManager.getInstance().addAdvertiseMent(getContentResolver(), aEntity); break; case R.id.db_button2://delete DBManager.getInstance().deleteAdvertiseMentById(getContentResolver(), adIdString); break; case R.id.db_button3://update aEntity.setEntityName("ad update"+adIdString); DBManager.getInstance().updateAdervertiseEntity(getContentResolver(), aEntity); break; case R.id.db_button4://query DBManager.getInstance().querAllAdvertisementEntity(getContentResolver()); break; } } }運行效果圖:
運行日誌: 框架
08-07 10:52:50.305: I/blockcheng(458): query::content://blockcheng.android/AdvertisementEntity/1 08-07 10:52:50.415: I/blockcheng(458): createAdSql::CREATE TABLE'AdvertisementEntity'('adId' TEXT NOT NULL,'adName' TEXT,'lastUpdateTime' real DEFAULT 0,'adType' INTEGER,'adPic' TEXT,'adTarget' TEXT,PRIMARY KEY ('adId')); 08-07 10:52:50.434: I/blockcheng(458): insert::content://blockcheng.android/AdvertisementEntity 08-07 10:52:50.455: I/blockcheng(458): addAdvertiseMent::AdvertisementEntity [id=1, name=ad1, lastUpdateTime=0adPic= null, adType=1, adTarget=1, adPosition=0, data=null]id=1, name=ad1, lastUpdateTime=0 08-07 10:53:08.567: I/blockcheng(458): query::content://blockcheng.android/AdvertisementEntity 08-07 10:53:08.575: I/blockcheng(458): ad:AdvertisementEntity [id=1, name=ad1, lastUpdateTime=1375843970443adPic= 1, adType=1, adTarget=null, adPosition=0, data=null]id=1, name=ad1, lastUpdateTime=1375843970443 08-07 10:53:15.585: I/blockcheng(458): update::content://blockcheng.android/AdvertisementEntity: value=adTarget=1 adType=1 adPic=null adId=1 lastUpdateTime=1375843995585 adName=ad update1 08-07 10:53:15.585: I/blockcheng(458): update::sADVERTISEMENT 08-07 10:53:17.355: I/blockcheng(458): delete::content://blockcheng.android/AdvertisementEntity/1 08-07 10:53:18.835: I/blockcheng(458): query::content://blockcheng.android/AdvertisementEntity
麻雀雖小,五臟俱全,加多張表的話,也是如此的,就這樣吧,都寫了好一下子了。
補充manifest文件:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="blockcheng.android" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="17" /> <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.READ_LOGS"></uses-permission> <application android:allowBackup="true" android:label="@string/app_name" > <activity android:name="cn.helloclq.android.activity.TestAct" android:label="@string/app_name" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation" android:screenOrientation="portrait" > </activity> <activity android:name="cn.helloclq.android.activity.TestDBAct" android:label="@string/app_name" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:exported="false" android:authorities="blockcheng.android" android:name="blockcheng.android.service.database.DemoContentProvider" /> </application> </manifest>