Conductor
A small, yet full-featured framework that allows building View-based Android applications
Top Related Projects
Name UI states, navigate between them, remember where you've been.
A Model-View-Presenter / Model-View-Intent library for modern Android apps
Mavericks: Android on Autopilot
Uber's cross-platform mobile architecture framework.
A sample Android app which showcases advanced usage of Dagger among other open source libraries.
Quick Overview
Conductor is a small, yet full-featured framework for building Android applications. It provides a lightweight alternative to Fragments, offering improved lifecycle management, custom view-based navigation, and easy-to-use transitions between screens.
Pros
- Simplified lifecycle management compared to Fragments
- Easy and flexible navigation between screens
- Smooth and customizable transitions
- Lightweight and easy to integrate into existing projects
Cons
- Learning curve for developers accustomed to Fragment-based architecture
- Limited community resources compared to the standard Android framework
- May require additional effort to integrate with some third-party libraries designed for Fragments
- Not officially supported by Google or part of the Android framework
Code Examples
- Creating a basic Controller:
class HomeController : Controller() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
return inflater.inflate(R.layout.controller_home, container, false)
}
}
- Navigating to a new Controller:
router.pushController(RouterTransaction.with(DetailController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
- Handling back navigation:
override fun onBackPressed(): Boolean {
if (router.backstackSize > 1) {
router.handleBack()
return true
}
return super.onBackPressed()
}
- Using ControllerChangeHandler for custom transitions:
class FadeChangeHandler : ControllerChangeHandler() {
override fun performChange(
container: ViewGroup,
from: View?,
to: View?,
isPush: Boolean,
changeListener: ControllerChangeCompletedListener
) {
if (to != null) {
container.addView(to)
to.alpha = 0f
to.animate().alpha(1f).setDuration(300).start()
}
if (from != null) {
from.animate().alpha(0f).setDuration(300).withEndAction {
container.removeView(from)
changeListener.onChangeCompleted()
}.start()
} else {
changeListener.onChangeCompleted()
}
}
}
Getting Started
- Add Conductor to your project's
build.gradle
:
dependencies {
implementation 'com.bluelinelabs:conductor:3.1.5'
}
- Set up your Activity to use Conductor:
class MainActivity : AppCompatActivity() {
private lateinit var router: Router
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
router = Conductor.attachRouter(this, findViewById(R.id.controller_container), savedInstanceState)
if (!router.hasRootController()) {
router.setRoot(RouterTransaction.with(HomeController()))
}
}
override fun onBackPressed() {
if (!router.handleBack()) {
super.onBackPressed()
}
}
}
Competitor Comparisons
Name UI states, navigate between them, remember where you've been.
Pros of Flow
- Simpler API with a focus on single-Activity applications
- Lightweight and minimalistic approach to navigation
- Better integration with Kotlin coroutines
Cons of Flow
- Less flexibility for complex navigation scenarios
- Limited support for nested navigation and backstack management
- Fewer built-in features compared to Conductor
Code Comparison
Flow:
class MyScreen : Screen {
override fun createView(context: Context) =
TextView(context).apply { text = "Hello, Flow!" }
}
flow.set(MyScreen())
Conductor:
class MyController : Controller() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
return TextView(container.context).apply { text = "Hello, Conductor!" }
}
}
router.setRoot(RouterTransaction.with(MyController()))
Both Flow and Conductor aim to simplify Android navigation, but they take different approaches. Flow focuses on simplicity and single-Activity applications, while Conductor offers more flexibility and features for complex navigation scenarios. Flow's API is generally more concise, but Conductor provides more built-in functionality for managing the backstack and nested navigation. The choice between the two depends on the specific requirements of your project and personal preferences.
A Model-View-Presenter / Model-View-Intent library for modern Android apps
Pros of Mosby
- Implements the Model-View-Presenter (MVP) pattern, providing a clear separation of concerns
- Offers a robust solution for handling view state and preventing memory leaks
- Includes built-in support for RxJava, making it easier to work with reactive programming
Cons of Mosby
- Steeper learning curve due to its more complex architecture
- May introduce more boilerplate code compared to simpler solutions
- Less flexible for non-MVP architectures or custom implementations
Code Comparison
Mosby (MVP implementation):
public class MyPresenter extends MvpBasePresenter<MyView> {
public void loadData() {
// Perform data loading
getView().showData(data);
}
}
Conductor (Controller implementation):
public class MyController extends Controller {
@Override
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
View view = inflater.inflate(R.layout.my_layout, container, false);
// Set up view and handle logic
return view;
}
}
Mosby focuses on MVP architecture, providing a structured approach to separating concerns and managing view state. Conductor, on the other hand, offers a more flexible solution for building Android UIs with a focus on navigation and lifecycle management. The choice between the two depends on the specific needs of the project and the preferred architectural approach.
Mavericks: Android on Autopilot
Pros of Mavericks
- Built specifically for Kotlin, leveraging its features and syntax
- Integrates well with Jetpack Compose for modern UI development
- Provides a robust state management system with built-in error handling
Cons of Mavericks
- Steeper learning curve, especially for developers new to MVI architecture
- More opinionated, which may limit flexibility in some scenarios
- Requires more boilerplate code for simple use cases
Code Comparison
Conductor (Java):
public class MainController extends Controller {
@Override
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
View view = inflater.inflate(R.layout.controller_main, container, false);
// Initialize views and set up logic
return view;
}
}
Mavericks (Kotlin):
class MainViewModel(state: MyState) : MavericksViewModel<MyState>(state) {
fun updateData() = setState { copy(data = "New Data") }
}
@Composable
fun MainScreen() {
val viewModel: MainViewModel = mavericksViewModel()
// Use viewModel state and actions in Composables
}
Uber's cross-platform mobile architecture framework.
Pros of RIBs
- Provides a more comprehensive architecture for large-scale apps
- Offers better support for deep linking and state restoration
- Includes built-in dependency injection and scoping
Cons of RIBs
- Steeper learning curve and more complex implementation
- May be overkill for smaller projects
- Less flexibility in UI management compared to Conductor
Code Comparison
Conductor:
class MainController : Controller() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
return inflater.inflate(R.layout.controller_main, container, false)
}
}
RIBs:
class MainRouter(
interactor: MainInteractor,
component: MainBuilder.Component
) : ViewRouter<MainView, MainInteractor, MainBuilder.Component>(interactor, component)
Both Conductor and RIBs aim to improve Android app architecture, but they take different approaches. Conductor focuses on simplifying navigation and view management, while RIBs provides a more comprehensive architecture for large-scale applications. Conductor is generally easier to adopt and more flexible for UI management, making it suitable for a wider range of projects. RIBs, on the other hand, offers more robust features for complex apps, including built-in dependency injection and better support for deep linking. The choice between the two depends on the project's scale, complexity, and specific requirements.
A sample Android app which showcases advanced usage of Dagger among other open source libraries.
Pros of u2020
- Demonstrates advanced usage of Dagger for dependency injection
- Showcases integration with popular libraries like Retrofit and RxJava
- Provides a comprehensive example of app architecture and testing practices
Cons of u2020
- Less focused on navigation and view management compared to Conductor
- May be more complex for beginners due to its advanced architecture
- Not actively maintained (last update was several years ago)
Code Comparison
u2020 (Activity setup)
public final class U2020App extends Application {
@Override public void onCreate() {
super.onCreate();
Timber.plant(new Timber.DebugTree());
ObjectGraph objectGraph = ObjectGraph.create(Modules.list(this));
objectGraph.inject(this);
}
}
Conductor (Controller setup)
public class HomeController extends Controller {
@Override
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
View view = inflater.inflate(R.layout.controller_home, container, false);
return view;
}
}
The code snippets demonstrate the different focus areas of each library. u2020 emphasizes dependency injection and application-level setup, while Conductor focuses on view management and navigation through its Controller system.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Conductor
A small, yet full-featured framework that allows building View-based Android applications. Conductor provides a light-weight wrapper around standard Android Views that does just about everything you'd want:
Conductor | |
---|---|
:tada: | Easy integration |
:point_up: | Single Activity apps without using Fragments |
:recycle: | Simple but powerful lifecycle management |
:train: | Navigation and backstack handling |
:twisted_rightwards_arrows: | Beautiful transitions between views |
:floppy_disk: | State persistence |
:phone: | Callbacks for onActivityResult, onRequestPermissionsResult, etc |
:european_post_office: | MVP / MVVM / MVI / VIPER / MVC ready |
Conductor is architecture-agnostic and does not try to force any design decisions on the developer. We here at BlueLine Labs tend to use either MVP or MVVM, but it would work equally well with standard MVC or whatever else you want to throw at it.
Installation
Conductor 4.0 is coming soon. It is already being used in production with many, many millions of users. It is, however, not guaranteed to be API stable. As such, it is being released as a preview rather than a standard release. Preview in this context is not a commentary on stability. It is considered to be up to the same quality standards as the current 3.x stable release. Changes in Conductor 4 are available in the GitHub releases. In preparation for the release of the next version, there are currently 3 installation options:
Latest Stable 3.x
def conductorVersion = '3.2.0'
implementation "com.bluelinelabs:conductor:$conductorVersion"
// AndroidX Transition change handlers:
implementation "com.bluelinelabs:conductor-androidx-transition:$conductorVersion"
// ViewPager PagerAdapter:
implementation "com.bluelinelabs:conductor-viewpager:$conductorVersion"
// ViewPager2 Adapter:
implementation "com.bluelinelabs:conductor-viewpager2:$conductorVersion"
4.0 Preview
Use 4.0.0-preview-4
as your version number in any of the dependencies above.
SNAPSHOT
Use 4.0.0-SNAPSHOT
as your version number in any of the dependencies above and add the url to the snapshot repository:
allprojects {
repositories {
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
}
Components to Know
Conductor Components | |
---|---|
Controller | The Controller is the View wrapper that will give you all of your lifecycle management features. Think of it as a lighter-weight and more predictable Fragment alternative with an easier to manage lifecycle. |
Router | A Router implements navigation and backstack handling for Controllers. Router objects are attached to Activity/containing ViewGroup pairs. Routers do not directly render or push Views to the container ViewGroup, but instead defer this responsibility to the ControllerChangeHandler specified in a given transaction. |
ControllerChangeHandler | ControllerChangeHandlers are responsible for swapping the View for one Controller to the View of another. They can be useful for performing animations and transitions between Controllers. Several default ControllerChangeHandlers are included. |
RouterTransaction | Transactions are used to define data about adding Controllers. RouterTransactions are used to push a Controller to a Router with specified ControllerChangeHandlers, while ChildControllerTransactions are used to add child Controllers. |
Getting Started
Minimal Activity implementation
class MainActivity : AppCompatActivity() {
private lateinit var router: Router
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val container = findViewById<ViewGroup>(R.id.controller_container)
router = Conductor.attachRouter(this, binding.controllerContainer, savedInstanceState)
.setPopRootControllerMode(PopRootControllerMode.NEVER)
.setOnBackPressedDispatcherEnabled(true)
if (!router.hasRootController()) {
router.setRoot(RouterTransaction.with(HomeController()))
}
}
}
Minimal Controller implementation
class HomeController : Controller() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup,
savedViewState: Bundle?
): View {
val view = inflater.inflate(R.layout.controller_home, container, false)
view.findViewById<TextView>(R.id.tv_title).text = "Hello World"
return view
}
}
Sample Project
Demo app - Shows how to use all basic and most advanced functions of Conductor.
Controller Lifecycle
The lifecycle of a Controller is significantly simpler to understand than that of a Fragment. A lifecycle diagram is shown below:
Advanced Topics
Retain View Modes
setRetainViewMode
can be called on a Controller
with one of two values: RELEASE_DETACH
, which will release the Controller
's view as soon as it is detached from the screen (saves memory), or RETAIN_DETACH
, which will ensure that a Controller
holds on to its view, even if it's not currently shown on the screen (good for views that are expensive to re-create).
Custom Change Handlers
ControllerChangeHandler
can be subclassed in order to perform different functions when changing between two Controllers
. Two convenience ControllerChangeHandler
subclasses are included to cover most basic needs: AnimatorChangeHandler
, which will use an Animator
object to transition between two views, and TransitionChangeHandler
, which will use Lollipop's Transition
framework for transitioning between views.
Child Routers & Controllers
getChildRouter
can be called on a Controller
in order to get a nested Router
into which child Controller
s can be pushed. This enables creating advanced layouts, such as Master/Detail.
RxJava Lifecycle
If the AutoDispose dependency has been added, there is a ControllerScopeProvider
available that can be used along with the standard AutoDispose library.
Community Projects
The community has provided several helpful modules to make developing apps with Conductor even easier. Here's a collection of helpful libraries:
- ConductorGlide - Adds Glide lifecycle support to Controllers
- ConductorDialog - Adds a helpful DialogController (a Conductor version of DialogFragment)
- Mosby-Conductor - A plugin to integrate Mosby, an MVP/MVI library
License
Copyright 2020 BlueLine Labs, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Top Related Projects
Name UI states, navigate between them, remember where you've been.
A Model-View-Presenter / Model-View-Intent library for modern Android apps
Mavericks: Android on Autopilot
Uber's cross-platform mobile architecture framework.
A sample Android app which showcases advanced usage of Dagger among other open source libraries.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot