improved status list performance

This commit is contained in:
Mariotaku Lee 2017-05-18 23:20:42 +08:00
parent e673a339c8
commit d5fe843d8b
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
2 changed files with 46 additions and 24 deletions

View File

@ -372,7 +372,7 @@ class ParcelableActivitiesAdapter(
readInfoValueAction: (ActivityInfo) -> T, readInfoValueAction: (ActivityInfo) -> T,
readStatusValueAction: (status: ParcelableActivity) -> T, readStatusValueAction: (status: ParcelableActivity) -> T,
defValue: T, raw: Boolean = false): T { defValue: T, raw: Boolean = false): T {
val data = data val data = this.data
if (data is ObjectCursor) { if (data is ObjectCursor) {
val dataPosition = position - activityStartIndex val dataPosition = position - activityStartIndex
if (dataPosition < 0 || dataPosition >= getActivityCount(true)) { if (dataPosition < 0 || dataPosition >= getActivityCount(true)) {

View File

@ -20,7 +20,6 @@
package org.mariotaku.twidere.adapter package org.mariotaku.twidere.adapter
import android.content.Context import android.content.Context
import android.database.Cursor
import android.database.CursorIndexOutOfBoundsException import android.database.CursorIndexOutOfBoundsException
import android.support.v4.widget.Space import android.support.v4.widget.Space
import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView
@ -120,6 +119,7 @@ abstract class ParcelableStatusesAdapter(
private var displayDataCount: Int = 0 private var displayDataCount: Int = 0
private var showingActionCardId = RecyclerView.NO_ID private var showingActionCardId = RecyclerView.NO_ID
private val reuseStatus = ParcelableStatus() private val reuseStatus = ParcelableStatus()
private var infoCache: Array<StatusInfo?>? = null
override val itemCounts = ItemCounts(5) override val itemCounts = ItemCounts(5)
@ -207,51 +207,48 @@ abstract class ParcelableStatusesAdapter(
override fun getItemId(position: Int): Long { override fun getItemId(position: Int): Long {
val countIndex = getItemCountIndex(position) val countIndex = getItemCountIndex(position)
when (countIndex) { return (countIndex.toLong() shl 32) or when (countIndex) {
ITEM_INDEX_PINNED_STATUS -> { ITEM_INDEX_PINNED_STATUS -> {
val status = pinnedStatuses!![position - getItemStartPosition(ITEM_INDEX_PINNED_STATUS)] val status = pinnedStatuses!![position - getItemStartPosition(ITEM_INDEX_PINNED_STATUS)]
return (countIndex.toLong() shl 32) or status.hashCode().toLong() return status.hashCode().toLong()
} }
ITEM_INDEX_STATUS -> return (countIndex.toLong() shl 32) or getFieldValue(position, { cursor, indices -> ITEM_INDEX_STATUS -> getFieldValue(position, { (_, accountKey, id) ->
val accountKey = UserKey.valueOf(cursor.getString(indices[Statuses.ACCOUNT_KEY]))
val id = cursor.getString(indices[Statuses.ID])
return@getFieldValue ParcelableStatus.calculateHashCode(accountKey, id) return@getFieldValue ParcelableStatus.calculateHashCode(accountKey, id)
}, { status -> }, { status ->
return@getFieldValue status.hashCode() return@getFieldValue status.hashCode()
}, -1).toLong() }, -1).toLong()
else -> return (countIndex.toLong() shl 32) or position.toLong() else -> position.toLong()
} }
} }
override fun getStatusId(position: Int, raw: Boolean): String { override fun getStatusId(position: Int, raw: Boolean): String {
return getFieldValue(position, { cursor, indices -> return getFieldValue(position, { info ->
return@getFieldValue cursor.getString(indices[Statuses.ID]) return@getFieldValue info.id
}, { status -> }, { status ->
return@getFieldValue status.id return@getFieldValue status.id
}, "") }, "")
} }
fun getStatusSortId(position: Int, raw: Boolean): Long { fun getStatusSortId(position: Int, raw: Boolean): Long {
return getFieldValue(position, { cursor, indices -> return getFieldValue(position, { info ->
return@getFieldValue cursor.safeGetLong(indices[Statuses.SORT_ID]) return@getFieldValue info.sortId
}, { status -> }, { status ->
return@getFieldValue status.sort_id return@getFieldValue status.sort_id
}, -1L, raw) }, -1L, raw)
} }
override fun getStatusTimestamp(position: Int, raw: Boolean): Long { override fun getStatusTimestamp(position: Int, raw: Boolean): Long {
return getFieldValue(position, { cursor, indices -> return getFieldValue(position, { info ->
return@getFieldValue cursor.safeGetLong(indices[Statuses.TIMESTAMP]) return@getFieldValue info.timestamp
}, { status -> }, { status ->
return@getFieldValue status.timestamp return@getFieldValue status.timestamp
}, -1L) }, -1L)
} }
override fun getStatusPositionKey(position: Int, raw: Boolean): Long { override fun getStatusPositionKey(position: Int, raw: Boolean): Long {
return getFieldValue(position, { cursor, indices -> return getFieldValue(position, { info ->
val positionKey = cursor.safeGetLong(indices[Statuses.POSITION_KEY]) if (info.positionKey > 0) return@getFieldValue info.positionKey
if (positionKey > 0) return@getFieldValue positionKey return@getFieldValue info.timestamp
return@getFieldValue cursor.safeGetLong(indices[Statuses.TIMESTAMP])
}, { status -> }, { status ->
val positionKey = status.position_key val positionKey = status.position_key
if (positionKey > 0) return@getFieldValue positionKey if (positionKey > 0) return@getFieldValue positionKey
@ -261,8 +258,8 @@ abstract class ParcelableStatusesAdapter(
override fun getAccountKey(position: Int, raw: Boolean): UserKey { override fun getAccountKey(position: Int, raw: Boolean): UserKey {
val def: UserKey? = null val def: UserKey? = null
return getFieldValue(position, { cursor, indices -> return getFieldValue(position, { info ->
return@getFieldValue UserKey.valueOf(cursor.getString(indices[Statuses.ACCOUNT_KEY])) return@getFieldValue info.accountKey
}, { status -> }, { status ->
return@getFieldValue status.account_key return@getFieldValue status.account_key
}, def, raw)!! }, def, raw)!!
@ -422,18 +419,31 @@ abstract class ParcelableStatusesAdapter(
} }
private inline fun <T> getFieldValue(position: Int, private inline fun <T> getFieldValue(position: Int,
readCursorValueAction: (cursor: Cursor, indices: ObjectCursor.CursorIndices<ParcelableStatus>) -> T, readInfoValueAction: (StatusInfo) -> T,
readStatusValueAction: (status: ParcelableStatus) -> T, readStatusValueAction: (status: ParcelableStatus) -> T,
defValue: T, raw: Boolean = false): T { defValue: T, raw: Boolean = false): T {
val data = this.data
if (data is ObjectCursor) { if (data is ObjectCursor) {
val dataPosition = position - statusStartIndex val dataPosition = position - statusStartIndex
if (dataPosition < 0 || dataPosition >= getStatusCount(true)) { if (dataPosition < 0 || dataPosition >= getStatusCount(true)) {
throw CursorIndexOutOfBoundsException("index: $position, valid range is $0..${getStatusCount(true)}") throw CursorIndexOutOfBoundsException("index: $position, valid range is $0..${getStatusCount(true)}")
} }
val cursor = (data as ObjectCursor).cursor val cursor = data.cursor
if (!cursor.safeMoveToPosition(dataPosition)) return defValue if (!cursor.safeMoveToPosition(dataPosition)) return defValue
val indices = (data as ObjectCursor).indices val indices = data.indices
return readCursorValueAction(cursor, indices) val info = infoCache?.get(dataPosition) ?: run {
val _id = cursor.safeGetLong(indices[Statuses._ID])
val accountKey = UserKey.valueOf(cursor.getString(indices[Statuses.ACCOUNT_KEY]))
val id = cursor.getString(indices[Statuses.ID])
val timestamp = cursor.safeGetLong(indices[Statuses.TIMESTAMP])
val sortId = cursor.safeGetLong(indices[Statuses.SORT_ID])
val positionKey = cursor.safeGetLong(indices[Statuses.POSITION_KEY])
val gap = cursor.getInt(indices[Statuses.IS_GAP]) == 1
val newInfo = StatusInfo(_id, accountKey, id, timestamp, sortId, positionKey, gap)
infoCache?.set(dataPosition, newInfo)
return@run newInfo
}
return readInfoValueAction(info)
} }
return readStatusValueAction(getStatus(position, raw)) return readStatusValueAction(getStatus(position, raw))
} }
@ -474,6 +484,18 @@ abstract class ParcelableStatusesAdapter(
itemCounts[ITEM_INDEX_LOAD_END_INDICATOR] = if (ILoadMoreSupportAdapter.END in loadMoreIndicatorPosition) 1 else 0 itemCounts[ITEM_INDEX_LOAD_END_INDICATOR] = if (ILoadMoreSupportAdapter.END in loadMoreIndicatorPosition) 1 else 0
} }
data class StatusInfo(
val _id: Long,
val accountKey: UserKey,
val id: String,
val timestamp: Long,
val sortId: Long,
val positionKey: Long,
val gap: Boolean
)
companion object { companion object {
const val VIEW_TYPE_STATUS = 2 const val VIEW_TYPE_STATUS = 2
const val VIEW_TYPE_EMPTY = 3 const val VIEW_TYPE_EMPTY = 3