MainActivity companion object 안티패턴 제거 및 SharedFlow 이벤트 버스 전환#372
MainActivity companion object 안티패턴 제거 및 SharedFlow 이벤트 버스 전환#372
Conversation
…이벤트 버스로 전환 - ScreenRefreshEventBus 도입: Fragment 직접 참조 대신 SharedFlow 기반 이벤트 통신 - VisitorModeManager 도입: static mutable 변수 대신 Hilt Singleton으로 방문자 모드 관리 - MainActivity.companion에서 Fragment 인스턴스 참조, isVisitorMode, updateXxxScreen() 제거 - DiscoverFragment/StorageScrapFragment의 self-registration(onAttach/onDestroy) 제거 - Adapter에 isVisitorMode 람다 파라미터 전달로 정적 참조 제거
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughReplaces static MainActivity state with injected singletons: Changes
Sequence Diagram(s)sequenceDiagram
participant Upload as DiscoverUploadActivity
participant Bus as ScreenRefreshEventBus
participant Discover as DiscoverFragment
participant Scrap as StorageScrapFragment
rect rgba(200,200,255,0.5)
Upload->>Bus: emit(RefreshDiscoverCourses)
end
rect rgba(200,255,200,0.5)
Bus->>Discover: events -> RefreshDiscoverCourses
Discover->>Discover: refreshDiscoverCourses()
end
rect rgba(255,200,200,0.5)
Bus->>Scrap: events -> RefreshStorageScrap
Scrap->>Scrap: getMyScrapCourses()
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
app/src/main/java/com/runnect/runnect/presentation/event/VisitorModeManager.kt (1)
14-18: Optional: Consider caching login status for cleaner code.The
isVisitorModegetter performs a SharedPreferences read (getAccessToken()→ synchronizedPreferenceManager.getString()) on every invocation. In the current implementation, this is called only in click event listeners (DiscoverRecommendAdapterandDiscoverMarathonAdapter), so the frequency is low. However, caching the value at the call site can simplify the code and avoid repeated disk access:♻️ Option: Cache at call site
// In Fragment/Activity before creating adapter: val isVisitorMode = visitorModeManager.isVisitorMode adapter = DiscoverMultiViewAdapter( // ... isVisitorMode = { isVisitorMode }, // captured once // ... )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/runnect/runnect/presentation/event/VisitorModeManager.kt` around lines 14 - 18, The isVisitorMode getter calls context.getAccessToken() (which reads SharedPreferences) on every access; to avoid repeated disk reads and simplify call sites, capture visitorModeManager.isVisitorMode once where the adapters are created (e.g., in the Fragment/Activity before constructing DiscoverMultiViewAdapter/DiscoverRecommendAdapter/DiscoverMarathonAdapter) and pass that captured Boolean into the adapters (or pass a lambda that returns the captured value) instead of passing the live getter; this keeps the existing VisitorModeManager.isVisitorMode, reduces SharedPreferences accesses from LoginStatus.getLoginStatus(context.getAccessToken()), and makes adapter code cleaner.app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt (1)
43-45: Remove the dead visitor-mode hook.
checkVisitorMode()no longer does any initialization, butonCreate()still calls it. Keeping the empty hook makes the startup path look stateful when it isn't.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt` around lines 43 - 45, Remove the dead hook by deleting the empty checkVisitorMode() function and its call from MainActivity.onCreate(), since VisitorModeManager now handles visitor-mode via Hilt; search for the checkVisitorMode() declaration and any invocation in MainActivity (including onCreate()) and remove both so startup path is not misleading, keeping any real initialization in VisitorModeManager instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/com/runnect/runnect/presentation/discover/DiscoverFragment.kt`:
- Around line 98-108: The refresh events are currently lost because
ScreenRefreshEventBus uses MutableSharedFlow with no replay; update the event
bus to buffer the last event (e.g., initialize
MutableSharedFlow<ScreenRefreshEvent>(replay = 1)) or replace it with a
StateFlow that holds the current refresh state so new collectors always see the
latest value; ensure this change is made in the ScreenRefreshEventBus
initialization (affecting collectors like
DiscoverFragment.collectScreenRefreshEvents and StorageScrapFragment) so
ScreenRefreshEvent.RefreshDiscoverCourses emitted while the view is destroyed
will be delivered when the fragment re-subscribes.
In
`@app/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.kt`:
- Around line 124-127: The event emission is happening after startActivity()
inside DiscoverUploadActivity and inside lifecycleScope.launch, which can be
cancelled when the Activity finishes and lost with MutableSharedFlow(replay=0);
move or duplicate the emit so it cannot be missed: emit
ScreenRefreshEvent.RefreshDiscoverCourses before calling startActivity() (or
change the flow to replay=1 in ScreenRefreshEventBus), or perform the emit from
a non-cancellable/app-scoped coroutine (e.g., an application-level scope or the
fragment/viewmodel scope) instead of lifecycleScope.launch so the refresh event
is delivered reliably; reference screenRefreshEventBus.emit, startActivity(),
lifecycleScope.launch, applyScreenExitAnimation, and
ScreenRefreshEventBus/MutableSharedFlow(replay=0) when applying the change.
In
`@app/src/main/java/com/runnect/runnect/presentation/event/ScreenRefreshEventBus.kt`:
- Line 16: The MutableSharedFlow _events in ScreenRefreshEventBus currently uses
the default replay=0 so emissions sent in
DiscoverUploadActivity.handleReturnToDiscover() can be missed by the target
Fragment; change the MutableSharedFlow construction in ScreenRefreshEventBus to
use replay = 1 so the latest event is available to late subscribers, and if you
need to avoid delivering stale events to future subscribers call
_events.resetReplayCache() after the consumer processes the event (refer to the
ScreenRefreshEventBus._events symbol and
DiscoverUploadActivity.handleReturnToDiscover() to locate the emit and
collection sites).
---
Nitpick comments:
In
`@app/src/main/java/com/runnect/runnect/presentation/event/VisitorModeManager.kt`:
- Around line 14-18: The isVisitorMode getter calls context.getAccessToken()
(which reads SharedPreferences) on every access; to avoid repeated disk reads
and simplify call sites, capture visitorModeManager.isVisitorMode once where the
adapters are created (e.g., in the Fragment/Activity before constructing
DiscoverMultiViewAdapter/DiscoverRecommendAdapter/DiscoverMarathonAdapter) and
pass that captured Boolean into the adapters (or pass a lambda that returns the
captured value) instead of passing the live getter; this keeps the existing
VisitorModeManager.isVisitorMode, reduces SharedPreferences accesses from
LoginStatus.getLoginStatus(context.getAccessToken()), and makes adapter code
cleaner.
In `@app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt`:
- Around line 43-45: Remove the dead hook by deleting the empty
checkVisitorMode() function and its call from MainActivity.onCreate(), since
VisitorModeManager now handles visitor-mode via Hilt; search for the
checkVisitorMode() declaration and any invocation in MainActivity (including
onCreate()) and remove both so startup path is not misleading, keeping any real
initialization in VisitorModeManager instead.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 929d25d1-2022-44e6-8dac-80a9af9aa1bb
📒 Files selected for processing (15)
app/src/main/java/com/runnect/runnect/binding/BaseVisitorFragment.ktapp/src/main/java/com/runnect/runnect/presentation/MainActivity.ktapp/src/main/java/com/runnect/runnect/presentation/MainPager.ktapp/src/main/java/com/runnect/runnect/presentation/detail/CourseDetailActivity.ktapp/src/main/java/com/runnect/runnect/presentation/discover/DiscoverFragment.ktapp/src/main/java/com/runnect/runnect/presentation/discover/adapter/DiscoverMarathonAdapter.ktapp/src/main/java/com/runnect/runnect/presentation/discover/adapter/DiscoverRecommendAdapter.ktapp/src/main/java/com/runnect/runnect/presentation/discover/adapter/multiview/DiscoverMultiViewAdapter.ktapp/src/main/java/com/runnect/runnect/presentation/discover/adapter/multiview/DiscoverMultiViewHolder.ktapp/src/main/java/com/runnect/runnect/presentation/discover/adapter/multiview/DiscoverMultiViewHolderFactory.ktapp/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.ktapp/src/main/java/com/runnect/runnect/presentation/draw/DrawActivity.ktapp/src/main/java/com/runnect/runnect/presentation/event/ScreenRefreshEventBus.ktapp/src/main/java/com/runnect/runnect/presentation/event/VisitorModeManager.ktapp/src/main/java/com/runnect/runnect/presentation/storage/StorageScrapFragment.kt
app/src/main/java/com/runnect/runnect/presentation/discover/DiscoverFragment.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/runnect/runnect/presentation/discover/upload/DiscoverUploadActivity.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/runnect/runnect/presentation/event/ScreenRefreshEventBus.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/runnect/runnect/presentation/MainActivity.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/runnect/runnect/presentation/discover/DiscoverFragment.kt
Outdated
Show resolved
Hide resolved
| class ScreenRefreshEventBus @Inject constructor() { | ||
| private val _events = MutableSharedFlow<ScreenRefreshEvent>() | ||
| val events: SharedFlow<ScreenRefreshEvent> = _events.asSharedFlow() |
There was a problem hiding this comment.
기존엔 생명주기를 고려하지 않은 코드여서 안정적이지 않았음. 그래서 개선을 해내야 하는데 지금의 방식은 sharedFlow를 쓰면 이벤트를 쏘고나서 백그라운드 전환 등으로 이벤트가 소실됐을 때 복구가 안 돼서 문제가 생길 것 같은데 검토 필요해보임.
There was a problem hiding this comment.
반영 완료 (1356ba1)
MutableSharedFlow(replay = 1)로 변경하여 마지막 이벤트를 버퍼링- late subscriber도 최신 이벤트를 수신 가능
- 이 앱에서 이벤트는 "화면 새로고침" 용도라 stale replay 시 최악의 경우 불필요한 리프레시 1회 — 실질적 부작용 없음
- SharedFlow 공식 문서 참고
- checkVisitorMode() dead code 삭제 - collectScreenRefreshEvents()를 addObserver() 안으로 이동 - MutableSharedFlow(replay=1)로 이벤트 소실 방지 - DiscoverUploadActivity에서 emit을 startActivity 전으로 이동
코드래빗 Nitpick 대응VisitorModeManager SharedPreferences 캐싱 제안 — 현재 코드 유지
|
작업 배경
MainActivity.companion에 Fragment 인스턴스를 직접 보관하고,isVisitorMode를 static mutable 변수로 전역 공유하는 안티패턴이 존재변경 사항
ScreenRefreshEventBus.kt@SingletonSharedFlow 기반 화면 간 이벤트 디스패처VisitorModeManager.kt@Singleton방문자 모드 상태 관리 (토큰 기반 판단)MainActivity.ktisVisitorMode,updateXxxScreen()제거DiscoverFragment.ktStorageScrapFragment.ktMainPager.ktDrawActivity.ktVisitorModeManager주입으로 교체CourseDetailActivity.ktVisitorModeManager+ 이벤트 버스 주입DiscoverUploadActivity.ktDiscoverRecommendAdapter.ktisVisitorMode람다 파라미터로 변경DiscoverMarathonAdapter.ktisVisitorMode람다 파라미터로 변경BaseVisitorFragment.ktVisitorModeManager주입DiscoverMultiViewAdapter/Holder/FactoryisVisitorMode람다 전달 체인영향 범위
Test Plan
🤖 Generated with Claude Code
Summary by CodeRabbit