Home » Android » android – Get cursor by using Realm library

android – Get cursor by using Realm library

Posted by: admin June 15, 2020 Leave a comment

Questions:

I am using cursor adapter for my list view. I was planning to use content resolvers to get a cursor. Now i changed my mind to give it a chance to learn Realm.

How can i get “Cursor” object by using realm? By the way, i would also be appreciate give a few snippet to sort cursor.

Thanks.

final class Contact extends RealmObject {

private CharSequence mName;


public CharSequence getName() {
    return mName;
}

public void setName(CharSequence name) {
    this.mName = name;
}

}

final class ContactListAdapter extends CursorRecyclerViewAdapter<ContactListAdapter.ContactHolder> implements View.OnClickListener {

private OnContactClickListener mListener;


public ContactListAdapter(Context context, Cursor cursor) {
    super(context, cursor);
}

@Override
public ContactHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = getLayoutInflater().inflate(R.layout.cell_contact, parent, false);

    ContactHolder contactHolder = new ContactHolder(itemView);
    contactHolder.mContactPhotoImageView.setOnClickListener(this);

    return contactHolder;
}

@Override
public void onBindViewHolder(ContactHolder viewHolder, Cursor cursor) {
    viewHolder.mNameTextView.setText("Emre Akturk");

    int position = cursor.getPosition();
    viewHolder.mContactPhotoImageView.setImageResource(R.mipmap.ic_launcher);
    viewHolder.mContactPhotoImageView.setTag(position);
}

@Override
public int getItemCount() {
    return 5;
}

public void setOnContactClickListener(OnContactClickListener callback) {
    this.mListener = callback;
}

@Override
public void onClick(View v) {
    if (mListener == null) {
        return;
    }

    int position = (int) v.getTag();
    if (position == -1) {
        mListener.onContactCallClicked(v, getCursor(), position);
    } else {
        mListener.onContactAddClicked(v, position);
    }
}

public interface OnContactClickListener {
    void onContactCallClicked(View v, Cursor cursor, int position);

    void onContactAddClicked(View v, int position);
}

protected class ContactHolder extends RecyclerView.ViewHolder {
    private TextView mNameTextView;
    private CircleImageView mContactPhotoImageView;

    protected ContactHolder(View itemView) {
        super(itemView);
        mNameTextView = (TextView) itemView.findViewById(R.id.cell_contact_name_textview);
        mContactPhotoImageView = (CircleImageView) itemView.findViewById(R.id.cell_contact_photo_imageview);
    }
}

}

How to&Answers:

Christian from Realm here. Realm currently doesn’t expose a Cursor although it is something we want to do: https://github.com/realm/realm-java/issues/438

If you want to use a RecyclerView with Realm I would recommend this blogpost for advice on how to integrate them: http://gradlewhy.ghost.io/realm-results-with-recyclerview/

Answer:

Currently, i think that Realm doesn’t support the ContentProvider architecture.

But, just to match on the Cursor interface, you can fetch a list result from Realm and create a MatrixCursor from this data.

Sure, is not the best solution for RAM performance. Cursor support is really a deep needed feature on Realm.

Answer:

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))