Room源码分析

751 阅读3分钟

前言

  • 内容很干,请结合前一篇关于Room的文章一起看
  • demo地址:github.com/AfterChrist…
  • 如果对你有用,请点赞收藏,感谢感谢。

DataBase注解生成的类

public final class DataBase_Impl extends DataBase {
  private volatile PersonDao _personDao;

  @Override
  protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
    final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(2) {
      @Override
      public void createAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("CREATE TABLE IF NOT EXISTS `Person` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `type` INTEGER NOT NULL, `age` INTEGER NOT NULL)");
        _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
        _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dc505637cc281fbab1e623c6d84bdaa5')");
      }

      @Override
      public void dropAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("DROP TABLE IF EXISTS `Person`");
        if (mCallbacks != null) {
          for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
            mCallbacks.get(_i).onDestructiveMigration(_db);
          }
        }
      }
  • 通过生成的源码可知,在生成的impl文件中创建了SupportSQLiteOpenHelper
  • SupportSQLiteOpenHelper  An interface to map the behavior of {**@link **android.database.sqlite.SQLiteOpenHelper}.

Dao的注解类

package com.afterchristmas.roomdemo.room;

import android.database.Cursor;
import androidx.room.EntityInsertionAdapter;
import androidx.room.RoomDatabase;
import androidx.room.RoomSQLiteQuery;
import androidx.room.util.CursorUtil;
import androidx.room.util.DBUtil;
import androidx.sqlite.db.SupportSQLiteStatement;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings({"unchecked", "deprecation"})
public final class PersonDao_Impl implements PersonDao {
  private final RoomDatabase __db;

  private final EntityInsertionAdapter<Person> __insertionAdapterOfPerson;

  public PersonDao_Impl(RoomDatabase __db) {
    this.__db = __db;
    this.__insertionAdapterOfPerson = new EntityInsertionAdapter<Person>(__db) {
      @Override
      public String createQuery() {
        return "INSERT OR REPLACE INTO `Person` (`id`,`title`,`type`,`age`) VALUES (nullif(?, 0),?,?,?)";
      }

      @Override
      public void bind(SupportSQLiteStatement stmt, Person value) {
        stmt.bindLong(1, value.getCache_id());
        if (value.getTitle() == null) {
          stmt.bindNull(2);
        } else {
          stmt.bindString(2, value.getTitle());
        }
        stmt.bindLong(3, value.getType());
        stmt.bindLong(4, value.getAge());
      }
    };
  }

  @Override
  public void insert(final Person person) {
    __db.assertNotSuspendingTransaction();
    __db.beginTransaction();
    try {
      __insertionAdapterOfPerson.insert(person);
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }

  @Override
  public List<Person> getAll() {
    final String _sql = "Select * from person";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
    __db.assertNotSuspendingTransaction();
    final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
    try {
      final int _cursorIndexOfCacheId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
      final int _cursorIndexOfTitle = CursorUtil.getColumnIndexOrThrow(_cursor, "title");
      final int _cursorIndexOfType = CursorUtil.getColumnIndexOrThrow(_cursor, "type");
      final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "age");
      final List<Person> _result = new ArrayList<Person>(_cursor.getCount());
      while(_cursor.moveToNext()) {
        final Person _item;
        final long _tmpCache_id;
        _tmpCache_id = _cursor.getLong(_cursorIndexOfCacheId);
        final String _tmpTitle;
        _tmpTitle = _cursor.getString(_cursorIndexOfTitle);
        final int _tmpType;
        _tmpType = _cursor.getInt(_cursorIndexOfType);
        final int _tmpAge;
        _tmpAge = _cursor.getInt(_cursorIndexOfAge);
        _item = new Person(_tmpCache_id,_tmpTitle,_tmpType,_tmpAge);
        _result.add(_item);
      }
      return _result;
    } finally {
      _cursor.close();
      _statement.release();
    }
  }
}

  • 从注解类的相关的impl文件可知,dao中生成了相关的操作数据库的相关的语句
  • 这一点和Sqlitehelper的操作语句一样

Room类的源码分析

首先看创建room的使用方法

Room.databaseBuilder(WanApplication.instance, DataBase::class.java, DB_NAME)
            .addCallback(object : RoomDatabase.Callback() {
                override fun onCreate(db: SupportSQLiteDatabase) {
                    super.onCreate(db)
                    Log.e("db", "create")

                }

                override fun onOpen(db: SupportSQLiteDatabase) {
                    super.onOpen(db)
                    Log.e("db", "open")
                }
            })
            .allowMainThreadQueries()
            .addMigrations(DataBase.MIGRATION_1_2)
            .build()

RoomDatabase.Builder的作用

 @NonNull
    public static <T extends RoomDatabase> RoomDatabase.Builder<T> databaseBuilder(
            @NonNull Context context, @NonNull Class<T> klass, @NonNull String name) {
        //noinspection ConstantConditions
        if (name == null || name.trim().length() == 0) {
            throw new IllegalArgumentException("Cannot build a database with null or empty name."
                    + " If you are trying to create an in memory database, use Room"
                    + ".inMemoryDatabaseBuilder");
        }
        return new RoomDatabase.Builder<>(context, klass, name);
    }
  • 传递context参数给room类
  • 传递DataBase class的类名 ** 用户反射找到注解生成impl类**
  • 传递数据库的名称
  1. DatabaseConfiguration
  • .addCallback
  • .allowMainThreadQueries()
  • .addMigrations(DataBase.MIGRATION_1_2)

这些东西都会添加到DatabaseConfiguration,在数据库实例init的时候会用到

build方法

	 /**
         * Creates the databases and initializes it.
         * <p>
         * By default, all RoomDatabases use in memory storage for TEMP tables and enables recursive
         * triggers.
         *
         * @return A new database instance.
         */
        @SuppressLint("RestrictedApi")
        @NonNull
        public T build() {
            //noinspection ConstantConditions
            if (mContext == null) {
                throw new IllegalArgumentException("Cannot provide null context for the database.");
            }
            //noinspection ConstantConditions
            if (mDatabaseClass == null) {
                throw new IllegalArgumentException("Must provide an abstract class that"
                        + " extends RoomDatabase");
            }
            if (mQueryExecutor == null && mTransactionExecutor == null) {
                mQueryExecutor = mTransactionExecutor = ArchTaskExecutor.getIOThreadExecutor();
            } else if (mQueryExecutor != null && mTransactionExecutor == null) {
                mTransactionExecutor = mQueryExecutor;
            } else if (mQueryExecutor == null && mTransactionExecutor != null) {
                mQueryExecutor = mTransactionExecutor;
            }

            if (mMigrationStartAndEndVersions != null && mMigrationsNotRequiredFrom != null) {
                for (Integer version : mMigrationStartAndEndVersions) {
                    if (mMigrationsNotRequiredFrom.contains(version)) {
                        throw new IllegalArgumentException(
                                "Inconsistency detected. A Migration was supplied to "
                                        + "addMigration(Migration... migrations) that has a start "
                                        + "or end version equal to a start version supplied to "
                                        + "fallbackToDestructiveMigrationFrom(int... "
                                        + "startVersions). Start version: "
                                        + version);
                    }
                }
            }

            if (mFactory == null) {
                mFactory = new FrameworkSQLiteOpenHelperFactory();
            }

            if (mCopyFromAssetPath != null || mCopyFromFile != null) {
                if (mName == null) {
                    throw new IllegalArgumentException("Cannot create from asset or file for an "
                            + "in-memory database.");
                }
                if (mCopyFromAssetPath != null && mCopyFromFile != null) {
                    throw new IllegalArgumentException("Both createFromAsset() and "
                            + "createFromFile() was called on this Builder but the database can "
                            + "only be created using one of the two configurations.");
                }
                mFactory = new SQLiteCopyOpenHelperFactory(mCopyFromAssetPath, mCopyFromFile,
                        mFactory);
            }
            DatabaseConfiguration configuration =
                    new DatabaseConfiguration(
                            mContext,
                            mName,
                            mFactory,
                            mMigrationContainer,
                            mCallbacks,
                            mAllowMainThreadQueries,
                            mJournalMode.resolve(mContext),
                            mQueryExecutor,
                            mTransactionExecutor,
                            mMultiInstanceInvalidation,
                            mRequireMigration,
                            mAllowDestructiveMigrationOnDowngrade,
                            mMigrationsNotRequiredFrom,
                            mCopyFromAssetPath,
                            mCopyFromFile);
            T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
            db.init(configuration);
            return db;
        }
    }
  • 通过Room.getGeneratedImplementation获取到注解生成的实例
  • db.init()方法初始化

初始化方法分析

  	/**
     * Called by {@link Room} when it is initialized.
     *
     * @param configuration The database configuration.
     */
    @CallSuper
    public void init(@NonNull DatabaseConfiguration configuration) {
        mOpenHelper = createOpenHelper(configuration);
        if (mOpenHelper instanceof SQLiteCopyOpenHelper) {
            SQLiteCopyOpenHelper copyOpenHelper = (SQLiteCopyOpenHelper) mOpenHelper;
            copyOpenHelper.setDatabaseConfiguration(configuration);
        }
        boolean wal = false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            wal = configuration.journalMode == JournalMode.WRITE_AHEAD_LOGGING;
            mOpenHelper.setWriteAheadLoggingEnabled(wal);
        }
        mCallbacks = configuration.callbacks;
        mQueryExecutor = configuration.queryExecutor;
        mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
        mAllowMainThreadQueries = configuration.allowMainThreadQueries;
        mWriteAheadLoggingEnabled = wal;
        if (configuration.multiInstanceInvalidation) {
            mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
                    configuration.name);
        }
    }

  • 创建Sqliteopenhelper
  • 初始化日志开关,回调,日志相关等

总结

Room的实现结构和SqliteOpenHelper类似或者基于SqliteOpenHelper,添加了相关的注解方法,简化数据库相关的书写,减少了使用的难度。