kotlin里可以在整个项目中共享数据和方法,其状态自动更新且可被任何页面调用和继承, 通常可以使用以下模式之一:
1, 首选:object
单例(Kotlin 特性)
object
是 Kotlin 提供的单例模式实现,天然支持全局访问和状态更新。
object AppState {
var currentUser: String = ""
var isLoggedIn: Boolean = false
fun logout() {
currentUser = ""
isLoggedIn = false
}
}
object BluetoothManager {
var connectedDeviceName by mutableStateOf<String?>(null)
fun connectTo(deviceName: String) {
connectedDeviceName = deviceName
}
fun disconnect() {
connectedDeviceName = null
}
}
2. 抽象类 + ViewModel(可继承 + 自动状态更新)
abstract class BaseUserViewModel : ViewModel() {
val userName = MutableLiveData<String>()
val isLoggedIn = MutableLiveData<Boolean>()
fun login(name: String) {
userName.value = name
isLoggedIn.value = true
}
}
@HiltViewModel
class MyAppViewModel @Inject constructor() : BaseUserViewModel()
调用页面(Jetpack Compose 或 Fragment)
@Composable
fun HomeScreen(viewModel: MyAppViewModel = hiltViewModel()) {
val userName by viewModel.userName.observeAsState("")
Text(text = "Welcome $userName")
}
或在 Fragment 中:
val viewModel: MyAppViewModel by viewModels()
viewModel.userName.observe(viewLifecycleOwner) {
name -> textView.text = "Welcome $name"
}
3. 接口 + 实现类 + Hilt 注入
🔸接口定义
interface SettingsManager {
var isDarkMode: Boolean fun toggleTheme()
}
class SettingsManagerImpl @Inject constructor() : SettingsManager {
override var isDarkMode = false
override fun toggleTheme() {
isDarkMode = !isDarkMode
}
}
🔸Hilt 模块绑定
@Module
@InstallIn(SingletonComponent::class) interface SettingsModule {
@Binds
fun bindSettingsManager(impl: SettingsManagerImpl): SettingsManager
}
🔸注入使用(ViewModel 或 Composable)
@HiltViewModel
class SettingsViewModel @Inject constructor(
private val settingsManager: SettingsManager ) :
ViewModel() {
fun toggle() = settingsManager.toggleTheme()
fun isDark() = settingsManager.isDarkMode
}
在页面中使用:
@Composable
fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
val isDark = viewModel.isDark()
Button(onClick = {
viewModel.toggle()
}) {
Text(if (isDark) "Switch to Light" else "Switch to Dark")
}
}
4. sealed class
状态机 + StateFlow
+ collect
🔸状态类 + 管理器
sealed class AuthState {
object LoggedOut : AuthState()
data class LoggedIn(val user: String) : AuthState()
object Loading : AuthState()
}
class AuthManager {
private val _authState = MutableStateFlow<AuthState>(AuthState.LoggedOut)
val authState: StateFlow<AuthState> = _authState
fun login(user: String) {
_authState.value = AuthState.Loading
_authState.value = AuthState.LoggedIn(user)
}
}
🔸注入并 collect 状态
@HiltViewModel
class AuthViewModel @Inject constructor(
val authManager: AuthManager
) : ViewModel()
在 Compose 页面中:
@Composable
fun LoginScreen(viewModel: AuthViewModel = hiltViewModel()) {
val state by viewModel.authManager.authState.collectAsState()
when (state) {
is AuthState.Loading -> CircularProgressIndicator()
is AuthState.LoggedIn -> Text("Welcome ${(state as AuthState.LoggedIn).user}")
AuthState.LoggedOut -> Text("Please log in")
}
}
5. CompositionLocal
注入共享对象
🔸Cart 管理器
class CartManager {
var items = mutableStateListOf<String>()
fun addItem(item: String) = items.add(item)
}
🔸声明并提供 CompositionLocal
val LocalCartManager = compositionLocalOf<CartManager> {
error("CartManager not provided")
}
在根组件中注入:
@Composable
fun App() {
val cartManager = remember { CartManager() }
CompositionLocalProvider(LocalCartManager provides cartManager) {
NavHost(...) // etc.
}
}
🔸在页面中使用
@Composable
fun CartScreen() {
val cart = LocalCartManager.current Column {
cart.items.forEach {
Text(it)
}
Button(onClick = {
cart.addItem("Apple")
}) {
Text("Add Apple")
}
}
}
6. Repository + Flow + 依赖注入
🔸Repository 类
class ThemeRepository
@Inject constructor() {
private val _isDark = MutableStateFlow(false)
val isDark: StateFlow<Boolean> = _isDark
fun toggleTheme() {
_isDark.value = !_isDark.value
}
}
🔸注入并观察状态
@HiltViewModel
class ThemeViewModel @Inject constructor(
private val themeRepository: ThemeRepository ) : ViewModel() {
val isDark = themeRepository.isDark
fun toggleTheme() = themeRepository.toggleTheme()
}
页面中使用:
@Composable
fun ThemeScreen(viewModel: ThemeViewModel = hiltViewModel()) {
val isDark by viewModel.isDark.collectAsState()
Text("Theme: ${if (isDark) "Dark" else "Light"}")
Button(onClick = {
viewModel.toggleTheme()
}) {
Text("Toggle Theme")
}
}
/// PROJECT STRUCTURE: GlobalStateDemo
// ========== build.gradle (Module) ==========
// Add required dependencies
// implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
// implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
// implementation "androidx.compose.runtime:runtime-livedata:1.6.0"
// kapt "com.google.dagger:hilt-compiler:2.44"
// ========== Application.kt ==========
@HiltAndroidApp
class GlobalStateDemoApp : Application()
// ========== MainActivity.kt ==========
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val cartManager = remember { CartManager() }
CompositionLocalProvider(LocalCartManager provides cartManager) {
NavHostControllerExample()
}
}
}
}
// ========== 1. ViewModel (Base + Inheritance) ==========
abstract class BaseUserViewModel : ViewModel() {
val userName = MutableLiveData<String>()
fun login(name: String) { userName.value = name }
}
@HiltViewModel
class MyAppViewModel @Inject constructor() : BaseUserViewModel()
@Composable
fun UserInfoScreen(viewModel: MyAppViewModel = hiltViewModel()) {
val name by viewModel.userName.observeAsState("")
Text("Hello, $name")
}
// ========== 2. Interface + Hilt ==========
interface SettingsManager {
var isDarkMode: Boolean
fun toggleTheme()
}
class SettingsManagerImpl @Inject constructor() : SettingsManager {
override var isDarkMode = false
override fun toggleTheme() { isDarkMode = !isDarkMode }
}
@Module
@InstallIn(SingletonComponent::class)
interface SettingsModule {
@Binds
fun bindSettingsManager(impl: SettingsManagerImpl): SettingsManager
}
@HiltViewModel
class SettingsViewModel @Inject constructor(private val settings: SettingsManager) : ViewModel() {
fun toggle() = settings.toggleTheme()
fun isDark() = settings.isDarkMode
}
@Composable
fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
val dark = remember { viewModel.isDark() }
Button(onClick = { viewModel.toggle() }) { Text(if (dark) "Light" else "Dark") }
}
// ========== 3. Sealed Class State + Flow ==========
sealed class AuthState {
object LoggedOut : AuthState()
data class LoggedIn(val user: String) : AuthState()
object Loading : AuthState()
}
class AuthManager @Inject constructor() {
private val _authState = MutableStateFlow<AuthState>(AuthState.LoggedOut)
val authState: StateFlow<AuthState> = _authState
fun login(user: String) {
_authState.value = AuthState.Loading
_authState.value = AuthState.LoggedIn(user)
}
}
@HiltViewModel
class AuthViewModel @Inject constructor(val authManager: AuthManager) : ViewModel()
@Composable
fun AuthScreen(viewModel: AuthViewModel = hiltViewModel()) {
val state by viewModel.authManager.authState.collectAsState()
when (state) {
is AuthState.Loading -> CircularProgressIndicator()
is AuthState.LoggedIn -> Text("Welcome ${(state as AuthState.LoggedIn).user}")
AuthState.LoggedOut -> Button(onClick = { viewModel.authManager.login("User") }) {
Text("Login")
}
}
}
// ========== 4. CompositionLocal Example ==========
class CartManager {
val items = mutableStateListOf<String>()
fun addItem(item: String) = items.add(item)
}
val LocalCartManager = compositionLocalOf<CartManager> { error("No cart manager") }
@Composable
fun CartScreen() {
val cart = LocalCartManager.current
Column {
Button(onClick = { cart.addItem("Banana") }) { Text("Add") }
cart.items.forEach { Text(it) }
}
}
// ========== 5. Repository + Flow ==========
class ThemeRepository @Inject constructor() {
private val _dark = MutableStateFlow(false)
val dark: StateFlow<Boolean> = _dark
fun toggle() { _dark.value = !_dark.value }
}
@HiltViewModel
class ThemeViewModel @Inject constructor(private val repo: ThemeRepository) : ViewModel() {
val dark = repo.dark
fun toggle() = repo.toggle()
}
@Composable
fun ThemeScreen(viewModel: ThemeViewModel = hiltViewModel()) {
val dark by viewModel.dark.collectAsState()
Column {
Text("Dark Mode: $dark")
Button(onClick = { viewModel.toggle() }) { Text("Toggle") }
}
}