編輯:關於Android編程
在上一篇博客《打造android ORM框架opendroid(一)——ORM框架的使用》中相信你已經了解了opendroid的使用,那麼從這篇博客開始,我們正式進入opendroid的源碼分析,打造一款自己的ORM框架!
在正式開始之前,你需要保證手裡有一份opendroid的源碼,如果還沒下載opendroid,請到http://git.oschina.net/qibin/OpenDroid 下載opendroid的源碼。
任何數據庫操作都是從創建數據庫開始的,今天我們就來看看opendroid是怎麼幫我們自動創建數據庫的。 還記得關系映射怎麼配置嗎? 在open-droid.xml中,通過配置mapping節點來告訴opendroid我們需要映射的java bean。那麼數據庫操作是從何時開始的呢, 拿insert來說,就是調用了從OpenDroid繼承而來的save()方法!在這之前,我們沒有任何數據庫方面的操作,那麼我們就從save()方法開始,看看opendroid是怎麼創建數據庫的。
/**
* 插入數據
* @return 最後插入的id
*/
public long save() {
try {
Class klass = (Class) getClass();
ContentValues cv = new ContentValues();
generateData(klass, cv);
return CRUD.insert(klass.getSimpleName(), cv, sSqliteDatabase);
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
private static SQLiteDatabase sSqliteDatabase = sOpenHelper.getWritableDatabase();
private static CreateDB sOpenHelper = new CreateDB();
public class CreateDB extends SQLiteOpenHelper {
public CreateDB() {
super(DroidApplication.sContext, OpenDroidHelper.getDBInfoBean().getName(),
null, OpenDroidHelper.getDBInfoBean().getVersion(), new DefaultDatabaseErrorHandler());
}
@Override
public void onCreate(SQLiteDatabase db) {
for(String sql : OpenDroidHelper.getDBInfoBean().getSqls()) {
db.execSQL(sql);
}
}
}
public class OpenDroidHelper {
public static final String TAG_DROID = open-droid;
public static final String TAG_VERSION = version;
public static final String TAG_NAME = name;
public static final String TAG_MAPPING = mapping;
private static DBBean sDBBean;
public static DBBean getDBInfoBean() {
if(sDBBean == null) {
generateDBInfoBean();
}
return sDBBean;
}
/**
* 解析Asserts目錄下的open_droid.xml文件,生成DBInfoBean
*/
private static void generateDBInfoBean() {
try {
XmlPullParser pullParser = Xml.newPullParser();
InputStream inputStream = DroidApplication.sContext.getAssets().open(open_droid.xml);
pullParser.setInput(inputStream, utf-8);
int type = pullParser.getEventType();
String tagName = null;
while(type != XmlPullParser.END_DOCUMENT) {
if(type == XmlPullParser.START_TAG) {
tagName = pullParser.getName();
if(tagName.equals(TAG_DROID)) {
sDBBean = new DBBean();
}else if(tagName.equals(TAG_VERSION)) {
// 獲取版本號
sDBBean.setVersion(Integer.parseInt(pullParser.getAttributeValue(null, value)));
}else if(tagName.equals(TAG_NAME)) {
// 獲取數據庫名
sDBBean.setName(pullParser.getAttributeValue(null, value));
}else if(tagName.equals(TAG_MAPPING)) {
// 獲取所有建表語句
sDBBean.addSql(generateSql(pullParser));
}
}
type = pullParser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成建表sql語句
* @param pullParser
* @return
* @throws ClassNotFoundException
* @throws XmlPullParserException
* @throws IOException
*/
private static String generateSql(XmlPullParser pullParser)
throws ClassNotFoundException, XmlPullParserException, IOException {
// 反射獲取class
Class klass = (Class) Class.forName(pullParser.getAttributeValue(null, class));
StringBuilder sql = new StringBuilder(create table );
// 獲取類名, getSimpleName獲取類名, getName()獲取包名+類名
sql.append(klass.getSimpleName()).append(();
// 自動創建一個_id
sql.append(_id integer primary key autoincrement,);
// 獲取所有的字段
Field[] fields = klass.getDeclaredFields();
for(Field field : fields) {
// 如果是public的, 則表示不是一個表的字段
if(field.isAccessible()) {
continue;
}
// 獲取字段名
String name = field.getName();
sql.append(name).append( );
// 獲取字段類型
Class fieldType = field.getType();
if(fieldType == String.class) { // 如果是String
sql.append(text,);
}else if(fieldType == Integer.class || fieldType == int.class) {
sql.append(integer,);
}else if(fieldType == Long.class || fieldType == long.class){
sql.append(integer,);
}else if(fieldType == Boolean.class || fieldType == boolean.class) {
sql.append(boolean,);
}else if(fieldType == Float.class || fieldType == float.class) {
sql.append(float,);
}
}
sql.replace(sql.length() - 1, sql.length(), );
sql.append(););
return sql.toString();
}
}
/**
* 解析Asserts目錄下的open_droid.xml文件,生成DBInfoBean
*/
private static void generateDBInfoBean() {
try {
XmlPullParser pullParser = Xml.newPullParser();
InputStream inputStream = DroidApplication.sContext.getAssets().open(open_droid.xml);
pullParser.setInput(inputStream, utf-8);
int type = pullParser.getEventType();
String tagName = null;
while(type != XmlPullParser.END_DOCUMENT) {
if(type == XmlPullParser.START_TAG) {
tagName = pullParser.getName();
if(tagName.equals(TAG_DROID)) {
sDBBean = new DBBean();
}else if(tagName.equals(TAG_VERSION)) {
// 獲取版本號
sDBBean.setVersion(Integer.parseInt(pullParser.getAttributeValue(null, value)));
}else if(tagName.equals(TAG_NAME)) {
// 獲取數據庫名
sDBBean.setName(pullParser.getAttributeValue(null, value));
}else if(tagName.equals(TAG_MAPPING)) {
// 獲取所有建表語句
sDBBean.addSql(generateSql(pullParser));
}
}
type = pullParser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
恩,在generateDBInfoBean這個方法中,都是我們熟悉的XMLPullParser的代碼,作用就是去解析open-droid.xml文件,獲取數據庫名稱、數據庫版本和數據表的信息。雖然很長,但是都很簡單,20行,我們獲取了數據庫的版本號,並保存到了DBBean中,同樣的23行獲取了數據庫的名稱,注意第26行,我們想DBBean中添加的sql語句,那添加的什麼sql語句呢? 肯定是建表的sql語句了。來看看generateSql()方法。
/**
* 生成建表sql語句
* @param pullParser
* @return
* @throws ClassNotFoundException
* @throws XmlPullParserException
* @throws IOException
*/
private static String generateSql(XmlPullParser pullParser)
throws ClassNotFoundException, XmlPullParserException, IOException {
// 反射獲取class
Class klass = (Class) Class.forName(pullParser.getAttributeValue(null, class));
StringBuilder sql = new StringBuilder(create table );
// 獲取類名, getSimpleName獲取類名, getName()獲取包名+類名
sql.append(klass.getSimpleName()).append(();
// 自動創建一個_id
sql.append(_id integer primary key autoincrement,);
// 獲取所有的字段
Field[] fields = klass.getDeclaredFields();
for(Field field : fields) {
// 如果是public的, 則表示不是一個表的字段
if(field.isAccessible()) {
continue;
}
// 獲取字段名
String name = field.getName();
sql.append(name).append( );
// 獲取字段類型
Class fieldType = field.getType();
if(fieldType == String.class) { // 如果是String
sql.append(text,);
}else if(fieldType == Integer.class || fieldType == int.class) {
sql.append(integer,);
}else if(fieldType == Long.class || fieldType == long.class){
sql.append(integer,);
}else if(fieldType == Boolean.class || fieldType == boolean.class) {
sql.append(boolean,);
}else if(fieldType == Float.class || fieldType == float.class) {
sql.append(float,);
}
}
sql.replace(sql.length() - 1, sql.length(), );
sql.append(););
return sql.toString();
}
12行,通過反射獲取我們要映射的class,然後14~18行,是初始化創建表的sql語句,並且可以看到opendroid會自動為我們添加一個_id字段,所以在定義bean的時候,我們不需要再次定義了。
21行,獲取了這個類中定義的所有字段,並在22行循環遍歷這些字段。
14~26行,可以看到,如果字段是public的,那就忽略它,所以如果你不想把某個字段映射到數據庫中,就定義成public的。
29~30行,是向創建表的sql語句中追加表名。
33~44行,通過判斷這個字段類型,來向創建表的sql語句中追加該字段的類型。
46行的作用是刪除最後一個的“,”
47行,就完成了該表的創建sql語句。
至此,opendroid的自動創建數據庫的流程我們就分析完了,現在來總結一下:
1、數據庫的創建我們借助了android API的SQLiteOpenHelper。
2、在SQLiteOpenHelper的onCreate中遍歷我們自動生成的建表語句並執行。
3、如何生成建表語句? 在OpenDroidHelper中通過反射獲取類的類名和字段名,並通過StringBuilder來拼湊建表語句。
4、為了方便,我們在OpenDroidHelper中將解析出來的數據名、數據庫版本和建表語句都放到了DBBean中,那麼我們在CreateDB類中就可以通過DBBean方便的獲取數據庫的信息了。
ok,數據庫的自動創建大體流程就是這樣,在接下來的博客中,我們還會去一一介紹opendroid的CRUD操作和它的數據庫升級機制。
Android Studio 2.2新特性:新布局、Firebase、OpenJDK以及Java 8
前幾天,收到了Android Studio 2.2的更新推送,於是迫不及待的更新了一下。不負眾望Android Studio 2.2帶來了很多新的特性,能讓我眼前一亮。A
Android Tab -- 使用TabWidget、TabHost、TabActivity來實現 - liDB - 博客園
TabActivity在API13之後被fragment替代了,所以不建議使用效果:點擊頭像標簽,進行切換。 代碼:https://github.com/ldb
Ubuntu中為Android簡單介紹硬件抽象層(HAL)
Android的硬件抽象層,簡單來說,就是對Linux內核驅
android源碼解析(二十八)--)電源開關機按鍵事件流程
前面我們講解了系統截屏按鍵處理流程,HOME按鍵處理流程,今天再來講解一下電源開關機按鍵事件流程,當然這也是系統按鍵處理流程方面的最後一篇博客了。和截屏按鍵、HOME按鍵