Realm源码分析之自动更新原理

来源:互联网 发布:软件启动画面 编辑:程序博客网 时间:2024/06/09 23:07

自动更新

  1. Realm的Model的自动更新机制是比较有趣的特性,如下自动更新代码:

    public void test() {    realm.executeTransaction(new Realm.Transaction() {        @Override        public void execute(Realm realm) {            Dog myDog = realm.createObject(Dog.class);            myDog.setName("Fido");            myDog.setAge(1);        }    });    Dog myDog = realm.where(Dog.class).equalTo("age", 1).findFirst();    LogDebug("dog age:" + myDog.getAge());// dog age:1    realm.executeTransaction(new Realm.Transaction() {        @Override        public void execute(Realm realm) {            Dog myPuppy = realm.where(Dog.class).equalTo("age", 1).findFirst();            myPuppy.setAge(2);        }    });    LogDebug("dog age:" + myDog.getAge());// dog age:2}
    public class Dog extends RealmObject {    private String name;    private int age;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}
  2. 上述代码先在第一个executeTransaction中创建了myDog这个model实例,接着查询了到这条记录并返回myDog实例,在第二个executeTransaction中直接修改这条记录,最后打印这个myDog时会自动更新age的值。

  3. 在上面的源码中看不出端倪,那最大的可能就是在编译过程做了手脚。首先反编译apk,先看如下的反编译代码:

    public void test() {    this.realm.executeTransaction(new Realm.Transaction() {        public void execute(Realm paramAnonymousRealm) {            paramAnonymousRealm = (Dog) paramAnonymousRealm.createObject(Dog.class);            paramAnonymousRealm.setName("Fido");            paramAnonymousRealm.setAge(1);        }    });    Dog localDog = (Dog) this.realm.where(Dog.class).equalTo("age", Integer.valueOf(1)).findFirst();    LogDebug("dog age:" + localDog.getAge());    this.realm.executeTransaction(new Realm.Transaction() {        public void execute(Realm paramAnonymousRealm) {            ((Dog) paramAnonymousRealm.where(Dog.class).equalTo("age", Integer.valueOf(1)).findFirst()).setAge(2);        }    });    LogDebug("dog age:" + localDog.getAge());}
  4. 上述的反编译代码与源码相比几乎没做任何改动,继续看Modle反编译代码,如下Dog反编译代码:

    public class Dog extends RealmObject implements DogRealmProxyInterface {    private int age;    private String name;    public Dog() {        if ((this instanceof RealmObjectProxy)) {            ((RealmObjectProxy) this).realm$injectObjectContext();        }    }    public int getAge() {        return realmGet$age();    }    public String getName() {        return realmGet$name();    }    public int realmGet$age() {        return this.age;    }    public String realmGet$name() {        return this.name;    }    public void realmSet$age(int paramInt) {        this.age = paramInt;    }    public void realmSet$name(String paramString) {        this.name = paramString;    }    public void setAge(int paramInt) {        realmSet$age(paramInt);    }    public void setName(String paramString) {        realmSet$name(paramString);    }}
  5. 注意,上述代码中的get/set方法都被修改了。结合Realm源码分析之Writes这篇文章可以知道创建的model实例是Dog的代理子类DogRealmProxy,而DogRealmProxy会将Dog的这些代理方法都覆盖掉,如下DogRealmProxy的部分反编译代码:

    public int realmGet$age() {    this.proxyState.getRealm$realm().checkIfValid();    return (int) this.proxyState.getRow$realm().getLong(this.columnInfo.ageIndex);}public String realmGet$name() {    this.proxyState.getRealm$realm().checkIfValid();    return this.proxyState.getRow$realm().getString(this.columnInfo.nameIndex);}public void realmSet$age(int paramInt) {    if (this.proxyState.isUnderConstruction()) {        if (!this.proxyState.getAcceptDefaultValue$realm()) {            return;        }        Row localRow = this.proxyState.getRow$realm();        localRow.getTable().setLong(this.columnInfo.ageIndex, localRow.getIndex(), paramInt, true);        return;    }    this.proxyState.getRealm$realm().checkIfValid();    this.proxyState.getRow$realm().setLong(this.columnInfo.ageIndex, paramInt);}public void realmSet$name(String paramString) {    if (this.proxyState.isUnderConstruction()) {        return;    }    this.proxyState.getRealm$realm().checkIfValid();    throw new RealmException("Primary key field 'name' cannot be changed after object was created.");}
  6. 由上述代码可知,关键在于proxyState的Row,这个Row是在injectObjectContext时完成初始化的,而injectObjectContext又是在model构造方法中注入的。如下代码:

    public void realm$injectObjectContext() {    if (this.proxyState != null) {        return;    }    BaseRealm.RealmObjectContext localRealmObjectContext = (BaseRealm.RealmObjectContext) BaseRealm.objectContext        .get();    this.columnInfo = ((DogColumnInfo) localRealmObjectContext.getColumnInfo());    this.proxyState = new ProxyState(this);    this.proxyState.setRealm$realm(localRealmObjectContext.getRealm());    this.proxyState.setRow$realm(localRealmObjectContext.getRow());    this.proxyState.setAcceptDefaultValue$realm(localRealmObjectContext.getAcceptDefaultValue());    this.proxyState.setExcludeFields$realm(localRealmObjectContext.getExcludeFields());}
  7. 在Realm源码分析之Writes这篇文章中分析过这个Row其实就是UncheckedRow,所以realmGet$age()最终就会调用到如下的getLong方法:

    @Overridepublic long getLong(long columnIndex) {    return nativeGetLong(nativePtr, columnIndex);}protected native long nativeGetLong(long nativeRowPtr, long columnIndex);
  8. 上述代码最终是调用了native方法,来看看nativeGetLong这个在io_realm_internal_UncheckedRow.cpp中的实现:

    JNIEXPORT jlong JNICALL Java_io_realm_internal_UncheckedRow_nativeGetLong(JNIEnv* env, jobject, jlong nativeRowPtr, jlong columnIndex){    TR_ENTER_PTR(nativeRowPtr)    if (!ROW_VALID(env, ROW(nativeRowPtr))) {        return 0;    }    return ROW(nativeRowPtr)->get_int(S(columnIndex));}
  9. 上述代码先调用了TR_ENTER_PTR,这是一个宏,用于打印log,其实现在log.hpp中,代码如下:

    #define TR_ENTER_PTR(ptr)                                                  \if (realm::jni_util::Log::s_level <= realm::jni_util::Log::trace) {        \    realm::jni_util::Log::t(" --> %1 %2", __FUNCTION__, static_cast<int64_t>(ptr)); \}
  10. ROW_VALID用于参数有效性判断,它也是一个宏,其实现在util.hpp中,代码如下:

    #define ROW_VALID(env, ptr) RowIsValid(env, ptr)  inline bool RowIsValid(JNIEnv* env, realm::Row* rowPtr){    bool valid = (rowPtr != NULL && rowPtr->is_attached());    if (!valid) {        realm::jni_util::Log::e("Row %1 is no longer attached!", reinterpret_cast<int64_t>(rowPtr));        ThrowException(env, IllegalState,                       "Object is no longer valid to operate on. Was it deleted by another thread?");    }    return valid;}
  11. 再看ROW的这个宏,是在util.hpp中定义的,如下代码:

    #define ROW(x) reinterpret_cast<realm::Row*>(x)
  12. 上面的宏其实就是类型强制转换,nativeRowPtr最终指向的是row.hpp中的get_int()方法,源码如下:

       inline int_fast64_t RowFuncs<T, R>::get_int(size_t col_ndx) const noexcept{    return table()->get_int(col_ndx, row_ndx());}    inline const T* RowFuncs<T, R>::table() const noexcept{    return static_cast<const R*>(this)->impl_get_table();}... ...typedef BasicTableRef<T> TableRef; // Same as ConstTableRef if `T` is 'const'   ... ...TableRef m_table; // nullptr if detached.... ...    template <class T>    inline T* BasicRow<T>::impl_get_table() const noexcept{    return m_table.get();}   
  13. 上面的函数继续调用table.hpp中的get_int方法,源码如下:

       inline int64_t Table::get_int(size_t col_ndx, size_t ndx) const noexcept{    if (is_nullable(col_ndx))        return get<util::Optional<int64_t>>(col_ndx, ndx).value_or(0);    else        return get<int64_t>(col_ndx, ndx);}template<> int64_t Table::get<int64_t>(size_t, size_t) const noexcept;template<> util::Optional<int64_t> Table::get<util::Optional<int64_t>>(size_t, size_t) const noexcept;
  14. 以上关于get_int方法的追踪就先到此为止了,简单理解native是realm数据库实现的核心,通过jni方式提供api给java层调用。

  15. 总结,Realm的自动更新的原理其实就是通过编译手段直接修改了用户的字节码文件,即将Model对象的get/set方法直接进行修改。

原创粉丝点击