Try this. It's not perfect (I don't even know what all Cursor methods do...), but it works for me:
open class DetachedRealmCursor<T : RealmModel>(val clazz: Class<T>, fn: (Realm) -> RealmResults<T>) : Cursor {
open val result: List<T> by lazy {
val realm = Realm.getDefaultInstance()
val list = realm.copyFromRealm(fn(realm))
//val list = fn(realm)
realm.close()
closed = false
list
}
var current = 0
protected var notUri: Uri? = null
protected var contencontObservable = ContentObservable()
protected var datasetObservable = DataSetObservable()
private val columnTypes: List<Int>
private val columnNames: List<String>
private var extras : Bundle = Bundle.EMPTY
private var closed = true
val FIELD_TYPE_REALMLIST = Cursor.FIELD_TYPE_BLOB+1
init {
columnTypes = getFieldTypes()
columnNames = getFieldNames()
}
private fun getFieldNames(): List<String> {
return clazz.declaredFields?.map {
it.name
} ?: listOf("col1")
}
private fun getFieldTypes(): List<Int> {
return clazz.declaredFields?.map {
when (it.type) {
String::class.java -> Cursor.FIELD_TYPE_STRING
Float::class.java -> Cursor.FIELD_TYPE_FLOAT
Int::class.java -> Cursor.FIELD_TYPE_INTEGER
RealmList::class.java -> FIELD_TYPE_REALMLIST
else -> Cursor.FIELD_TYPE_NULL
}
} ?: listOf(Cursor.FIELD_TYPE_STRING)
}
private fun getValueFromColumn(col: Int, pos: Int): Any? {
val field = result[pos].javaClass.getDeclaredField(getColumnName(col))
field.isAccessible = true
return field.get(result[pos])
}
override fun moveToPosition(p0: Int): Boolean =
if (p0 >= -1 && p0 <= result.size) {
current = p0
true
} else
false
override fun moveToFirst(): Boolean =
if (result.isNotEmpty()) {
current = 0
true
} else false
override fun move(p0: Int): Boolean = if (p0 >= -1 && p0 <= result.size) {
current = p0
true
} else {
false
}
override fun moveToPrevious(): Boolean = if (current > -1) {
current--
true
} else false
override fun moveToNext(): Boolean = if (current < result.size) {
current++
true
} else false
override fun isBeforeFirst(): Boolean = current == -1
override fun moveToLast(): Boolean = if (result.isNotEmpty()) {
current = result.size - 1
true
} else false
override fun isAfterLast(): Boolean = current >= result.size
override fun getColumnIndexOrThrow(p0: String): Int {
val found = columnNames.indexOf(p0)
return if (found == -1) throw IllegalArgumentException() else found
}
override fun getColumnNames(): Array<String> = columnNames.toTypedArray()
override fun getType(p0: Int): Int = columnTypes[p0]
override fun getColumnName(p0: Int): String = columnNames[p0]
override fun getColumnIndex(p0: String?): Int = columnNames.indexOf(p0)
override fun getColumnCount(): Int = columnNames.size
override fun getNotificationUri(): Uri? = notUri
override fun deactivate() {}
override fun requery(): Boolean = true
override fun registerContentObserver(p0: ContentObserver) {
// Register an observer that is called when changes happen to the content backing this cursor.
// Typically the data set won't change until requery() is called.
contencontObservable.registerObserver(p0)
}
override fun registerDataSetObserver(p0: DataSetObserver) {
// Register an observer that is called when changes happen to the contents of the this
// cursors data set, for example, when the data set is changed via requery(), deactivate(), or close().
datasetObservable.registerObserver(p0)
}
override fun unregisterContentObserver(p0: ContentObserver?) {
if(!closed) {
contencontObservable.unregisterObserver(p0)
}
}
override fun unregisterDataSetObserver(p0: DataSetObserver?) {
datasetObservable.unregisterObserver(p0)
}
override fun getWantsAllOnMoveCalls(): Boolean = false
override fun getPosition(): Int = current
override fun close() {
closed = true
contencontObservable.unregisterAll()
datasetObservable.notifyInvalidated()
}
override fun isClosed() = closed
override fun getCount(): Int = result.size
override fun isFirst(): Boolean = current == 1
override fun isLast(): Boolean {
return current == result.size - 1
}
override fun isNull(p0: Int): Boolean = getValueFromColumn(p0, current) == null
// poniższe można zamienić na getValueFromColumn
override fun getLong(p0: Int): Long = getValueFromColumn(p0, current) as Long
override fun getFloat(p0: Int): Float = getValueFromColumn(p0, current) as Float
override fun getInt(p0: Int): Int = getValueFromColumn(p0, current) as Int
override fun getBlob(p0: Int): ByteArray = getValueFromColumn(p0, current) as ByteArray
override fun getShort(p0: Int): Short = getValueFromColumn(p0, current) as Short
override fun getString(p0: Int): String = getValueFromColumn(p0, current) as String
override fun getDouble(p0: Int): Double = getValueFromColumn(p0, current) as Double
fun getList(p0: Int): RealmList<*> = getValueFromColumn(p0, current) as RealmList<*>
override fun setNotificationUri(p0: ContentResolver?, p1: Uri?) {
notUri = p1
}
override fun copyStringToBuffer(p0: Int, p1: CharArrayBuffer?) {}
override fun respond(extras: Bundle): Bundle = Bundle.EMPTY
override fun getExtras(): Bundle = extras
override fun setExtras(p0: Bundle) {
extras = p0
}
}
Then you can use it like:
val cursor = RealmCursor(RAlbum::class.java)
{ it.where(RAlbum::class.java).distinct("sTitle") }
Of course if you do all your work on the same thread you can directly use realm
instance instead of doing realm.copyFromRealm(fn(realm))