A lightweight, lifecycle-aware component framework for Android. One line of DSL sets up modular UI components with automatic lifecycle management, service communication, and dependency ordering.
- One-Line Setup --
componentScope { }DSL handles lifecycle binding, service registration, and dependency ordering automatically - Component Lifecycle -- Components mirror the host lifecycle with state guards to prevent invalid transitions
- Service Communication -- Loosely-coupled inter-component messaging via type-safe
getService<T>(), scoped per host - Dependency Ordering -- Declare dependencies; the framework topologically sorts components with circular dependency detection
- Activity & Fragment -- Same component model works seamlessly in both Activity and Fragment contexts
- Error Isolation -- Lifecycle propagation is wrapped in try-catch; one component failure does not crash others
class TitleBar(
host: ComponentHost,
private val binding: LayoutTitleBarBinding,
) : Component(host), TitleBarService {
override fun onCreate() {
binding.textTitle.text = "Hello"
}
override fun sayHello(content: String) {
Log.d("TitleBar", "Received: $content")
}
}
interface TitleBarService : Service {
fun sayHello(content: String)
}class BottomBar(
host: ComponentHost,
private val binding: LayoutBottomBarBinding,
) : Component(host), BottomBarService {
// Framework ensures TitleBar is created before BottomBar
override fun dependencies() = listOf(TitleBarService::class)
override fun onCreate() {
val titleBar = getService<TitleBarService>()
titleBar?.sayHello("Hello from BottomBar")
}
}class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
componentScope {
+TitleBar(host, binding.titleBarContent)
+ContentLayout(host, binding.layoutContent)
+BottomBar(host, binding.bottomBarContent)
}
}
}class DemoFragment : Fragment(R.layout.fragment_demo) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val binding = FragmentDemoBinding.bind(view)
componentScope {
+HeaderComponent(host, binding.header)
+ContentComponent(host, binding.content)
}
}
}That's it. Lifecycle binding, service registration, dependency sorting, and cleanup are all automatic.
graph TB
subgraph Host["Activity / Fragment"]
DSL["componentScope { }"]
end
DSL --> Scope["ComponentScope<br/><i>DSL receiver + service registry</i>"]
DSL --> Container["ComponentContainer<br/><i>lifecycle propagation + topo sort</i>"]
DSL --> HostAdapter["ComponentHost<br/><i>Activity/Fragment adapter</i>"]
Scope --> |registers| Services["Service Registry<br/><i>per-scope, no global state</i>"]
Container --> |manages| Components
subgraph Components["Components"]
C1["TitleBar<br/>implements TitleBarService"]
C2["ContentLayout"]
C3["BottomBar<br/>implements BottomBarService"]
C4["NoneUiComponent"]
end
C3 -.->|"getService<TitleBarService>()"| Services
C2 -.->|"getService<BottomBarService>()"| Services
HostAdapter --> |lifecycle events| Container
sequenceDiagram
participant A as Activity/Fragment
participant S as ComponentScope
participant C as ComponentContainer
participant Comp as Components
A->>S: componentScope { }
S->>S: +Component(host, binding)
S->>S: register Service interfaces
S->>C: pass components list
C->>C: topological sort by dependencies()
A->>C: ON_CREATE
C->>Comp: performCreate() (in dependency order)
A->>C: ON_START / ON_RESUME
C->>Comp: performStart() / performResume()
A->>C: ON_PAUSE / ON_STOP
C->>Comp: performPause() / performStop()
A->>C: ON_DESTROY
C->>Comp: performDestroy()
C->>S: release services
stateDiagram-v2
[*] --> INITIALIZED
INITIALIZED --> CREATED: performCreate()
CREATED --> STARTED: performStart()
STARTED --> RESUMED: performResume()
RESUMED --> PAUSED: performPause()
PAUSED --> RESUMED: performResume()
PAUSED --> STOPPED: performStop()
STOPPED --> STARTED: performStart()
STOPPED --> DESTROYED: performDestroy()
CREATED --> DESTROYED: performDestroy()
DESTROYED --> [*]
| Class | Role |
|---|---|
Component |
Base class with lifecycle callbacks, state machine, and dependency declaration |
Service |
Marker interface for inter-component communication |
ComponentHost |
Host environment abstraction (lifecycle, context, ViewModel store) |
componentScope() |
DSL entry point -- creates host, scope, container, binds lifecycle |
getService<T>() |
Type-safe service lookup extension on Component |
| Class | Role |
|---|---|
ComponentContainer |
Manages components, propagates lifecycle, topological sort |
ComponentScope |
DSL receiver, scope-local service registry |
ActivityComponentHost |
ComponentHost adapter for Activity (Kotlin by delegation) |
FragmentComponentHost |
ComponentHost adapter for Fragment (view lifecycle) |
component-framework/
├── src/main/java/com/andy/modularization/ # Core framework library
│ ├── Component.kt # Base component + state machine
│ ├── ComponentHost.kt # Host abstraction interface
│ ├── ComponentContainer.kt # Lifecycle container + dependency sort
│ ├── ComponentScope.kt # DSL receiver + scope-local service registry
│ ├── ComponentKt.kt # componentScope() DSL + extensions
│ ├── Service.kt # Service marker interface
│ ├── ActivityComponentHost.kt # Activity adapter (by-delegation)
│ └── FragmentComponentHost.kt # Fragment adapter (view lifecycle)
│
├── app/ # Demo application
│ └── src/main/java/com/andy/modules/
│ ├── ui/ # Activity-scoped components
│ └── fragment/ # Fragment demo
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}dependencies {
implementation("com.github.andyhaha:component-framework:v2.0.0")
}- Android SDK 23+
- Kotlin 2.0+
- AndroidX AppCompat
Copyright 2024 Andy
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.
