Multi Module Jetpack Compose Recomposition Skippable Issue
21963 ワード
こんにちは!このプレゼンテーションでは、マルチモジュールでコンポーネントを使用する際の注意点について説明します.
https://qiita.com/takahirom/items/6907e810d3661e19cfcf
このメッセージングが表示され、作成されます.
最近、ますます多くのマルチモジュールプロジェクトがクライアントアーキテクチャを構成しています.また、プロポーズを導入する項目も多く増えています.
クライアント・アーキテクチャを構成する場合、通常はドメイン・レイヤでモデルを構成し、Presenterレイヤでこれらのモデルを使用します.
ただし、プレゼンテーションレイヤでリクエストを使用する場合は、注意すべき事項が分からない場合があります.
簡単に言えば、複合で「再結合」が発生した場合、ドメインレイヤで使用されるモデルがパラメータとして使用される場合、その組合せのスキップは発生しません.
このような状況を再現するために、以下のようにプロジェクトを構成しました.
Domain Layerで構成されたモデルに直接書き込むユーザーと@Stable Annotationを適用するwrappedUser、およびPresenterレイヤで構成されたotherStateの3つの変数を宣言して使用します.
2つのボタンを作成します.1つはuser値を変更し、user値とwrappedUser値を変更し、もう1つはotherState値を変更します.
通常、ユーザー値が変更された場合、「コンテンツ」のみが予想通りに合成され、「プロファイル」、「WrappedUserProfile」および「その他のステータス」の値が変更された場合、「コンテンツ」は合成されます.
結果を見てみましょう.
2022-04-07 16:04:18.866 8922-8922/com.example.multimodulecomposemodeltest I/System.out: User Profile Composable
2022-04-07 16:04:18.873 8922-8922/com.example.multimodulecomposemodeltest I/System.out: Wrapped User Profile Composable
2022-04-07 16:04:18.893 8922-8922/com.example.multimodulecomposemodeltest I/System.out: Other Content Composable
2022-04-07 16:04:28.696 8922-8922/com.example.multimodulecomposemodeltest I/System.out: User Profile Composable
2022-04-07 16:04:28.698 8922-8922/com.example.multimodulecomposemodeltest I/System.out: Wrapped User Profile Composable
2022-04-07 16:06:15.791 9474-9474/com.example.multimodulecomposemodeltest I/System.out: User Profile Composable
2022-04-07 16:06:15.793 9474-9474/com.example.multimodulecomposemodeltest I/System.out: Other Content Composable
結果として,シーンでは予想通りに合成されず,ドメイン層モデルであるユーザはSkipにならずに合成を実行し続けることが分かる.
トランスファにも良い解決策があります.
@Stable Annotationを使用してこの問題を解決する方法と、Compose Runtime Denependencyをモデルを使用するモジュールに適用します.
ただし、Pure JavaおよびKotlinライブラリとして通常ドメイン層が使用されるため、Android Frameworkから返されるComponent Runtime Dependencyは適用できません.
したがって,@Stable Annotationで再結合する場合にはSkipの期待値が適切である.
https://qiita.com/takahirom/items/6907e810d3661e19cfcf
https://qiita.com/takahirom/items/6907e810d3661e19cfcf
このメッセージングが表示され、作成されます.
最近、ますます多くのマルチモジュールプロジェクトがクライアントアーキテクチャを構成しています.また、プロポーズを導入する項目も多く増えています.
クライアント・アーキテクチャを構成する場合、通常はドメイン・レイヤでモデルを構成し、Presenterレイヤでこれらのモデルを使用します.
ただし、プレゼンテーションレイヤでリクエストを使用する場合は、注意すべき事項が分からない場合があります.
簡単に言えば、複合で「再結合」が発生した場合、ドメインレイヤで使用されるモデルがパラメータとして使用される場合、その組合せのスキップは発生しません.
このような状況を再現するために、以下のようにプロジェクトを構成しました.
Domain Layer
data class User(
val name: String
)
interface UserRepository {
fun observeUser(): Flow<User>
suspend fun setUser()
}
class SetUserUseCase @Inject constructor(
private val userRepository: UserRepository
) {
suspend operator fun invoke() = userRepository.setUser()
}
class ObserveUserUseCase @Inject constructor(
private val userRepository: UserRepository
) {
operator fun invoke() = userRepository.observeUser()
}
Data Layer
@Module
@InstallIn(SingletonComponent::class)
interface RepositoryModule {
@Binds
fun bindUserRepository(userRepository: UserRepositoryImpl): UserRepository
}
@Singleton
class UserApiExecutor @Inject constructor() {
private val _user: MutableSharedFlow<User> = MutableSharedFlow()
val user: SharedFlow<User> get() = _user
var cnt = 2
suspend fun setUser() {
_user.emit(User(cnt++.toString()))
}
}
class UserRepositoryImpl @Inject constructor(
private val userApiExecutor: UserApiExecutor
) : UserRepository {
override fun observeUser(): Flow<User> = userApiExecutor.user
override suspend fun setUser() = userApiExecutor.setUser()
}
Presentation Layer
@HiltAndroidApp
class ModelTestApplication : Application()
@Stable
data class UserUiState(
val user: User = User("1")
)
@HiltViewModel
class MainViewModel @Inject constructor(
observeUserUseCase: ObserveUserUseCase,
private val setUserUseCase: SetUserUseCase
) : ViewModel() {
val user = observeUserUseCase()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), User("1"))
val wrappedUser = observeUserUseCase()
.map(::UserUiState)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), UserUiState())
private val _otherState: MutableStateFlow<Int> = MutableStateFlow(0)
val otherState: StateFlow<Int> = _otherState
fun onButtonClick() {
viewModelScope.launch {
setUserUseCase()
}
}
fun onOtherAction() {
_otherState.update {
it + 1
}
}
}
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val viewModel: MainViewModel = viewModel()
val otherState by viewModel.otherState.collectAsState()
val userState by viewModel.wrappedUser.collectAsState()
val user by viewModel.user.collectAsState()
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween
) {
UserProfile(user)
WrappedUserProfile(userUiState = userState)
Button(viewModel::onButtonClick) {
Text(text = "Set User")
}
OtherContent(otherState = otherState)
Button(viewModel::onOtherAction) {
Text(text = "Other Action")
}
}
}
}
}
@Composable
fun UserProfile(user: User) {
println("User Profile Composable")
Text(text = user.name)
}
@Composable
fun WrappedUserProfile(userUiState: UserUiState) {
println("Wrapped User Profile Composable")
Text(text = userUiState.user.name)
}
@Composable
fun OtherContent(otherState: Int) {
println("Other Content Composable")
Text(text = otherState.toString())
}
イニシアチブ
Domain Layerで構成されたモデルに直接書き込むユーザーと@Stable Annotationを適用するwrappedUser、およびPresenterレイヤで構成されたotherStateの3つの変数を宣言して使用します.
2つのボタンを作成します.1つはuser値を変更し、user値とwrappedUser値を変更し、もう1つはotherState値を変更します.
通常、ユーザー値が変更された場合、「コンテンツ」のみが予想通りに合成され、「プロファイル」、「WrappedUserProfile」および「その他のステータス」の値が変更された場合、「コンテンツ」は合成されます.
結果を見てみましょう.
結果ログ
最初のコンポーネント
2022-04-07 16:04:18.866 8922-8922/com.example.multimodulecomposemodeltest I/System.out: User Profile Composable
2022-04-07 16:04:18.873 8922-8922/com.example.multimodulecomposemodeltest I/System.out: Wrapped User Profile Composable
2022-04-07 16:04:18.893 8922-8922/com.example.multimodulecomposemodeltest I/System.out: Other Content Composable
ユーザー値の変更
2022-04-07 16:04:28.696 8922-8922/com.example.multimodulecomposemodeltest I/System.out: User Profile Composable
2022-04-07 16:04:28.698 8922-8922/com.example.multimodulecomposemodeltest I/System.out: Wrapped User Profile Composable
その他のステータス値の変更
2022-04-07 16:06:15.791 9474-9474/com.example.multimodulecomposemodeltest I/System.out: User Profile Composable
2022-04-07 16:06:15.793 9474-9474/com.example.multimodulecomposemodeltest I/System.out: Other Content Composable
結果として,シーンでは予想通りに合成されず,ドメイン層モデルであるユーザはSkipにならずに合成を実行し続けることが分かる.
トランスファにも良い解決策があります.
@Stable Annotationを使用してこの問題を解決する方法と、Compose Runtime Denependencyをモデルを使用するモジュールに適用します.
ただし、Pure JavaおよびKotlinライブラリとして通常ドメイン層が使用されるため、Android Frameworkから返されるComponent Runtime Dependencyは適用できません.
したがって,@Stable Annotationで再結合する場合にはSkipの期待値が適切である.
References
https://qiita.com/takahirom/items/6907e810d3661e19cfcf
Reference
この問題について(Multi Module Jetpack Compose Recomposition Skippable Issue), 我々は、より多くの情報をここで見つけました https://velog.io/@ams770/Multi-Module에서의-Jetpack-Compose-Recomposition-Skippable-Issue-1テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol