登录时后台并发复制GC

Background concurrent copying GC when Login

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        val navController = rememberNavController()
        NavHost(
            navController = navController,
            startDestination = Screen.LoginScreen.route
        ) {
            composable(route = Screen.LoginScreen.route) {
                navBackStackEntry ->
                val factory = HiltViewModelFactory(LocalContext.current, navBackStackEntry)
                val viewModel: LoginViewModel = viewModel(key = "LoginViewModel", factory = factory)
                LoginScreen(
                    viewModel = viewModel,
                    onNavigateToNextScreen = navController::navigate,
                    navController = navController
                )
            }
            composable(route = Screen.HomeScreen.route) {
                navBackStackEntry ->
                val factory = HiltViewModelFactory(LocalContext.current, navBackStackEntry)
                val viewModel: HomeViewModel = viewModel(key = "HomeViewModel", factory = factory)
                HomeScreen(

                )
            }
        }
    }
}

Screen.kt

sealed class Screen (
    val route: String,
){
    object LoginScreen: Screen("loginScreen")
    object HomeScreen: Screen("homeScreen")
}

登录Screen.kt

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun LoginScreen(
    viewModel: LoginViewModel,
    onNavigateToNextScreen: (String) -> Unit,
    navController: NavController
) {
    val focusManager = LocalFocusManager.current
    val keyboardController = LocalSoftwareKeyboardController.current

    val empno = viewModel.empno.value
    val password = viewModel.password.value
    val loading = viewModel.loading.value
    val user = viewModel.user.value
    val dialogQueue = viewModel.dialogQueue

    var isPasswordVisible by remember {
        mutableStateOf(false)
    }
    val isFormValid by derivedStateOf {
        empno.isNotBlank() && password.isNotBlank()
    }
    
    Scaffold(backgroundColor = Color.White) {
        Column(
            modifier = Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Top
        ) {
            if (loading && user == null) {
                LoadingRecipeShimmer(imageHeight = IMAGE_HEIGHT.dp)
            }
            else if (!loading && user != null) {
                //Something to do.
                val route = Screen.HomeScreen.route
                onNavigateToNextScreen(route)
            }else {
                Spacer(modifier = Modifier.height(16.dp))
                Image(
                    painter = painterResource(id = R.drawable.image_login),
                    contentDescription = "loginImage",
                    modifier = Modifier
                        .weight(1f)
                        .size(300.dp)
                )
                Card(
                    modifier = Modifier
                        .weight(2f)
                        .padding(8.dp),
                    shape = RoundedCornerShape(32.dp)
                ) {
                    Column(
                        modifier = Modifier
                            .fillMaxSize()
                            .padding(32.dp),
                        horizontalAlignment = Alignment.CenterHorizontally,
                        verticalArrangement = Arrangement.Center
                    ) {
                        Text(
                            text = "CompanyApp",
                            fontWeight = FontWeight.Bold,
                            fontSize = 40.sp
                        )
                        Column(
                            Modifier.fillMaxSize(),
                            horizontalAlignment = Alignment.CenterHorizontally,
                            verticalArrangement = Arrangement.Center
                        ) {
                            Spacer(modifier = Modifier.weight(1f))
                            OutlinedTextField(
                                value = empno,
                                onValueChange = {
                                    viewModel.setEmpno(it)
                                },
                                modifier = Modifier
                                    .fillMaxWidth(),
                                label = { Text(text = "EmployeeNumber") },
                                singleLine = true,
                                keyboardOptions = KeyboardOptions(
                                    keyboardType = KeyboardType.Number,
                                    imeAction = ImeAction.Next
                                ),
                                keyboardActions = KeyboardActions(
                                    onNext = {
                                        focusManager.moveFocus(FocusDirection.Down)
                                    }
                                ),
                                trailingIcon = {
                                    if (empno.isNotBlank()) {
                                        IconButton(
                                            onClick = {
                                                viewModel.setEmpno("")
                                            }) {
                                            Icon(
                                                imageVector = Icons.Filled.Clear,
                                                contentDescription = "")
                                        }
                                    }
                                }
                            )
                            Spacer(modifier = Modifier.height(16.dp))
                            OutlinedTextField(
                                value = password,
                                onValueChange = {
                                    viewModel.setPassword(it)
                                },
                                modifier = Modifier
                                    .fillMaxWidth(),
                                label = { Text(text = "Password") },
                                singleLine = true,
                                keyboardOptions = KeyboardOptions(
                                    keyboardType = KeyboardType.NumberPassword,
                                    imeAction = ImeAction.Done
                                ),
                                keyboardActions = KeyboardActions(
                                    onDone = {
                                        keyboardController?.hide()
                                        viewModel.login()
                                    }
                                ),
                                visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(),
                                trailingIcon = {
                                    IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) {
                                        Icon(
                                            imageVector = if (isPasswordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff,
                                            contentDescription = "Password Toggle"
                                        )
                                    }
                                }
                            )
                            Spacer(modifier = Modifier.height(16.dp))
                            Button(
                                onClick = {
                                    keyboardController?.hide()
                                    viewModel.login()
                                },
                                enabled = isFormValid,
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .height(60.dp),
                                shape = RoundedCornerShape(16.dp)
                            ) {
                                Text(text = "Login")
                            }
                            Spacer(modifier = Modifier.weight(1f))
                        }
                    }
                }
            }
        }
    }
}

LoginViewModel.kt

@HiltViewModel
class LoginViewModel @Inject constructor(
    private val login: Login,
    private val connectivityManager: ConnectivityManager,
    private val state: SavedStateHandle
) : ViewModel() {

    val empno = mutableStateOf("")
    val password = mutableStateOf("")
    val user: MutableState<User?> = mutableStateOf(null)

    val loading = mutableStateOf(false)
    val onLoad: MutableState<Boolean> = mutableStateOf(false)
    val dialogQueue = DialogQueue()

    fun setEmpno(empno: String) {
        this.empno.value = empno
    }
    fun setPassword(password: String) {
        this.password.value = password
    }

    init {
    }


    fun login() {
        val auth = Auth(store_code = "2001", empno = empno.value, password = password.value)
        login.execute(auth).onEach { dataState ->
            loading.value = dataState.loading

            dataState.data?.let { data ->
                user.value = data
            }

            dataState.error?.let { error ->
                Log.e(TAG, "getRecipe: ${error}")
                dialogQueue.appendErrorMessage("An Error Occurred", error)
            }
        }.launchIn(viewModelScope)
    }
}

首页Screen.kt

@Composable
fun HomeScreen(

) {
    Card(
        modifier = Modifier.fillMaxSize()
    ) {
        Column(
            modifier = Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = "HomeScreen",
                fontWeight = FontWeight.Bold,
                fontSize = 40.sp
            )
        }
    }
}

按登录按钮尝试登录并接收用户信息,然后转到下一个屏幕。(HomeScreen) 但是,当登录并转到下一个主屏幕时,屏幕闪烁并输出以下消息。

Background concurrent copying GC freed 821063(20MB) AllocSpace objects, 4(1176KB) LOS objects, 49% free, 19MB/38MB, paused 77us total 100.203ms
2021-11-01 11:51:04.493 32596-4299/com.woorimart.projectapp I/rt.woorimartap: Background concurrent copying GC freed 783275(19MB) AllocSpace objects, 3(960KB) LOS objects, 49% free, 21MB/43MB, paused 42us total 107.347ms
2021-11-01 11:51:12.030 32596-4299/com.woorimart.projectapp I/rt.woorimartap: Background concurrent copying GC freed 905429(22MB) AllocSpace objects, 3(768KB) LOS objects, 49% free, 23MB/46MB, paused 43us total 112.243ms
2021-11-01 11:51:15.313 32596-4299/com.woorimart.projectapp I/rt.woorimartap: Background concurrent copying GC freed 951843(23MB) AllocSpace objects, 3(1184KB) LOS objects, 48% free, 25MB/49MB, paused 38us total 114.812ms
2021-11-01 11:51:22.273 32596-4299/com.woorimart.projectapp I/rt.woorimartap: Background concurrent copying GC freed 1030020(25MB) AllocSpace objects, 2(872KB) LOS objects, 47% free, 26MB/50MB, paused 45us total 101.114ms
2021-11-01 11:51:36.202 32596-4299/com.woorimart.projectapp I/rt.woorimartap: Background concurrent copying GC freed 1035054(25MB) AllocSpace objects, 2(1008KB) LOS objects, 44% free, 30MB/54MB, paused 42us total 126.748ms
2021-11-01 11:51:38.349 32596-4299/com.woorimart.projectapp I/rt.woorimartap: Background concurrent copying GC freed 903031(22MB) AllocSpace objects, 3(3596KB) LOS objects, 41% free, 33MB/57MB, paused 40us total 127.925ms
2021-11-01 11:51:41.070 32596-4299/com.woorimart.projectapp I/rt.woorimartap: Background concurrent copying GC freed 975005(24MB) AllocSpace objects, 3(1584KB) LOS objects, 40% free, 35MB/59MB, paused 46us total 106.787ms

在网上查了下,发现是if gson不匹配,但是打印[=43=中的值时,打印值正常。 如果在进入下一个屏幕之前我需要添加任何内容,请告诉我。


编辑。 我做了一些实验。 当我把HomeScreen的代码放到LoginScreen里面换屏的时候,没有卡顿。

像这样,

...
            else if (!loading && user != null) {
                //Something to do.
                //navController.navigate(Screen.HomeScreen.route)
                Card(
                    modifier = Modifier.fillMaxSize()
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        horizontalAlignment = Alignment.CenterHorizontally,
                        verticalArrangement = Arrangement.Center
                    ) {
                        Text(
                            text = "HomeScreen",
                            fontWeight = FontWeight.Bold,
                            fontSize = 40.sp
                        )
                    }
                }
            }else {
...

Main Activity 的代码似乎有问题,但我猜不出哪里出了问题。知道的请告诉我

该日志比较正常,应该与闪退无关。 Java 和 Kotlin 都是垃圾回收语言。每隔一段时间,系统将决定释放不再使用的所有内存。在 Activity 之间切换是一个常见的时间,因为你突然需要新对象的内存(尤其是新的视图层次结构),如果之前的 Activity 正在完成,它可能会释放大部分.这些消息只是告诉您 GC 运行 及其状态。

因此,如果您发现问题,可能与这些日志无关。

如果你只是出于好奇想要进一步细分,

Background concurrent copying GC freed 783275(19MB) AllocSpace objects, 3(960KB) LOS objects, 49% free, 21MB/43MB, paused 42us total 107.347ms

并发复制 GC 是 Android 从 8 开始使用的一种 GC 形式。在此之前,它使用较旧的标记和清除算法。

它释放了 783275 个对象,占 space 的 19 MB。这实际上是一个巨大的数字,让我很好奇为什么会有这么多。它释放了 Large Object Space(用于大型对象的内存池)中的 3 个对象。完成后,您的堆有 49% 空闲(43 MB 中的 21 个)。该操作耗时 107 毫秒,您的应用需要暂停 42 微秒,同时移动部分数据。

除了物件数量多以外,其他都很一般。对象的数量可能也不是问题,现代编程技术使用很多小对象。