先吐槽下:最近几周压力老大了,前面我们三个ios开发人员花了一个多月开发的应用要移植到android上,由于应用相对比较复杂,有拖拽排序、离线下载、二维码扫描,而这次,另一个ios开发人员离职,剩下的另一个还没做个android,得由我带,于是,我估计了下开发时间,大约34天,我将计划发给领导,领导却说最多给我15天,我半天没说一个字:*&%¥#%**&⋯⋯&&
哎,出来混的,不管怎样,尽量吧。
这个开发,我们四张表要操作,相对于一般的移动应用,还是比较大的,故数据库实现肯定是整个开发的第一个和至关重要的一步,于是,我整理了下面这个demo。
其实网上有很多比较不错的orm移动开发框架,相对还是比较优秀,可以节省大把的开发时间,但我个人认为,现在这个还不是很稳定,而且,在效率上,我持保守态度,移动开发上,应相对的少用反射和抽象接口,而网络上的一些第三方的框架,却抓住这个大用特用,个人觉得,不大好,另外,采用android原生的数据库实现方案,还有个极大的好处是:很容易实现产品生态系统,这个是其他的orm所不易达到的。也即,可以轻而易举地实现我们的几个应用间的数据共享和交互,URI访问即可,从而形成一个生态圈的目的,具体的,就不多说了。
这个demo是一个完整的db操作,但只给出了一个实体的db实现,不过,足以:
程序结构图:
后面的所有db操作,都会被封装到DBManager中,也即,以后应用的db操作,只需通过DBManager进行交互接口,具体的看TestDBAct中实现:
1、抽象实体基类 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 + "";
}
}
2、广告实体类AdvertisementEntity.java:
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();
}
}
3、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;
}
4、建表类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);
}
}
5、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>
来源:oschina
链接:https://my.oschina.net/u/173728/blog/150718