bluelinelabs / bluelinelabs/Conductor
[Question] Replacing one controller with another upon configuration change
- Dominant language
- Java
- Stars
- 3.9k
- Forks
- 338
- PR merge metrics
- No merged PRs in 30d
Description
I have a controller in which I want to insert another controller, conditionally. If the phone is in portrait layout I want to add Portrait controller, if in landscape or if an additional setting is turned on then Landscape controller. I'm doing it the following way:
```Kotlin
// onControllerCreated is basically onCreateView()
override fun onControllerCreated(savedViewState: Bundle?) {
super.onControllerCreated(savedViewState)
// AndroidUtils.isSplitMode() is basically "orientation==LANDSCAPE" but it is also manually configurable
val controller = if (AndroidUtils.isSplitMode(currentContext())) {
SplitHomeController()
} else {
HomeController()
}
val transaction = RouterTransaction.with(controller)
.tag(controller.getControllerTag().tag)
getChildRouter(contentContainer).setRoot(transaction)
}
```
I'm using the same view (contentContainer) for both controllers and also using RetainViewMode.RETAIN_DETACH for all controllers.
The issue I'm having with this approach is that upon config change the old HomeController is still in the stack, it's getting rebound and then is destroyed (after `getChildRouter(contentContainer).setRoot(transaction)`) which is expected. I'm getting the following log:
```
// Normal app start (the phone is in landscape orientation)
2020-12-04 14:46:19.995 D/BaseController: MainController onCreateView()
2020-12-04 14:46:20.005 D/BaseController: SplitHomeController onCreateView()
2020-12-04 14:46:20.216 D/BaseController: HomeController onCreateView()
2020-12-04 14:46:20.436 D/BaseController: SplitBrowseController onCreateView()
2020-12-04 14:46:20.556 D/BaseController: CatalogController onCreateView()
2020-12-04 14:46:20.624 D/BaseController: SplitThreadController onCreateView()
2020-12-04 14:46:20.679 D/BaseController: MainController onAttach()
2020-12-04 14:46:20.680 D/BaseController: SplitHomeController onAttach()
2020-12-04 14:46:20.680 D/BaseController: HomeController onAttach()
2020-12-04 14:46:20.680 D/BaseController: SplitBrowseController onAttach()
2020-12-04 14:46:20.680 D/BaseController: CatalogController onAttach()
2020-12-04 14:46:20.681 D/BaseController: SplitThreadController onAttach()
// The app start with the phone in portrait orientation and then is rotated (logs are taken when rotation occurs)
2020-12-04 14:46:35.528 D/BaseController: MainController onDetach()
2020-12-04 14:46:35.528 D/BaseController: HomeController onDetach()
2020-12-04 14:46:35.528 D/BaseController: SlideBrowseController onDetach()
2020-12-04 14:46:35.528 D/BaseController: CatalogController onDetach()
2020-12-04 14:46:35.528 D/BaseController: ThreadController onDetach()
2020-12-04 14:46:35.531 D/BaseController: MainController onDestroyView()
2020-12-04 14:46:35.531 D/BaseController: HomeController onDestroyView()
2020-12-04 14:46:35.532 D/BaseController: SlideBrowseController onDestroyView()
2020-12-04 14:46:35.532 D/BaseController: CatalogController onDestroyView()
2020-12-04 14:46:35.532 D/BaseController: ThreadController onDestroyView()
2020-12-04 14:46:35.574 D/BaseController: MainController onCreateView()
2020-12-04 14:46:35.614 D/BaseController: HomeController onCreateView()
2020-12-04 14:46:35.620 D/BaseController: SlideBrowseController onCreateView()
2020-12-04 14:46:35.626 D/BaseController: CatalogController onCreateView()
2020-12-04 14:46:35.632 D/BaseController: ThreadController onCreateView()
2020-12-04 14:46:35.670 D/BaseController: MainController onAttach()
2020-12-04 14:46:36.029 D/BaseController: SplitHomeController onCreateView()
2020-12-04 14:46:36.051 D/BaseController: HomeController onCreateView()
2020-12-04 14:46:36.055 D/BaseController: SplitBrowseController onCreateView()
2020-12-04 14:46:36.057 D/BaseController: CatalogController onCreateView()
2020-12-04 14:46:36.065 D/BaseController: SplitThreadController onCreateView()
2020-12-04 14:46:36.068 D/BaseController: HomeController onDestroyView()
2020-12-04 14:46:36.069 D/BaseController: SlideBrowseController onDestroyView()
2020-12-04 14:46:36.069 D/BaseController: CatalogController onDestroyView()
2020-12-04 14:46:36.069 D/BaseController: ThreadController onDestroyView()
2020-12-04 14:46:36.072 D/BaseController: SplitHomeController onAttach()
2020-12-04 14:46:36.072 D/BaseController: HomeController onAttach()
2020-12-04 14:46:36.072 D/BaseController: SplitBrowseController onAttach()
2020-12-04 14:46:36.072 D/BaseController: CatalogController onAttach()
2020-12-04 14:46:36.073 D/BaseController: SplitThreadController onAttach()
```
While everything seems to be working I wonder if there is an official way to handle this situation (without old controller getting recteated)? Maybe I can somehow remove this controller during the call `Conductor.attachRouter(this, rootContainer, savedInstanceState)` before setting the root controller onto the main router?
I saw the MasterDetailController demo and that it uses two containers when the phone is in landscape orientation and two child routers but I wonder whether it's possible to only have one container.
Contributor guide
Research direction
Start with Conductor.attachRouter(this, rootContainer, savedInstanceState), the child router's setRoot(transaction), RetainViewMode.RETAIN_DETACH, and the MasterDetailController demo. Trace restoration and configuration-change behavior for a single container, then determine whether replacing the child controller without recreation is supported. Done means documenting the supported approach or confirming that the observed lifecycle is expected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, kotlin
- Domain
- mobile
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100