1. 程式人生 > >android Xutils 資料庫操作原始碼分析

android Xutils 資料庫操作原始碼分析

XUtils下載地址 http://www.oschina.net/p/xutils

以下是對demo的分析

進入DbFragment

首先看到的是

 DbManager.DaoConfig daoConfig = new DbManager.DaoConfig()
            .setDbName("test")
            .setDbDir(new File("/sdcard")) // "sdcard"的寫法並非最佳實踐, 這裡為了簡單, 先這樣寫了.
            .setDbVersion(2)
            .setDbUpgradeListener(new DbManager.DbUpgradeListener() {
                @Override
                public void onUpgrade(DbManager db, int oldVersion, int newVersion) {
                    // TODO: ...
                    // db.addColumn(...);
                    // db.dropTable(...);
                    // ...
                }
            });

此程式碼大致寫了關於資料庫名稱、版本、路徑以及更新操作

所以可以進入 new DbManager.DaoConfig() 中去檢視具體資訊

<pre name="code" class="java">public interface DbManager extends Closeable 
資料庫介面訪問 此介面定義了關於資料增、刪、查、改、版本相關操作

DbManager中的內部類定義了相關資料庫配置、更新資訊

  public static class DaoConfig {
        private File dbDir;
        private String dbName = "xUtils.db"; // default db name
        private int dbVersion = 1;
        private boolean allowTransaction = true;
        private DbUpgradeListener dbUpgradeListener;
        private TableCreateListener tableCreateListener;

此處注意物件的==與equals的不同

DaoConfig包含如下程式碼

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            DaoConfig daoConfig = (DaoConfig) o;

            if (!dbName.equals(daoConfig.dbName)) return false;
            return dbDir == null ? daoConfig.dbDir == null : dbDir.equals(daoConfig.dbDir);
        }

        @Override
        public int hashCode() {
            int result = dbName.hashCode();
            result = 31 * result + (dbDir != null ? dbDir.hashCode() : 0);
            return result;
        }

介面分析大致結束

然後回到DbFragment檢視資料庫具體操作

   private void onTestDbClick(View view) {

        // 一對多: (本示例的程式碼)
        // 自己在多的一方(child)儲存另一方的(parentId), 查詢的時候用parentId查parent或child.
        // 一對一:
        // 在任何一邊儲存另一邊的Id並加上唯一屬性: @Column(name = "parentId", property = "UNIQUE")
        // 多對多:
        // 再建一個關聯表, 儲存兩邊的id. 查詢分兩步: 先查關聯表得到id, 再查對應表的屬性.

        String temp = "";

        try {

            DbManager db = x.getDb(daoConfig);

此處真正的呼叫了資料庫然後進入org.xutils.x;去檢視具體實現

/**
 * Created by wyouflf on 15/6/10.
 * 任務控制中心, http, image, db, view注入等介面的入口.
 * 需要在在application的onCreate中初始化: x.Ext.init(this);
 */
public final class x {

    private x() {
    }

此處可以看出是所有模組的封裝類
   public static DbManager getDb(DbManager.DaoConfig daoConfig) {
        return DbManagerImpl.getInstance(daoConfig);
    }

此處可以看到DbManagerImpl類
public final class DbManagerImpl implements DbManager {

    //*************************************** create instance ****************************************************

    /**
     * key: dbName
     */
    private static HashMap<DaoConfig, DbManagerImpl> daoMap = new HashMap<DaoConfig, DbManagerImpl>();

    private SQLiteDatabase database;
    private DaoConfig daoConfig;
    private boolean allowTransaction;

    private DbManagerImpl(DaoConfig config) {
        if (config == null) {
            throw new IllegalArgumentException("daoConfig may not be null");
        }
        this.database = createDatabase(config);
        this.daoConfig = config;
        this.allowTransaction = config.isAllowTransaction();
    }

    public synchronized static DbManager getInstance(DaoConfig daoConfig) {

        if (daoConfig == null) {//使用預設配置
            daoConfig = new DaoConfig();
        }

        DbManagerImpl dao = daoMap.get(daoConfig);
        if (dao == null) {
            dao = new DbManagerImpl(daoConfig);
            daoMap.put(daoConfig, dao);
        } else {
            dao.daoConfig = daoConfig;
        }

        // update the database if needed
        SQLiteDatabase database = dao.database;
        int oldVersion = database.getVersion();
        int newVersion = daoConfig.getDbVersion();
        if (oldVersion != newVersion) {
            if (oldVersion != 0) {
                DbUpgradeListener upgradeListener = daoConfig.getDbUpgradeListener();
                if (upgradeListener != null) {
                    upgradeListener.onUpgrade(dao, oldVersion, newVersion);
                } else {
                    try {
                        dao.dropDb();
                    } catch (DbException e) {
                        LogUtil.e(e.getMessage(), e);
                    }
                }
            }
            database.setVersion(newVersion);
        }

        return dao;
    }

注意public final class DbManagerImpl 為final 並且private DbManagerImpl(DaoConfig config) 私有建構函式

上述程式碼建立了database 並儲存到HashMap<DaoConfig, DbManagerImpl> daoMap = new HashMap<DaoConfig, DbManagerImpl>();

建立database程式碼

 private SQLiteDatabase createDatabase(DaoConfig config) {
        SQLiteDatabase result = null;

        File dbDir = config.getDbDir();
        if (dbDir != null && (dbDir.exists() || dbDir.mkdirs())) {
            File dbFile = new File(dbDir, config.getDbName());
            result = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
        } else {
            result = x.app().openOrCreateDatabase(config.getDbName(), 0, null);
        }
        return result;
    }

如果資料庫路徑不存在那麼使用預設路徑並但會database

返回DbFragment繼續往下走

看到關於資料庫表的操作

  Parent test = db.selector(Parent.class).where("id", "in", new int[]{1, 3, 6}).findFirst();

關於

public final class Selector<T> 與  public class WhereBuilder  
db.selector(Parent.class).where("id", "in", new int[]{1, 3, 6})  產生sql語句
</pre>此處程式碼是為了封裝sql資料<p>一下是具體分析</p><p></p><pre name="code" class="java"> @Override
    public <T> Selector<T> selector(Class<T> entityType) throws DbException {
        return Selector.from(TableEntity.get(this, entityType));
    }

public final class TableEntity<T> {

    private final DbManager db;
    private final String name;
    private final String onCreated;
    private ColumnEntity id;
    private Class<T> entityType;
    private Constructor<T> constructor;

    /**
     * key: columnName
     */
    private final LinkedHashMap<String, ColumnEntity> columnMap;

    /**
     * key: dbName#className
     */
    private static final HashMap<String, TableEntity<?>> tableMap = new HashMap<String, TableEntity<?>>();
tableEntity對錶結構和是否存在實現封裝

public class WhereBuilder
對sql語句的封裝

最後執行findFirst

    public T findFirst() throws DbException {
        if (!table.tableIsExist()) return null;

        this.limit(1);
        Cursor cursor = table.getDb().execQuery(this.toString());
        if (cursor != null) {
            try {
                if (cursor.moveToNext()) {
                    return CursorUtils.getEntity(table, cursor);
                }
            } catch (Throwable e) {
                throw new DbException(e);
            } finally {
                IOUtil.closeQuietly(cursor);
            }
        }
        return null;
    }

到此第一個操作語句結束

接著往下看進入第二個與資料庫操作有關的方法

db.save(parent);

進入DbManagerImpl實現程式碼

  @Override
    public void save(Object entity) throws DbException {
        try {
            beginTransaction();

            if (entity instanceof List) {
                List<?> entities = (List<?>) entity;
                if (entities.isEmpty()) return;
                TableEntity<?> table = TableEntity.get(this, entities.get(0).getClass());
                createTableIfNotExist(table);
                for (Object item : entities) {
                    execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, item));
                }
            } else {
                TableEntity<?> table = TableEntity.get(this, entity.getClass());
                createTableIfNotExist(table);
                execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, entity));
            }

            setTransactionSuccessful();
        } finally {
            endTransaction();
        }
    }

此方法首先開啟了事務,然後判斷傳入的物件是否是集合再進行資料操作、此處運用了instanceof去判斷是否是集合

此方法中呼叫了createTableIfNotExist(table)和execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, entity))首先判斷表是否存在然後進行資料庫操作

關於異常的處理程式中直接向外層丟擲未做處理

其他方法的呼叫流程大體相似故不再分析

此部落格中進行了大致呼叫流程分析未對具體進行描述

關於程式碼的具體實現請下載原始碼進行檢視

相關推薦

android Xutils 資料庫操作原始碼分析

XUtils下載地址 http://www.oschina.net/p/xutils 以下是對demo的分析 進入DbFragment 首先看到的是 DbManager.DaoConfig daoConfig = new DbManager.DaoConfig(

Android IntentService用法和原始碼分析

關於IntentService的介紹,我個人覺得還是先看官方描述比較好: IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) o

Django rest framework 許可權操作(原始碼分析二)

知識回顧  這一篇是基於上一篇寫的,上一篇謝了認證的具體流程,看懂了上一篇這一篇才能看懂, 當用戶訪問是 首先執行dispatch函式,當執行當第二部時: #2.處理版本資訊 處理認證資訊 處理許可權資訊 對使用者的訪問頻率進行限制 self

Android事件分發機制原始碼分析之Activity篇

在之前的事件分發分析中,曾提及到View的事件是由ViewGroup分發的,然而ViewGroup的事件我們只是稍微帶過是由Activity分發的。而我們知道,事件產生於使用者按下螢幕的一瞬間,事件生成後,經過一系列的過程來到我們的Activity層,那麼事件是怎樣從Activity傳遞

Android GATT 連線過程原始碼分析

Android GATT 連線過程原始碼分析   低功耗藍芽(BLE)裝置的通訊基本協議是 GATT, 要操作 BLE 裝置,第一步就是要連線裝置,其實就是連線 BLE 裝置上的 GATT service。 結合上一篇文章,我這裡結合原始碼,分析一下 GATT 連線的流程

Android Hawk資料庫原始碼解析,Github開源專案,基於SharedPreferences的的儲存框架

今天看了朋友一個專案用到了Hawk,然後寫了這邊文章 一、瞭解一下概念 Android Hawk資料庫github開源專案 Hawk是一個非常便捷的資料庫.操作資料庫只需一行程式碼,能存任何資料型別. 相信大家應該很熟悉SharedPreferences。它是一種輕量級的儲存簡單配置

Android恢復出廠設定原始碼分析,基於Android 6.0

近兩天解決一些關於恢復出廠設定的問題,對此略有了解,註釋了部分程式碼,給大家分享 設定>>>備份與重置>>>恢復出廠設定>>>… … 對應得類的路徑: \packages\apps\Settings\

Android 使用資料庫操作應用加鎖、未加鎖,列表展示效果

效果圖: 要求:1.獲取應用並展示,上下滑動帶動畫 2.未加鎖中點選"鎖"圖示動畫刪除該條目,並新增至 程式鎖 資料庫(存放已加鎖應用) 3.已加鎖中點選"鎖"圖示動畫刪除該條目,並將當前應用從  程式鎖  中刪除 上程式碼: 首先編寫頁面: activity_main.

Android Launcher載入流程原始碼分析

Launcher載入流程分析 最近開始接手Launcher模組,為了更好的技術積累,也看到很多大神在CSDN上發的博文,就有了在CSDN寫部落格的想法,這篇博文是我在研究了一段時間Launcher3後寫的,可能有不對的,望大家拍磚。首先我們可以先參考這篇htt

Android 8.0系統原始碼分析--Binder程序間通訊(一)

 開始我們的沉澱之路,老羅的書中第二章講的是Android HAL層的知識,而且直接自己實現了一個虛擬的freg驅動程式,後面的幾節是分別從native、java層如何訪問這個虛擬的驅動程式介面,我這裡沒有這樣的環境,所以就不分析這節了,第三章的智慧指標我對比8.0系統原

Android面試題-OkHttp3原始碼分析

本文配套視訊: 原始碼分析相關面試題 基本使用 從使用方法出發,首先是怎麼使用,其次是我們使用的功能在內部是如何實現的.建議大家下載 OkHttp 原始碼之後,跟著本文,過一遍原始碼。 OkHttpClient client = ne

Android系統原理與原始碼分析(1):利用Java反射技術阻止通過按鈕關閉對話方塊

本文為原創,如需轉載,請註明作者和出處,謝謝!     眾所周知,AlertDialog類用於顯示對話方塊。關於AlertDialog的基本用法在這裡就不詳細介紹了,網上有很多,讀者可以自己搜尋。那

ElasticSearch Index操作原始碼分析

ElasticSearch Index操作原始碼分析 本文記錄ElasticSearch建立索引執行原始碼流程。從執行流程角度看一下建立索引會涉及到哪些服務(比如AllocationService、MasterService),由於本人對分散式系統理解不是很深,所以很多一些細節原理也是不懂。 建立索引請求

Android Camera 系統架構原始碼分析(4)---->Camera的資料來源及Camera的管理

Camera的資料來源及Camera的管理  我們接著第3篇,再返回Cam1DeviceBase::startPreview()的(4) mpCamAdapter->startPreview()。在講(4)前我們先來看看(1)onStartPreview(

Android 8.0系統原始碼分析--openCamera(HAL)啟動過程原始碼分析

     前面我們詳細分析了從應用層呼叫CameraManager的openCamera的方法來開啟相機的邏輯,上次的分析我們來到了CameraServer程序當中,但是還沒有真正看到open操作裝置節點來實現真正開啟的邏輯,遺留的問題也就是從frameworks\av\se

Android四大元件BroadcastReceiver原始碼分析

1 用法 (1)定義廣播接收者  public class MyBroadcastReceiver extends BroadcastReceiver {       @Override       public void onReceive(Context context

深度理解Android InstantRun原理以及原始碼分析

Instant Run官方介紹 簡單介紹一下Instant Run,它是Android Studio2.0以後新增的一個執行機制,能夠顯著減少你第二次及以後的構建和部署時間。簡單通俗的解釋就是,當你在Android Studio中改了你的程式碼,Instant Ru

Android 8.0系統原始碼分析--Activity的視窗Window物件新增過程原始碼分析

     這節我們來看一下Activity的視窗Window物件的建立過程,Activity作為Android提供的四大元件之首,我們之所以能非常簡單的使用它,就是因為它的建立過程中,framework為我們作了大量的初始化工作,包括它的視窗Window、視訊記憶體Surf

Android 8.0 RIL原始碼分析(一)

1.去電流程三中跟蹤到最後的時候可以看到其呼叫了RIL的dail方法 這裡繼續以此分析其從RIL到Modem的流程 @Override public void dial(String address, int clirMode, UUSInfo

Android 8.0系統原始碼分析--開篇

     好久沒寫部落格了,在這裡上班基本都加班,而且公司上不了外網,手機都不能帶進辦公室,所以就間斷了。昨天中午沒事翻老羅的CSDN部落格,忽然發現老羅的《Android系統原始碼情景分析 [羅昇陽著