1. 程式人生 > >Android資料庫ORM框架用法、原始碼和效能比較分析

Android資料庫ORM框架用法、原始碼和效能比較分析

基本用法

LitePal

LitePal是一款開源的Android資料庫框架,它採用了物件關係對映(ORM)的模式,LitePal很“輕”,jar包只有100k不到,使用起來也比較簡單,原始碼地址為Github地址

首先需要引入lib,可以通過gradle引入也可以將下載的litepal.jar包直接放入libs目錄下。然後需要在assets目錄下新建一個litepal.xml檔案,檔名稱不能隨意更改,程式碼如下:

<?xml version="1.0" encoding="utf-8"?>  
<litepal>  
    <dbname value
="demo" >
</dbname> <version value="1" ></version> <list> <mapping class="com.example.databasetest.model.News"></mapping> </list> </litepal>

用於設定資料庫的名字,用於設定資料庫的版本號,用於設定所有的對映模型,用於存放具體的Model類,必須是完整的類名。

然後還需要配置LitePalApplication,由於操作資料庫時需要用到Context,而我們顯然不希望在每個介面中都去傳一遍這個引數,那樣操作資料庫就顯得太繁瑣了。因此,LitePal使用了一個方法來簡化掉Context這個引數,只需要在AndroidManifest.xml中配置一下LitePalApplication,所有的資料庫操作就都不用再傳Context了。如果需要自定義application檔案,只需繼承LitePalApplication即可。

接下來需要建立Model類

public class News extends DataSupport {  
    private int id;       
    private String title;      
    private String content;        
    private Date publishDate;        
    private int commentCount;        
    // 自動生成get、set方法  
    ...  
}  

繼承了DataSupport類之後,這些實體類就擁有了進行CRUD操作的能力

News news = new News();  
news.setTitle("這是一條新聞標題");  
news.setContent("這是一條新聞內容");  
news.setPublishDate(new Date());  
news.save(); 

List<News> newsList = DataSupport.select("title", "content")  
        .where("commentcount > ?", "0")  
        .order("publishdate desc").find(News.class);  

LitePal還提供了直接用sql原句實現查詢的api方法,擴充套件性也比較好。

AFinal

AFinal是一個Android的orm、ioc快速開發框架,裡面包含了四大功能:控制元件的id繫結和事件繫結功能;網路圖片的顯示功能(裡面包含了強大的快取框架);資料庫sqlite的操作功能;http資料的讀取功能(支援ajax方式讀取),可從Github AFinal獲取原始碼。資料庫FinalDb是其中的一個元件,使用比較簡單。

首先需要建立model類,承擔物件與資料庫表的對映功能。

@Table(name="table_user")
public class User {
    @id
    private int id;
    private String name;
    private String email;
    private Date birth;

    // getter and setter不能省略
    ...
}

宣告@Table註解,表示表名稱為table_user;@id表示id作為該表自動增長的主鍵,接下來就可以直接使用了。

FinalDb db = FinalDb.create(this, "afinal_db");
User user = new User();
user.setEmail("[email protected]");
user.setName("探索者");
user.setBirth(new Date());
db.save(user);
String name = "探索者";
List<User> userList = db.findAllByWhere(User.class, "name="' + name + "'" );//查詢使用者名稱為探索者的使用者
if(userList.size() > 0){
    User other = userList.get(0);
    other.setEmail("customer@youzan.com");
    db.update(other);
}

以上就是AFinal中關於資料庫的簡單用法,不需要額外的配置,api介面也比較簡單易用。

greenDAO

greenDAO與上述兩種ORM框架不同,其原理不是根據反射進行資料庫的各項操作,而是一開始就人工生成業務需要的Model和DAO檔案,業務中可以直接呼叫相應的DAO檔案進行資料庫操作,從而避免了因反射帶來的效能損耗和效率低下。但是由於需要人工生成model和DAO檔案,所以greenDAO的配置就略顯複雜。

首先需要新建一個java工程來生成DAO類檔案,該工程需要匯入greendao-generator.jar和freemarker.jar檔案到專案中,github官方原始碼中已經提供了該工程。

public class ExampleDaoGenerator
{

    public static void main(String[] args) throws Exception
    {
        Schema schema = new Schema(3, "de.greenrobot.daoexample");

        addNote(schema);
        addCustomerOrder(schema);

        new DaoGenerator().generateAll(schema, "../DaoExample/src-gen");
    }

    private static void addNote(Schema schema)
    {
        Entity note = schema.addEntity("Note");
        note.addIdProperty();
        note.addStringProperty("text").notNull();
        note.addStringProperty("comment");
        note.addDateProperty("date");
    }

    private static void addCustomerOrder(Schema schema)
    {
        Entity customer = schema.addEntity("Customer");
        customer.addIdProperty();
        customer.addStringProperty("name").notNull();

        Entity order = schema.addEntity("Order");
        order.setTableName("ORDERS"); // "ORDER" is a reserved keyword
        order.addIdProperty();
        Property orderDate = order.addDateProperty("date").getProperty();
        Property customerId = order.addLongProperty("customerId").notNull().getProperty();
        order.addToOne(customer, customerId);

        ToMany customerToOrders = customer.addToMany(order, customerId);
        customerToOrders.setName("orders");
        customerToOrders.orderAsc(orderDate);
    }

}

在main方法中,

Schema schema = new Schema(3, "de.greenrobot.daoexample");

該方法第一個引數用來更新資料庫版本號,第二個引數為要生成的DAO類所在包路徑。然後進行建表和設定要生成DAO檔案的目標工程的專案路徑。

addNote(schema);
addCustomerOrder(schema);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
new DaoGenerator().generateAll(schema, "../DaoExample/src-gen");

其中src-gen這個目錄名需要在執行前手動建立,否則會報錯。執行後出現以下的提示說明DAO檔案自動生成成功了,重新整理一下DaoExample專案即可看到。

Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/NoteDao.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/Note.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/CustomerDao.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/Customer.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/OrderDao.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/Order.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/DaoMaster.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/DaoSession.java
Processed 3 entities in 268ms

然後具體的使用就比較簡單了

DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
db = helper.getWritableDatabase();
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();

String textColumn = NoteDao.Properties.Text.columnName;
String orderBy = textColumn + " COLLATE LOCALIZED ASC";
cursor = db.query(noteDao.getTablename(), noteDao.getAllColumns(), null, null, null, null, orderBy);
String[] from = { textColumn, NoteDao.Properties.Comment.columnName };
int[] to = { android.R.id.text1, android.R.id.text2 };

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursor, from,
        to);
setListAdapter(adapter);

三種資料庫框架效能比較

為了比較三種資料庫框架的效能,首先選擇同一款手機華為Mate7,然後從以下幾個方面分析比較:

迴圈插入10000次,每次插入一條資料,三種框架用時為:

LitePal         155313ms;
AFinal          110601ms;
greedDAO        98238ms;

批量插入10000條資料,三種框架用時為:

LitePal         12882ms;
AFinal          2783ms;
greedDAO        623ms;

迴圈更新10000次,每次更新一條資料,三種框架用時為:

LitePal         152395ms;
AFinal          4123ms;
greedDAO        97840ms;

批量更新10000條資料,三種框架用時為:

LitePal         12234ms;
AFinal          1585ms;
greedDAO        739ms;

迴圈查詢10000條資料,每次查詢一條資料,三種框架用時為:

LitePal         13806ms;
AFinal          4117ms;
greedDAO        31923ms;

批量查詢10000條資料,三種框架用時為:

LitePal         6863ms;
AFinal          1585ms;
greedDAO        149ms;

根據以上資料可以得出以下幾個結論:

  • 不管是批量插入、更新、查詢,greenDAO都是用時最短,執行速度最快的;
  • 大部分資料庫操作的情況下,LitePal都是用時最長的,只有在迴圈查詢時表現比greenDAO好
  • AFinal對於很多批量操作沒有提供相應的api方法
  • 不管對於哪個框架,批量操作比迴圈操作用時都要短很多

下面是以greenDAO為例的測試原始碼

private void testInsertQueryTime(final int count){
    noteList1 = new ArrayList<Note>();
    for (int i = 0; i < count; i++){
        Note note = new Note(null, "title", "comment", new Date());
        noteList1.add(note);
    }

    noteList2 = new ArrayList<Note>();
    for (int i = 0; i < count; i++){
        Note note = new Note(null, "title", "comment", new Date());
        noteList2.add(note);
    }

    new Thread(){
        @Override
        public void run() {
            super.run();
            long time1 = System.currentTimeMillis();
            for(Note note : noteList1){
                noteDao.insert(note);
            }
            // 迴圈插入10000次,每次插入一條資料
            long time = System.currentTimeMillis() - time1;
            Log.d("NoteActivity", "greedDAO one by one insert " + noteList1.size() + " data, use time " + time + "ms");
                // 批量插入10000條資料
            long time2 = System.currentTimeMillis();
            noteDao.insertInTx((Note[])noteList2.toArray(new Note[noteList2.size()]));
            long time3 = System.currentTimeMillis() - time2;
            Log.d("NoteActivity", "greedDAO batch insert " + noteList2.size() + " data, use time " + time3 + "ms");

                // 迴圈查詢10000條資料,每次查詢一條資料
            long time4 = System.currentTimeMillis();
            for(int i = 0; i < count; i++){
                noteDao.queryBuilder().limit(1).list();
            }
            long time5 = System.currentTimeMillis() - time4;
            Log.d("NoteActivity", "greedDAO one by one query " + count + " data, use time " + time5 + "ms");

                // 批量查詢10000條資料
            long time6 = System.currentTimeMillis();
            List<Note> list2 = noteDao.queryBuilder().limit(10000).offset(-1).list();
            long time7 = System.currentTimeMillis() - time6;
            Log.d("NoteActivity", "greedDAO batch query " + list2.size() + " data, use time " + time7 + "ms");

                // 迴圈更新10000次,每次更新一條資料
            long time8 = System.currentTimeMillis();
            for (Note note : noteList1){
                note.setText("update_title");
                note.setComment("update_comment");
                noteDao.update(note);
            }
            // 批量更新10000條資料
            long time9 = System.currentTimeMillis() - time8;
            Log.d("NoteActivity", "greedDAO one by one update " + count + " data, use time " + time9 + "ms");

            for (Note note : noteList2){
                note.setText("update_title");
                note.setComment("update_comment");
            }
            long time10 = System.currentTimeMillis();
            noteDao.updateInTx(noteList2);
            long time11 = System.currentTimeMillis() - time10;
            Log.d("NoteActivity", "greedDAO batch update " + count + " data, use time " + time11 + "ms");

        }
    }.start();

}

三種資料庫框架的原理和原始碼分析

LitePal

LitePal通過LitePal.xml檔案獲取資料庫的名稱、版本號以及表,然後建立資料庫和表,要執行增刪改查操作時,就會根據資料model進行引數拼裝,最後呼叫系統原生的資料庫操作。解析litepal.xml檔案方法原始碼如下:

private InputStream getConfigInputStream() throws IOException {
    AssetManager assetManager = LitePalApplication.getContext().getAssets();
    String[] fileNames = assetManager.list("");
    if (fileNames != null && fileNames.length > 0) {
        for (String fileName : fileNames) {
            if (Const.LitePal.CONFIGURATION_FILE_NAME.equalsIgnoreCase(fileName)) {
                return assetManager.open(fileName, AssetManager.ACCESS_BUFFER);
            }
        }
    }
    throw new ParseConfigurationFileException(
            ParseConfigurationFileException.CAN_NOT_FIND_LITEPAL_FILE);
}

void usePullParse() {
    try {
        LitePalAttr litePalAttr = LitePalAttr.getInstance();
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        XmlPullParser xmlPullParser = factory.newPullParser();
        xmlPullParser.setInput(getConfigInputStream(), "UTF-8");
        int eventType = xmlPullParser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            String nodeName = xmlPullParser.getName();
            switch (eventType) {
            case XmlPullParser.START_TAG: {
                if (NODE_DB_NAME.equals(nodeName)) {
                    String dbName = xmlPullParser.getAttributeValue("", ATTR_VALUE);
                    litePalAttr.setDbName(dbName);
                } else if (NODE_VERSION.equals(nodeName)) {
                    String version = xmlPullParser.getAttributeValue("", ATTR_VALUE);
                    litePalAttr.setVersion(Integer.parseInt(version));
                } else if (NODE_MAPPING.equals(nodeName)) {
                    String className = xmlPullParser.getAttributeValue("", ATTR_CLASS);
                    litePalAttr.addClassName(className);
                } else if (NODE_CASES.equals(nodeName)) {
                    String cases = xmlPullParser.getAttributeValue("", ATTR_VALUE);
                    litePalAttr.setCases(cases);
                }
                break;
            }
            default:
                break;
            }
            eventType = xmlPullParser.next();
        }
    } catch (XmlPullParserException e) {
        throw new ParseConfigurationFileException(
                ParseConfigurationFileException.FILE_FORMAT_IS_NOT_CORRECT);
    } catch (IOException e) {
        throw new ParseConfigurationFileException(ParseConfigurationFileException.IO_EXCEPTION);
    }
}

LitePal框架會根據model自動建立好相應的資料表,以及表資料型別和非空約束等。

protected String generateCreateTableSQL(String tableName, List<ColumnModel> columnModels,
            boolean autoIncrementId) {
    StringBuilder createTableSQL = new StringBuilder("create table ");
    createTableSQL.append(tableName).append(" (");
    if (autoIncrementId) {
        createTableSQL.append("id integer primary key autoincrement,");
    }
    if (columnModels.size() == 0) {
        createTableSQL.deleteCharAt(createTableSQL.length() - 1);
    }
    boolean needSeparator = false;
    for (ColumnModel columnModel : columnModels) {
        if (columnModel.isIdColumn()) {
            continue;
        }
        if (needSeparator) {
            createTableSQL.append(", ");
        }
        needSeparator = true;
        createTableSQL.append(columnModel.getColumnName()).append(" ").append(columnModel.getColumnType());
        if (!columnModel.isNullable()) {
            createTableSQL.append(" not null");
        }
        if (columnModel.isUnique()) {
            createTableSQL.append(" unique");
        }
        String defaultValue = columnModel.getDefaultValue();
        if (!TextUtils.isEmpty(defaultValue)) {
            createTableSQL.append(" default ").append(defaultValue);
        }
    }
    createTableSQL.append(")");
    LogUtil.d(TAG, "create table sql is >> " + createTableSQL);
    return createTableSQL.toString();
}

要執行增刪改查操作的資料model都會繼承DataSupport,以查詢方法findAll()為例,DataSupport會先呼叫QueryHandler.onFindAll()方法,然後最後會執行到QueryHandler.query方法,該方法傳入的引數有類名、列名稱、查詢條件、排序等等,經過處理後呼叫SDLiteDatabase.query方法,最後將查詢得到的資料轉換成List並返回,原始碼如下:

protected <T> List<T> query(Class<T> modelClass, String[] columns, String selection,
            String[] selectionArgs, String groupBy, String having, String orderBy, String limit,
            List<AssociationsInfo> foreignKeyAssociations) {
    List<T> dataList = new ArrayList<T>();
    Cursor cursor = null;
    try {
        List<Field> supportedFields = getSupportedFields(modelClass.getName());
        String tableName = getTableName(modelClass);
        String[] customizedColumns = getCustomizedColumns(columns, foreignKeyAssociations);
        cursor = mDatabase.query(tableName, customizedColumns, selection, selectionArgs,
                groupBy, having, orderBy, limit);
        if (cursor.moveToFirst()) {
            SparseArray<QueryInfoCache> queryInfoCacheSparseArray = new SparseArray<QueryInfoCache>();
            do {
                T modelInstance = (T) createInstanceFromClass(modelClass);
                giveBaseObjIdValue((DataSupport) modelInstance,
                        cursor.getLong(cursor.getColumnIndexOrThrow("id")));
                setValueToModel(modelInstance, supportedFields, foreignKeyAssociations, cursor, queryInfoCacheSparseArray);
                if (foreignKeyAssociations != null) {
                    setAssociatedModel((DataSupport) modelInstance);
                }
                dataList.add(modelInstance);
            } while (cursor.moveToNext());
            queryInfoCacheSparseArray.clear();
        }
        return dataList;
    } catch (Exception e) {
        e.printStackTrace();
        throw new DataSupportException(e.getMessage());
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

可以看到LitePal不管是建立資料庫、表還是執行增刪改查都是根據Model的類名和屬性名,每次都需要進行反射拼裝然後呼叫Android原生的資料庫操作或者直接執行sql語句。

AFinal

AFinal本質上也是利用java的反射原理實現物件與資料表的對映的,實現上和LitePal有很多不同。AFinal並未提前建立資料庫和表,而是在第一次執行增刪改查方法時,先判斷資料庫和表是否存在,如果不存在的話就根據model執行sql語句進行建立,建立完成後再拼裝具體的操作sql語句,完成相應的增刪改查操作。相關原始碼如下:

public void save(Object entity) {
    checkTableExist(entity.getClass());
    exeSqlInfo(SqlBuilder.buildInsertSql(entity));
}

private void checkTableExist(Class<?> clazz) {
    if (!tableIsExist(TableInfo.get(clazz))) {
        String sql = SqlBuilder.getCreatTableSQL(clazz);
        debugSql(sql);
        db.execSQL(sql);
    }
}

public static String getCreatTableSQL(Class<?> clazz) {
    TableInfo table = TableInfo.get(clazz);

    Id id = table.getId();
    StringBuffer strSQL = new StringBuffer();
    strSQL.append("CREATE TABLE IF NOT EXISTS ");
    strSQL.append(table.getTableName());
    strSQL.append(" ( ");

    Class<?> primaryClazz = id.getDataType();
    if (primaryClazz == int.class || primaryClazz == Integer.class
            || primaryClazz == long.class || primaryClazz == Long.class) {
        strSQL.append(id.getColumn()).append(" INTEGER PRIMARY KEY AUTOINCREMENT,");
    } else {
        strSQL.append(id.getColumn()).append(" TEXT PRIMARY KEY,");
    }


    Collection<Property> propertys = table.propertyMap.values();
    for (Property property : propertys) {
        strSQL.append(property.getColumn());
        Class<?> dataType = property.getDataType();
        if (dataType == int.class || dataType == Integer.class
                || dataType == long.class || dataType == Long.class) {
            strSQL.append(" INTEGER");
        } else if (dataType == float.class || dataType == Float.class
                || dataType == double.class || dataType == Double.class) {
            strSQL.append(" REAL");
        } else if (dataType == boolean.class || dataType == Boolean.class) {
            strSQL.append(" NUMERIC");
        }
        strSQL.append(",");
    }

    Collection<ManyToOne> manyToOnes = table.manyToOneMap.values();
    for (ManyToOne manyToOne : manyToOnes) {
        strSQL.append(manyToOne.getColumn())
                .append(" INTEGER")
                .append(",");
    }
    strSQL.deleteCharAt(strSQL.length() - 1);
    strSQL.append(" )");
    return strSQL.toString();
}

greenDAO

greenDAO與上述兩種ORM框架不同,其原理不是根據反射進行資料庫的各項操作,而是一開始就人工生成業務需要的Model和DAO檔案,業務中可以直接呼叫相應的DAO檔案進行資料庫的增刪改查操作,從而避免了因反射帶來的效能損耗和效率低下。以查詢為例,其程式碼如下:

DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
db = helper.getWritableDatabase();
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();
cursor = db.query(noteDao.getTablename(), noteDao.getAllColumns(), null, null, null, null, orderBy);

首先先建立資料庫,然後在SQLiteOpenHelper.onCreate方法中根據已生成的model建立所有的表,而db.query其實就是Android原生的查詢操作,只不過引數是經過DAO檔案處理過的,無需手動匹配。其他資料庫操作與查詢雷同。

相關推薦

Android資料庫ORM框架用法原始碼效能比較分析

基本用法 LitePal LitePal是一款開源的Android資料庫框架,它採用了物件關係對映(ORM)的模式,LitePal很“輕”,jar包只有100k不到,使用起來也比較簡單,原始碼地址為Github地址。 首先需要引入lib,可以通過g

Android 資料庫ORM框架——GreenDao

最近在對開發專案的效能進行優化。由於專案裡涉及了大量的快取處理和資料庫運用,需要對資料庫進行頻繁的讀寫、查詢等操作。因此首先想到了對整個專案的資料庫框架進行優化。 原先使用android本身內建的sqllite,也就是用的最基本的SQLiteOpenHelper方法,這種方法對自己來說比較方便易懂。但是

Android框架MVCMVPMVVM探究(圖解+案例+附原始碼

1、介紹 MVC、MVP、MVVM這些模式是為了解決開發過程中的實際問題而提出來的,目前作為主流的幾種架構模式而被廣泛使用。本文程式碼 2、瞭解並區分MVC,MVP,MVVM 2.1 MVC MVC,(Model View Controller)

Android常用資料庫ORM框架ORMliteGreenDao比較

一、關於ORM 物件關係對映(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程式技術,用於實現面向物件程式語言裡不同型別系統的資料之間的轉換。從效果上說,它其實是建立了一個可在程式語言裡使用的“虛擬物件資料庫”

Android資料庫高手祕籍 二 建立表LitePal的基本用法

                上一篇文章中我們學習了一些Android資料庫相關的基礎知識,和幾個頗為有用的SQLite命令,都是直接在命令列操作的。但是我們都知道,資料庫是要和程式結合在一起使用的,單獨對一個數據庫去進行増刪改查操作並沒有什麼意義,因此今天我們就來學習一下如何在Android程式當中去操作

Android資料庫高手祕籍(二)——建立表LitePal的基本用法

上一篇文章中我們學習了一些Android資料庫相關的基礎知識,和幾個頗為有用的SQLite命令,都是直接在命令列操作的。但是我們都知道,資料庫是要和程式結合在一起使用的,單獨對一個數據庫去進行増刪改查操作並沒有什麼意義,因此今天我們就來學習一下如何在Android程式當中去操

Android 資料庫ORM開源框架之greenDAO

我相信,在平時的開發過程中,大家一定會或多或少地接觸到 SQLite。然而在使用它時,我們往往需要做許多額外的工作,像編寫 SQL 語句與解析查詢結果等。所以,適用於 Android 的ORM 框架也就孕育而生了,現在市面上主流的框架有 OrmLite、Suga

Android開源:資料庫ORM框架GreenDao學習(三)封裝工具類使用

部落格轉載地址:http://www.it165.net/pro/html/201401/9026.html         上一篇中講解了基本的增刪改查,本篇繼續講解 QureyBuilder 使用,及工具類封裝使用   一、使用QureyBuilder實現表的增刪改查

Android 圖片載入框架Picasso基本使用原始碼完全解析

寫在之前 原本打算是每週更新一篇博文,同時記錄一週的生活狀態,但是稍微工作忙一點就顧不上寫部落格了。悲催 還是說下最近的狀況,最近兩週一直在接公司申請的計費點, 沃商店,銀貝殼,微信等等,然後就是不停的被人催促催促,真是一個頭兩個大。在這期間專案組還搞了個A

手把手帶你擼一套Android簡易ORM框架

ORM概念 實體模型建立 註解列 ID 主鍵 自增長 資料表的列

Android 訊息機制:HandlerMessageQueue Looper

在這篇文章中,我們將會討論 Android 的訊息機制。提到 Handler,有過一些 Android 開發經驗的都應該很清楚它的作用,通常我們使用它來通知主執行緒更新 UI。但是 Handler 需要底層的 MessageQueue 和 Looper 來支援才能運作。這篇文章中,我們將會討論它們三個之間的關

Android 記憶體快取框架 LruCache 的原始碼分析

LruCache 是 Android 提供的一種基於記憶體的快取框架。LRU 是 Least Recently Used 的縮寫,即最近最少使用。當一塊記憶體最近很少使用的時候就會被從快取中移除。在這篇文章中,我們會先簡單介紹 LruCache 的使用,然後我們會對它的原始碼進行分析。 1、基本的使用示例

資料庫直接生成實體daomapper

使用Mybatis Generator <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.o

Android訊息機制(HandlerMessageQueueLooper三者的工作原理)

Android的訊息機制主要是指Handler的執行機制以及Handler所附帶的MessageQueue和Looper的工作過程。messagequeue意思是訊息佇列,它內部儲存一組訊息,有插入和刪除的功能,其實內部是以單鏈表的形式來實現佇列功能的。looper的意思是迴圈,它的主要功能是迴

Android——EditText自定義邊框圓角其常用屬性總結

看下效果圖: 執行步驟: 首先在/res/layout資料夾下建立custom_et_layout.xml佈局檔案,原始碼如下: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:

Android深入學習之requestLayoutinvalidatepostInvalidate的區別

         Android開發離不開介面的重繪和更新,尤其是自定義控制元件更是經常遇到重繪更新介面的場景,下面具體介紹一下重繪介面幾個方法的區別:         (1)、requestLayout:

python Scrapy框架1—框架流程結構一個簡單的例子

python爬蟲學習_Scrapy框架1—框架流程、結構和一個簡單的例子 框架圖 Scrapy Engine(引擎): 負責Spider、ItemPipeline、Downloader、Scheduler中間的通訊,訊號、資料傳遞等。 Scheduler(排程器)

java io系列14:DataInputStream(資料輸入流)的認知原始碼示例

本章介紹DataInputStream。我們先對DataInputStream有個大致認識,然後再深入學習它的原始碼,最後通過示例加深對它的瞭解。 轉載請註明出處:http://www.cnblogs.com/skywang12345/p/io_14.html DataInputStream

Android依賴注入框架AndroidAnnotations

AndroidAnnotations是一個能夠讓你快速進行Android開發的開源框架,它能讓你專注於真正重要的地方。使程式碼更加精簡,使專案更容易維護。相比原生的Android App程式碼量,幾乎可以少一半。 用com.github.barteksc:and

Oracle資料庫中的函式檢視索引

1. Oracle函式 Oracle SQL 提供了用於執行特定操作的專用函式。這些函式大大增強了 SQL 語言的功能。函式可以接受零個或者多個輸入引數,並返回一個輸出結果。 Oracle 資料庫中主要使用兩種型別的函式: 1. 單行函式: 對每一個函式應用在表的記錄中時,只能輸入一