4.3 KiB
4.3 KiB
Fluffytrix Android Bug Hunter Memory
Architecture
- Messages stored descending (newest at index 0) for
reverseLayout = trueLazyColumn - Thread replies filtered from main timeline into
threadMessageCache[roomId][threadRootEventId] - Thread detection: parse
content.m.relates_to.rel_type == "m.thread"from raw JSON viaeventItem.lazyProvider.debugInfo().originalJson(must be in try/catch) - Space hierarchy: top-level spaces → child spaces → rooms. Orphan rooms = rooms not in any space
- Static channel ordering enforced via
_channelOrderMap(DataStore-persisted) MainViewModelusesProcessLifecycleOwnerobserver to pause/resume sync on app background/foregroundAuthRepositoryholdsmatrixClientandsyncServiceas plainvar(not thread-safe, accessed from IO threads)
Recurring Bug Patterns
Threading / Coroutines
loadMoreMessages()launches onDispatchers.Default(omits dispatcher), sotimeline.paginateBackwards()runs on main thread — SDK calls must useDispatchers.IOTimelineListener.onUpdatelaunchesviewModelScope.launch(Dispatchers.Default)— fine for CPU work, but the mutex-protected list manipulation inside is correctprocessRoomsuseswithContext(Dispatchers.Default)for CPU-heavy room processing — correct patternrebuildThreadListandupdateThreadMessagesViewcalled from withinDispatchers.Defaultcoroutine (insideonUpdate) — these write to_roomThreadsand_threadMessagesStateFlows, which is safe from any thread
Memory Leaks
messageCache,messageIds,memberCache,threadMessageCacheare plainmutableMapOfon ViewModel — accessed from multiple coroutines without synchronization (race condition potential)senderAvatarCacheandsenderNameCachesimilarly unsynchronizedactiveTimelineis written fromDispatchers.IOcoroutine and read fromDispatchers.Defaultin the listener — not volatile/synchronized
Compose
rememberLazyListState()inMessageTimelineis recreated on channel/thread switch — loses scroll position. Should be keyed per channel or held in ViewModelcollectAsState()withoutrepeatOnLifecycleinMainScreen— acceptable sincecollectAsStateinternally usesrepeatOnLifecycle(STARTED)in Compose lifecycle-runtime- Thread items in
ChannelListrendered withforloop insideLazyColumnitems block — not usingitem(key=)for thread rows, causing missed optimizations but not a correctness bug
Visual
senderColorsarray contains hardcoded hex colors — violates Material You convention but is intentional Discord-style sender coloring (acceptable)Color.Whiteused directly inVideoContentplay button and fullscreen viewers — minor Material You violation but acceptable for media overlays
Data Correctness
colorForSender:name.hashCode().ushr(1) % senderColors.size—hashCode()can be negative;ushr(1)makes it non-negative, so modulo is safe. Correct.- Binary search in
processEventItemfor descending insert: comparator ismsg.timestamp.compareTo(it.timestamp)— this inserts newer messages at lower indices (ascending by timestamp reversed). ForreverseLayoutthis is correct. - Thread binary search uses same comparator — threads stored ascending by timestamp, which for
reverseLayoutis correct (newest at index 0 visually). sendThreadMessagesends as plain message without thread relation — documented known limitation/TODO in code
Build
isMinifyEnabled = truein debug build — unusual, slows debug builds and can make debugging harder, but not a bug per sekotlin = "2.2.10"in version catalog — check that this is a valid release (2.2.0 is latest as of mid-2025; 2.2.10 may be typo for 2.2.0 or future patch)
Key File Paths
- ViewModel:
app/src/main/java/com/example/fluffytrix/ui/screens/main/MainViewModel.kt - Main screen:
app/src/main/java/com/example/fluffytrix/ui/screens/main/MainScreen.kt - Message timeline:
app/src/main/java/com/example/fluffytrix/ui/screens/main/components/MessageTimeline.kt - Channel list:
app/src/main/java/com/example/fluffytrix/ui/screens/main/components/ChannelList.kt - Auth repo:
app/src/main/java/com/example/fluffytrix/data/repository/AuthRepository.kt - Preferences:
app/src/main/java/com/example/fluffytrix/data/local/PreferencesManager.kt