美文网首页Android Weekly Notes
Android Weekly Notes #482

Android Weekly Notes #482

作者: 圣骑士wind | 来源:发表于2021-10-18 00:02 被阅读0次

    Android Weekly Issue #482

    Kotlin’s Flow in ViewModels: it’s complicated

    我们的目标

    UI数据加载要考虑的问题:

    • 1.缓存: 已经加载的数据应该可以直接显示, 而不是需要二次加载.
    • 2.避免后台工作: 当UI不可见时, 所有后台工作都应该被取消.
    • 3.在configuration change的时候工作不会被中断.

    ViewModel用来实现1和3, LiveData用来实现2和3.

    LiveData以及改进

    LiveData的局限性:

    • 只有主线程操作.
    • 只有3种转换操作符. map(), switchMap() and distinctUntilChanged().

    为了克服这些局限性, Jetpack提供了一些bridges, 比如androidx.lifecycle:lifecycle-livedata-ktx中的coroutine builder:

    val result: LiveData<Result> = liveData {
        val data = someSuspendingFunction()
        emit(data)
    }
    
    • 这段代码会根据生命周期自动取消(目标2).
    • 取消动作会延迟5秒, 如果新的activity立即取代, 则不会取消(目标3).
    • 只有值变了才会重新restart(目标1).

    如果repository返回的是流, 则可以这样做:

    val result: LiveData<Result> = someFunctionReturningFlow().asLiveData()
    

    其内部其实就是collect了一下:

    fun <T> Flow<T>.asLiveData(): LiveData<T> = liveData {
        collect {
            emit(it)
        }
    }
    

    Flow

    • Flow, SharedFlow和StateFlow.
    • StateFlow和LiveData.

    lifecycle:lifecycle-runtime-ktx:2.4.0推出的收集方法:

    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
            viewModel.result.collect { data ->
                displayResult(data)
            }
        }
    }
    

    或者是:

    viewLifecycleOwner.lifecycleScope.launch {
        viewModel.result
            .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
            .collect { data ->
                displayResult(data)
            }
    }
    

    后面又讨论了如何避免重播最新的value.

    Jetpack Compose navigation architecture with ViewModels

    在使用Compose的navigation时, 作者建议把导航的代码从UI中抽取出来:

    class Navigator {
    
        private val _sharedFlow = 
          MutableSharedFlow<NavTarget>(extraBufferCapacity = 1)
        val sharedFlow = _sharedFlow.asSharedFlow()
    
        fun navigateTo(navTarget: NavTarget) {
            _sharedFlow.tryEmit(navTarget)
        }
    
        enum class NavTarget(val label: String) {
    
            Home("home"),
            Detail("detail")
        }
    }
    

    导航代码:

    fun NavigationComponent(
      navController: NavHostController, 
      navigator: Navigator
    ) {
        LaunchedEffect("navigation") {
            navigator.sharedFlow.onEach {
                navController.navigate(it.label)
            }.launchIn(this)
        }
        
        NavHost(
            navController = navController,
            startDestination = NavTarget.Home.label
        ) {
            ...
        }
    }
    

    Coroutines under the hood

    协程的内部工作原理.

    有很多种选择来实现挂起函数, Kotlin用的是: continuation-passing style

    Jetpack Compose way to animate Android Views

    Compose结合Android View的动画.

    文章中有流程图.

    代码: https://github.com/andreymusth/stateful-animations

    Enabling cache & offline support on Android using Room

    利用Room实现离线模式.

    有精细的时序图.

    Understanding re-composition in Jetpack Compose with a case study

    理解recompose.

    问题来源: 有一段本该不recompose的代码recompose了, 为何.

    @Composable
    fun CounterRow(counter: Int, onButtonClick: () -> Unit) {
        /** SHOULD NOT BE CALLED ON SLIDER CHANGE **/
        Row(modifier = Modifier.fillMaxWidth()) {
            Button(onClick = onButtonClick) {
                Text(text = "Click me!")
            }
            Spacer(modifier = Modifier.width(24.dp))
            Text(text = counter.toString())
        }
    }
    

    这段代码recompose了, 引起变化的居然是第二个参数, lambda.

    关于compose的lifecycle的文档:
    https://developer.android.com/jetpack/compose/lifecycle

    原因就是当state变化时, lambda其实被重建了:

    ComposeStateTestTheme {
        val state: MainState by viewModel.state.collectAsState()
        MainScaffold(
            state,
            onValueUpdate = { viewModel.updateSlider(it.roundToInt()) },
            onButtonClick = { viewModel.updateCounter() }
        )
    }
    

    解决方法就是移出去:

    setContent {
        val state: MainState by viewModel.state.collectAsState()
        val onButtonClick = { viewModel.updateCounter() }
        ComposeStateTestTheme {
            MainScaffold(
                state,
                onValueUpdate = { viewModel.updateSlider(it.roundToInt()) },
                onButtonClick = onButtonClick
            )
        }
    }
    

    或者使用方法引用:

    setContent {
        ComposeStateTestTheme {
            val state: MainState by viewModel.state.collectAsState()
            MainScaffold(
                state,
                onValueUpdate = { viewModel.updateSlider(it.roundToInt()) },
                onButtonClick = viewModel::updateCounter
            )
        }
    }
    

    Basic Drag-n-Drop in Jetpack Compose

    Compose中的拖拽换位.

    在Roadmap中写了: Support Drag and Drop: https://developer.android.com/jetpack/androidx/compose-roadmap

    但是目前, 作者用现有的api实现了一个版本:
    https://gist.github.com/surajsau/f5342f443352195208029e98b0ee39f3

    Android Drag and Drop Tutorial

    基于Android View的拖拽教程.

    Principles and Techniques for Effective Localization

    国际化设计和实现要考虑的种种方面.

    Hilt Testing Best Practices

    Hilt在测试中的应用.

    Jetpack Compose: Building Grids

    在Compose中构建Grid.

    A Bit of Gradle Housekeeping

    gradle中已经可以清理掉的几个东西:

    android {
        buildToolsVersion "30.0.3"
    }
    
    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    
    android {
        kotlinOptions {
            jvmTarget = '1.8'
        }
    }
    

    以前这样写:

    android {
        compileSdkVersion 31
    
        defaultConfig {
            minSdkVersion 21
            targetSdkVersion 31
        }
    }
    

    现在可以改成这样:

    android {
        compileSdk 31
    
        defaultConfig {
            minSdk 21
            targetSdk 31
        }
    }
    

    还有:

    sourceSets.all {
        it.java.srcDir "src/$it.name/kotlin"
    }
    

    和:

    dependencies {
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    }
    

    Code

    相关文章

      网友评论

        本文标题:Android Weekly Notes #482

        本文链接:https://www.haomeiwen.com/subject/moegoltx.html