android常用的數據保存方式有文件、sharepreferences、數據庫、網絡、contentprovider集中方式。
文件存儲方式,經常使用在緩存整個頁面數據,比如電子書內容、html數據等。
sharepreferrences存儲方式,實質也就是xml文件存儲的封裝,常用於存儲配置參數數據。當然也可以用文件存儲+Properties來存儲參數數據。
網絡,就是將數據存儲在網絡空間上。
contentprovider主要作用是統一數據存儲方式,實現數據共享,以後有機會仔細分析下。
數據庫的方式,常用在存儲一系列的結構複雜的數據,輕量級的數據庫SQlit使用起來還是比較簡單,但是總想能像hibernate似的框架可以進行下封裝,實現orm並且可以實現簡單的rcud。這裏就介紹下一個分裝過程,源碼下載在後面。
一、封裝數據庫的類結構
結構比較簡單:BaseBean.java--實體類的基類
DBConfig.java---數據庫的參數,包括數據庫名、要創建的表列表、數據庫版本等
IssContentProvider.java--繼承了ContentProvider,定義了數據庫的初始化和操作
DBFactory--數據庫工廠類
Table.java----定義Table的annotation
TableColumn.java--定義表的列的annotation和屬性
TableUtil.java--書庫操作工具類,主要是獲得表和根據標籤拼裝sql語句
二、封裝數據庫的使用過程
由於封裝後的數據庫使用比較簡單,就跟配置好hibernate之後使用似的,所以咱們先看下咱們使用,不理解的地方,等分析了整個實現過程後就行清晰了。
1、要實現orm,肯定要定義帶標籤的實體類,當然是繼承BaseBean類。
2、要將數據庫參數傳遞給DBConfig,並初始化。
3、數據庫操作類,通過contentprovideder實現crud。
用例子看下
1,定義實體類
public class SmartDownloadBean extends BaseBean<SmartDownloadBean> {
@TableColumn(type = TableColumn.Types.TEXT, isIndex = true, isNotNull = true)
public String downpath;
@TableColumn(type = TableColumn.Types.INTEGER)
public int threadid;
@TableColumn(type = TableColumn.Types.INTEGER)
public int downlength;
@Override
public SmartDownloadBean parseJSON(JSONObject jsonObj) {
return null;
}
@Override
public JSONObject toJSON() {
// TODO Auto-generated method stub
return null;
}
@Override
public SmartDownloadBean cursorToBean(Cursor cursor) {
this.downpath = cursor.getString(cursor.getColumnIndex("downpath"));
this.threadid = cursor.getInt(cursor.getColumnIndex("threadid"));
this.downlength = cursor.getInt(cursor.getColumnIndex("downlength"));
return this;
}
@Override
public ContentValues beanToValues() {
ContentValues values = new ContentValues();
if (!TextUtils.isEmpty(downpath)) {
values.put("downpath", downpath);
}
if (!TextUtils.isEmpty(threadid+"")) {
values.put("threadid", threadid);
}
if (!TextUtils.isEmpty(downlength+"")) {
values.put("downlength", downlength);
}
return values;
}
}
實體類通過標籤,定義了對應表的列名、及列的屬性
2、定義要創建的表、數據名等參數
/**
* 數據庫配置
**/
public class SssProvider extends IssContentProvider {
@Override
public void init() {
// 數據庫相關參數設置
DBConfig config = new DBConfig.Builder()
.addTatble(SmartDownloadBean.class)
.setName("sss.db").setVersion(2)
.setAuthority("com.sss").build();
IssDBFactory.init(getContext(), config);
}
}
要定義都個表的話,再addTatble(Bean.class)即可。
3、調用數據庫的工具類
/**
* 操作數據庫的utils
*
* @author dllik 2013-11-23
*/
public class DBUtils {
public static Uri URI_SMARTDOWNLOAD = IssContentProvider.buildUri(SmartDownloadBean.class);
/**
* 獲取每條線程已經下載的文件長度
*
* @param context
* @param downpath
* @return
*/
public static Map<Integer, Integer> querySmartDownData(Context context, String downpath) {
ContentResolver mResolver = context.getContentResolver();
Cursor cursor = mResolver.query(URI_SMARTDOWNLOAD, null, "downpath=?", new String[] {
downpath
}, null);
Map<Integer, Integer> data = new HashMap<Integer, Integer>();
while (cursor.moveToNext()) {
SmartDownloadBean bean = new SmartDownloadBean();
bean.cursorToBean(cursor);
data.put(bean.threadid, bean.downlength);
}
cursor.close();
return data;
}
/**
* 保存每條線程已經下載的文件長度
*
* @param context
* @param path
* @param map
*/
public static void insertSmartDown(Context context, String path, Map<Integer, Integer> map) {
ContentResolver mResolver = context.getContentResolver();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
SmartDownloadBean bean = new SmartDownloadBean();
bean.downpath = path;
bean.downlength = entry.getValue();
bean.threadid = entry.getKey();
mResolver.insert(URI_SMARTDOWNLOAD, bean.beanToValues());
}
}
/**
* 實時更新每條線程已經下載的文件長度
*
* @param context
* @param path
* @param map
*/
public static void updateSmartDown(Context context, String path, Map<Integer, Integer> map) {
ContentResolver mResolver = context.getContentResolver();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
SmartDownloadBean bean = new SmartDownloadBean();
bean.downpath = path;
bean.downlength = entry.getValue();
bean.threadid = entry.getKey();
mResolver.update(URI_SMARTDOWNLOAD, bean.beanToValues(), "downpath=? and threadid=?",
new String[] {
bean.downpath, bean.threadid + ""
});
}
}
/**
* 當文件下載完成後,刪除對應的下載記錄
*
* @param context
* @param path
*/
public static void deleteSmartDown(Context context, String path) {
ContentResolver mResolver = context.getContentResolver();
mResolver.delete(URI_SMARTDOWNLOAD, "downpath=?", new String[] {
path
});
}
}
三、數據庫的封裝過程
看下實體類的基類
public abstract class BaseBean<T> implements Serializable {
private static final long serialVersionUID = -804757173578073135L;
@TableColumn(type = TableColumn.Types.INTEGER, isPrimary = true)
public static final String _ID = "_id";
/**
* 將json對象轉化爲Bean實例
*
* @param jsonObj
* @return
*/
public abstract T parseJSON(JSONObject jsonObj);
/**
* 將Bean實例轉化爲json對象
*
* @return
*/
public abstract JSONObject toJSON();
/**
* 將數據庫的cursor轉化爲Bean實例(如果對象涉及在數據庫存取,需實現此方法)
*
* @param cursor
* @return
*/
public abstract T cursorToBean(Cursor cursor);
/**
* 將Bean實例轉化爲一個ContentValues實例,供存入數據庫使用(如果對象涉及在數據庫存取,需實現此方法)
*
* @return
*/
public abstract ContentValues beanToValues();
@SuppressWarnings("unchecked")
public T parseJSON(Gson gson, String json) {
return (T) gson.fromJson(json, this.getClass());
}
public ContentValues toValues() {
ContentValues values = new ContentValues();
try {
Class<?> c = getClass();
Field[] fields = c.getFields();
for (Field f : fields) {
f.setAccessible(true);
final TableColumn tableColumnAnnotation = f.getAnnotation(TableColumn.class);
if (tableColumnAnnotation != null) {
if (tableColumnAnnotation.type() == TableColumn.Types.INTEGER) {
values.put(f.getName(), f.getInt(this));
} else if (tableColumnAnnotation.type() == TableColumn.Types.BLOB) {
values.put(f.getName(), (byte[]) f.get(this));
} else if (tableColumnAnnotation.type() == TableColumn.Types.TEXT) {
values.put(f.getName(), f.get(this).toString());
} else {
values.put(f.getName(), f.get(this).toString());
}
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return values;
}
}
說明幾點:1、用到了泛型,因爲定義的數據實體類有多個
2、實現序列化,實體類數據通過Intent進行傳遞
3、定義了一個主鍵id
數據庫參數類
public class DBConfig {
final ArrayList<Class<? extends BaseBean<?>>> tableList;
final String dbName;
final int dbVersion;
final String authority;
final ArrayList<String> tableNameList;
private DBConfig(final Builder builder) {
tableList = builder.tableList;
dbName = builder.dbName;
dbVersion = builder.dbVersion;
authority = builder.authority;
tableNameList = new ArrayList<String>();
for(Class<? extends BaseBean<?>> c:tableList){
String name = TableUtil.getTableName(c);
tableNameList.add(name);
}
}
public static class Builder {
private ArrayList<Class<? extends BaseBean<?>>> tableList;
private String dbName;
private int dbVersion;
private String authority = "com.iss.mobile";
public Builder() {
tableList = new ArrayList<Class<? extends BaseBean<?>>>();
}
public Builder setName(String name) {
dbName = name;
return this;
}
public Builder setVersion(int version) {
dbVersion = version;
return this;
}
public Builder addTatble(Class<? extends BaseBean<?>> table) {
tableList.add(table);
return this;
}
public Builder setAuthority(String authority){
this.authority = authority;
return this;
}
public DBConfig build(){
return new DBConfig(this);
}
}
}
通過該類,來設置數據庫的參數,在初始化數據庫的時候用到。
內容提供者類,初始化和操作數據庫
public abstract class IssContentProvider extends ContentProvider {
public static String CONTENT_TYPE = "vnd.android.cursor.dir/iss.db";
protected SQLiteDatabase mDB;
public static String AUTHORITY = "com.iss.mobile";
@Override
public boolean onCreate() {
init();
IssDBFactory issDBFactory = IssDBFactory.getInstance();
DBConfig config = IssDBFactory.getInstance().getDBConfig();
if (config == null) {
throw new RuntimeException("db factory not init");
}
AUTHORITY = config.authority;
CONTENT_TYPE = "vnd.android.cursor.dir/" + config.dbName;
mDB = issDBFactory.open();
return true;
}
public abstract void init();
public static final String SCHEME = "content";
@Override
public Uri insert(Uri uri, ContentValues values) {
String tableName = getTableName(uri);
long result = mDB.insert(tableName, null, values);
if (result != -1) {
getContext().getContentResolver().notifyChange(uri, null);
}
return buildResultUri(tableName, result);
}
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
mDB.beginTransaction();
String tableName = getTableName(uri);
for(ContentValues value:values){
mDB.insert(tableName, null, value);
}
mDB.setTransactionSuccessful();
mDB.endTransaction();
return values.length;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
String tableName = getTableName(uri);
return mDB.query(tableName, projection, selection, selectionArgs, null, null, sortOrder);
}
@Override
public String getType(Uri uri) {
return CONTENT_TYPE;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
String tableName = getTableName(uri);
int result = mDB.delete(tableName, selection, selectionArgs);
if (result != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return result;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
String tableName = getTableName(uri);
int result = mDB.update(tableName, values, selection, selectionArgs);
if (result != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return result;
}
private Uri buildResultUri(String tableName, long result) {
final Uri.Builder builder = new Uri.Builder();
DBConfig config = IssDBFactory.getInstance().getDBConfig();
if (config == null) {
throw new RuntimeException("db factory not init");
}
builder.scheme(SCHEME);
builder.authority(config.authority);
builder.path(tableName);
builder.appendPath(String.valueOf(result));
return builder.build();
}
private String getTableName(Uri uri) {
DBConfig config = IssDBFactory.getInstance().getDBConfig();
if (config == null) {
throw new RuntimeException("db factory not init");
}
String path = uri.getLastPathSegment();
if (!config.tableNameList.contains(path)) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
return path;
}
public static Uri buildUri(String path, String id) {
final Uri.Builder builder = new Uri.Builder();
DBConfig config = IssDBFactory.getInstance().getDBConfig();
if (config == null) {
throw new RuntimeException("db factory not init");
}
builder.scheme(SCHEME);
builder.authority(config.authority);
builder.path(path);
builder.appendPath(id);
return builder.build();
}
public static Uri buildUri(String path) {
final Uri.Builder builder = new Uri.Builder();
DBConfig config = IssDBFactory.getInstance().getDBConfig();
if (config == null) {
throw new RuntimeException("db factory not init");
}
builder.scheme(SCHEME);
builder.authority(config.authority);
builder.path(path);
return builder.build();
}
public static Uri buildUri(Class<? extends BaseBean<?>> c) {
final String tableName = TableUtil.getTableName(c);
return buildUri(tableName);
}
}
該內容提供者在創建的時候,先執行init()(將要數據庫的參數設置好,再將參數傳遞給工廠類),工廠類根據參數創建數據庫mDB = issDBFactory.open();
數據庫工廠類:
public class IssDBFactory {
private static final String TAG = IssDBFactory.class.getSimpleName();
private DBConfig mConfig;
private SQLiteDatabase mSQLiteDB;
private IssDBOpenHelper mDBOpenHelper;
private final Context mContext;
private static IssDBFactory instance ;
private IssDBFactory(Context context) {
mContext = context;
}
public static void init(Context context,DBConfig dbConfig){
if(instance==null){
instance = new IssDBFactory(context.getApplicationContext());
instance.setDBConfig(dbConfig);
}
}
public static IssDBFactory getInstance(){
return instance;
}
public void setDBConfig(DBConfig dbConfig){
mConfig = dbConfig;
}
public DBConfig getDBConfig(){
return mConfig;
}
public SQLiteDatabase open() {
if(mSQLiteDB==null){
mDBOpenHelper = new IssDBOpenHelper(mContext, mConfig.dbName, null, mConfig.dbVersion);
mSQLiteDB = mDBOpenHelper.getWritableDatabase();
}
return mSQLiteDB;
}
public void close() {
if(mDBOpenHelper!=null){
mDBOpenHelper.close();
}
}
public void beginTransaction() {
if(mSQLiteDB==null){
mSQLiteDB.beginTransaction();
}
}
public void endTransaction() {
if (mSQLiteDB==null&&mSQLiteDB.inTransaction()) {
mSQLiteDB.endTransaction();
}
}
public void setTransactionSuccessful() {
if (mSQLiteDB==null){
mSQLiteDB.setTransactionSuccessful();
}
}
private final class IssDBOpenHelper extends SQLiteOpenHelper {
public IssDBOpenHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
for (Class<? extends BaseBean<?>> table : mConfig.tableList) {
try {
for (String statment : TableUtil.getCreateStatments(table)) {
Log.d(TAG, statment);
db.execSQL(statment);
}
} catch (Throwable e) {
Log.e(TAG, "Can't create table " + table.getSimpleName());
}
}
/**
* 初始化數據
*/
// initData();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d(TAG, "onUpgrade: " + oldVersion + " >> " + newVersion);
for (Class<? extends BaseBean<?>> table : mConfig.tableList) {
try {
db.execSQL("DROP TABLE IF EXISTS " + TableUtil.getTableName(table));
} catch (Throwable e) {
Log.e(TAG, "Can't create table " + table.getSimpleName());
}
}
onCreate(db);
}
}
public void cleanTable(String tableName, int maxSize, int batchSize) {
Cursor cursor = mSQLiteDB.rawQuery("select count(_id) from " + tableName, null);
if (cursor.getCount() != 0 && cursor.moveToFirst() && !cursor.isAfterLast()) {
if (cursor.getInt(0) >= maxSize) {
int deleteSize = maxSize - batchSize;
mSQLiteDB.execSQL("delete from " + tableName + " where _id in (" + "select _id from " + tableName
+ " order by _id " + " limit " + deleteSize + " )");
}
}
cursor.close();
}
看到這用過數據庫的就比較清晰了,用到了SQLiteOpenHelper是以內部類的形式實現的,在oncreat裏創建了表,在onupgrade裏實現了更新表。
其中用到TableUtil.getCreateStatments(table),
數據庫工具類:
public class TableUtil {
public static String getTableName(Class<? extends BaseBean<?>> c) {
String name = null;
Table tableNameAnnotation = c.getAnnotation(Table.class);
if (tableNameAnnotation != null) {
name = tableNameAnnotation.name();
}
if (TextUtils.isEmpty(name)) {
name = c.getSimpleName();
}
return name;
}
/**
* 拼裝sql用的建表語句以及索引語句
*
* @param c
* @return
*/
public final static List<String> getCreateStatments(Class<? extends BaseBean<?>> c) {
final List<String> createStatments = new ArrayList<String>();
final List<String> indexStatments = new ArrayList<String>();
final StringBuilder builder = new StringBuilder();
final String tableName = getTableName(c);
builder.append("CREATE TABLE ");
builder.append(tableName);
builder.append(" (");
int columnNum = 0;
for (final Field f : c.getFields()) {
f.setAccessible(true);
final TableColumn tableColumnAnnotation = f.getAnnotation(TableColumn.class);
if (tableColumnAnnotation != null) {
columnNum++;
String columnName = f.getName();
builder.append(columnName);
builder.append(" ");
if (tableColumnAnnotation.type() == TableColumn.Types.INTEGER) {
builder.append(" INTEGER");
} else if (tableColumnAnnotation.type() == TableColumn.Types.BLOB) {
builder.append(" BLOB");
} else if (tableColumnAnnotation.type() == TableColumn.Types.TEXT) {
builder.append(" TEXT");
} else {
builder.append(" DATETIME");
}
if (tableColumnAnnotation.isPrimary()) {
builder.append(" PRIMARY KEY");
} else {
if (tableColumnAnnotation.isNotNull()) {
builder.append(" NOT NULL");
}
if (tableColumnAnnotation.isUnique()) {
builder.append(" UNIQUE");
}
}
if (tableColumnAnnotation.isIndex()) {
indexStatments.add("CREATE INDEX idx_" + columnName + "_" + tableName + " ON "
+ tableName + "(" + columnName + ");");
}
builder.append(", ");
}
}
builder.setLength(builder.length() - 2); // remove last ','
builder.append(");");
if (columnNum > 0) {
createStatments.add(builder.toString());
createStatments.addAll(indexStatments);
}
return createStatments;
}
}
就兩個方法,獲取表名,在更新表的時候用到,拼裝sql在創建表時候用到。
最後兩個標籤類:
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String name();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface TableColumn {
public enum Types {
INTEGER, TEXT, BLOB, DATETIME
}
Types type() default Types.TEXT;
boolean isPrimary() default false;
boolean isIndex() default false;
boolean isNotNull() default false;
boolean isUnique() default false;
}
思路比較簡單,就是注意先標籤、過濾器、內容提供者的使用就行了。
最後是封裝包代碼,使用過程沒有加,自己加入吧:http://download.csdn.net/detail/xiangxue336/7001299