登录时后台并发复制GC

Pol*_*ion 0 android kotlin retrofit2 android-jetpack-compose

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(

                )
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

屏幕.kt

sealed class Screen (
    val route: String,
){
    object LoginScreen: Screen("loginScreen")
    object HomeScreen: Screen("homeScreen")
}
Run Code Online (Sandbox Code Playgroud)

登录屏幕.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))
                        }
                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

主屏幕.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
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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


编辑。我做了一些实验。当我将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 {
...
Run Code Online (Sandbox Code Playgroud)

Main Activity 的代码似乎有问题,但我猜不出问题所在。如果您知道,请告诉我。

Gab*_*han 6

该日志相对正常,可能与闪存无关。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
Run Code Online (Sandbox Code Playgroud)

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

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

除了大量的物体之外,这一切都是相当标准的。对象的数量可能也不重要,现代编程技术使用很多小对象。