編輯:關於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 { Classklass = (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 Classklass = (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 Classklass = (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的更新推送,於是迫不及待的更新了一下。不負眾望Android Studio 2.2帶來了很多新的特性,能讓我眼前一亮。A
TabActivity在API13之後被fragment替代了,所以不建議使用效果:點擊頭像標簽,進行切換。 代碼:https://github.com/ldb
Android的硬件抽象層,簡單來說,就是對Linux內核驅
前面我們講解了系統截屏按鍵處理流程,HOME按鍵處理流程,今天再來講解一下電源開關機按鍵事件流程,當然這也是系統按鍵處理流程方面的最後一篇博客了。和截屏按鍵、HOME按鍵