createObject
在Realm源码分析之Writes中已经详细追踪过createObject的执行流程,此处不再赘述。
createObject有如下的两个重载方法,区别是如果Model没有指明主键使用前者,否则使用后者:
createObject(Class<E> clazz) createObject(Class<E> clazz, Object primaryKeyValue)
注意:每次使用createObject都会创建新的Model实例。
copyToRealm与copyToRealmOrUpdate
先上Model示例代码,注意是没有指明主键的:
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; } }
使用copyToRealm创建model示例,如下代码:
public void copyToRealm() { realm.executeTransactionAsync(new Realm.Transaction() { @Override public void execute(Realm realm) { Dog myDog = new Dog(); myDog.setName("Copy"); myDog.setAge(6); realm.copyToRealm(myDog); } }); }
开始追踪copyToRealm,如下源码:
public <E extends RealmModel> E copyToRealm(E object) { checkNotNullObject(object); return copyOrUpdate(object, false, new HashMap<RealmModel, RealmObjectProxy>()); }
上面代码先做了对象合法性判断,然后调用了copyOrUpdate这个静态方法,如下代码:
private <E extends RealmModel> E copyOrUpdate(E object, boolean update, Map<RealmModel, RealmObjectProxy> cache) { checkIfValid(); return configuration.getSchemaMediator().copyOrUpdate(this, object, update, cache); }
在Realm源码分析之初始化中已经分析过这个Mediator实例是DefaultRealmModuleMediator,它是有注解处理器自动生成,如下代码:
public <E extends RealmModel> E copyOrUpdate(Realm realm, E obj, boolean update, Map<RealmModel, RealmObjectProxy> cache) { // This cast is correct because obj is either // generated by RealmProxy or the original type extending directly from RealmObject @SuppressWarnings("unchecked") Class<E> clazz = (Class<E>) ((obj instanceof RealmObjectProxy) ? obj.getClass().getSuperclass() : obj.getClass()); if (clazz.equals(com.example.app.org.realm.demo.Dog.class)) { return clazz.cast(io.realm.DogRealmProxy.copyOrUpdate(realm, (com.example.app.org.realm.demo.Dog) obj, update, cache)); } throw getMissingProxyClassException(clazz); }
DefaultRealmModuleMediator只是做了中转,而是由DogRealmProxy负责实现,如下代码:
public static com.example.app.org.realm.demo.Dog copyOrUpdate(Realm realm, com.example.app.org.realm.demo.Dog object, boolean update, Map<RealmModel,RealmObjectProxy> cache) { if (object instanceof RealmObjectProxy && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm().threadId != realm.threadId) { throw new IllegalArgumentException("Objects which belong to Realm instances in other threads cannot be copied into this Realm instance."); } if (object instanceof RealmObjectProxy && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm().getPath().equals(realm.getPath())) { return object; } final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get(); RealmObjectProxy cachedRealmObject = cache.get(object); if (cachedRealmObject != null) { return (com.example.app.org.realm.demo.Dog) cachedRealmObject; } return copy(realm, object, update, cache); }
上述代码,先是做了合法性判断,然后从缓存中取实例并返回,取不到就调用如下的copy方法:
public static com.example.app.org.realm.demo.Dog copy(Realm realm, com.example.app.org.realm.demo.Dog newObject, boolean update, Map<RealmModel,RealmObjectProxy> cache) { RealmObjectProxy cachedRealmObject = cache.get(newObject); if (cachedRealmObject != null) { return (com.example.app.org.realm.demo.Dog) cachedRealmObject; } // rejecting default values to avoid creating unexpected objects from RealmModel/RealmList fields. com.example.app.org.realm.demo.Dog realmObject = realm.createObjectInternal(com.example.app.org.realm.demo.Dog.class, false, Collections.<String>emptyList()); cache.put(newObject, (RealmObjectProxy) realmObject); DogRealmProxyInterface realmObjectSource = (DogRealmProxyInterface) newObject; DogRealmProxyInterface realmObjectCopy = (DogRealmProxyInterface) realmObject; realmObjectCopy.realmSet$name(realmObjectSource.realmGet$name()); realmObjectCopy.realmSet$age(realmObjectSource.realmGet$age()); return realmObject; }
上面方法再次取缓存并返回,取不到就调用createObjectInternal方法创建实例,并未属性赋值。而createObjectInternal已经在Realm源码分析之Writes中已经分析过了,此处不再展开了。
至此可以得出copyToRealm与createObject的区别:copyToRealm会复用Realm中缓存的Model实例。
继续使用上述的Dog,使用copyToRealmOrUpdate来创建如下代码:
public void copyToRealmOrUpdate() { realm.executeTransactionAsync(new Realm.Transaction() { @Override public void execute(Realm realm) { Dog myDog = new Dog(); myDog.setName("Copy Update"); myDog.setAge(6); realm.copyToRealmOrUpdate(myDog); } }); }
直接运行的话会crash,log如下:
Caused by: java.lang.IllegalArgumentException: A RealmObject with no @PrimaryKey cannot be updated: class com.example.app.org.realm.demo.Dog at io.realm.Realm.checkHasPrimaryKey(Realm.java:1615) at io.realm.Realm.copyToRealmOrUpdate(Realm.java:1030) at com.example.app.org.realm.demo.RealmActivity$5.execute(RealmActivity.java:123) at io.realm.Realm$1.run(Realm.java:1508) at io.realm.internal.async.BgPriorityRunnable.run(BgPriorityRunnable.java:34) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:818)
上面的log很明显了,目前可以确定:copyToRealmOrUpdate方法是给指明了主键的model使用的。
让我们来揭开其神奇面纱,代码如下:
public <E extends RealmModel> E copyToRealmOrUpdate(E object) { checkNotNullObject(object); checkHasPrimaryKey(object.getClass()); return copyOrUpdate(object, true, new HashMap<RealmModel, RealmObjectProxy>()); }
private void checkHasPrimaryKey(Class<? extends RealmModel> clazz) { if (!schema.getTable(clazz).hasPrimaryKey()) { throw new IllegalArgumentException("A RealmObject with no @PrimaryKey cannot be updated: " + clazz.toString()); } }
上述代码先是对象判空,然后进行主键判断,最后调用的是copyOrUpdate方法。到目前为止,copyToRealm与copyToRealmOrUpdate唯一区别是调用copyOrUpdate这个静态方法传递的第二个参数不同,如下:
//copyToRealm copyOrUpdate(object, false, new HashMap<RealmModel, RealmObjectProxy>()) //copyToRealmOrUpdate copyOrUpdate(object, true, new HashMap<RealmModel, RealmObjectProxy>())
紧接着回去看6中的DogRealmProxy的copyOrUpdate与copy方法,可以发现这个boolean参数实际是没有使用的。因此,可以得出:对于未指明主键的model,其copyToRealm与copyToRealmOrUpdate底层实现是一致的。
接下来修改model并指明主键,如下代码:
public class Dog extends RealmObject { @PrimaryKey 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; } }
发现DogRealmProxy中的copyOrUpdate方法的实现已经改变了,如下代码:
public static com.example.app.org.realm.demo.Dog copyOrUpdate(Realm realm, com.example.app.org.realm.demo.Dog object, boolean update, Map<RealmModel,RealmObjectProxy> cache) { if (object instanceof RealmObjectProxy && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm().threadId != realm.threadId) { throw new IllegalArgumentException("Objects which belong to Realm instances in other threads cannot be copied into this Realm instance."); } if (object instanceof RealmObjectProxy && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm() != null && ((RealmObjectProxy) object).realmGet$proxyState().getRealm$realm().getPath().equals(realm.getPath())) { return object; } final BaseRealm.RealmObjectContext objectContext = BaseRealm.objectContext.get(); RealmObjectProxy cachedRealmObject = cache.get(object); if (cachedRealmObject != null) { return (com.example.app.org.realm.demo.Dog) cachedRealmObject; } com.example.app.org.realm.demo.Dog realmObject = null; boolean canUpdate = update; if (canUpdate) { Table table = realm.getTable(com.example.app.org.realm.demo.Dog.class); long pkColumnIndex = table.getPrimaryKey(); String value = ((DogRealmProxyInterface) object).realmGet$name(); long rowIndex = Table.NO_MATCH; if (value == null) { rowIndex = table.findFirstNull(pkColumnIndex); } else { rowIndex = table.findFirstString(pkColumnIndex, value); } if (rowIndex != Table.NO_MATCH) { try { objectContext.set(realm, table.getUncheckedRow(rowIndex), realm.schema.getColumnInfo(com.example.app.org.realm.demo.Dog.class), false, Collections.<String> emptyList()); realmObject = new io.realm.DogRealmProxy(); cache.put(object, (RealmObjectProxy) realmObject); } finally { objectContext.clear(); } } else { canUpdate = false; } } if (canUpdate) { return update(realm, realmObject, object, cache); } else { return copy(realm, object, update, cache); } }
相比没有加主键的方法,在取不到缓存之后它增加了去Table中查询主键的过程,如果在Table找到了匹配的主键就会新建一个DogRealmProxy对象并缓存起来,最后执行update操作,其源码如下:
static com.example.app.org.realm.demo.Dog update(Realm realm, com.example.app.org.realm.demo.Dog realmObject, com.example.app.org.realm.demo.Dog newObject, Map<RealmModel, RealmObjectProxy> cache) { DogRealmProxyInterface realmObjectTarget = (DogRealmProxyInterface) realmObject; DogRealmProxyInterface realmObjectSource = (DogRealmProxyInterface) newObject; realmObjectTarget.realmSet$age(realmObjectSource.realmGet$age()); return realmObject; }
上面的update方法其实赋值操作,而copyToRealm与copyToRealmOrUpdate就到此为止了。
结论
copyToRealm与copyToRealmOrUpdate:前者是给未指明主键的model使用的,后者则是给指明主键的model使用。
copyToRealm对应无主键参数的createObject,区别是copyToRealm会复用Realm缓存的model示例,不会每次都创建新的model实例。
copyToRealmOrUpdate对应有主键参数的createObject,区别是copyToRealmOrUpdate会复用Realm缓存的model示例,没有缓存实例就要去表中查询是否有匹配这个主键存在的记录,来决定是新建对象还是更新model实例。