范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

使用更为安全的方式收集AndroidUI数据流

  在 Android 应用中,通常需要从 UI 层收集 Kotlin 数据流,以便在屏幕上显示数据更新。同时,您也会希望通过收集这些数据流,来避免产生不必要的操作和资源浪费 (包括 CPU 和内存),以及防止在 View 进入后台时泄露数据。
  本文将会带您学习如何使用 LifecycleOwner.addRepeatingJob、Lifecycle.repeatOnLifecycle 以及 Flow.flowWithLifecycle API 来避免资源的浪费;同时也会介绍为什么这些 API 适合作为在 UI 层收集数据流时的默认选择。  资源浪费
  无论数据流生产者的具体实现如何,我们都推荐从应用的较底层级暴露 Flow API。不过,您也应该保证数据流收集操作的安全性。
  使用一些现存 API (如 CoroutineScope.launch、Flow.launchIn 或 LifecycleCoroutineScope.launchWhenX) 收集基于 channel 或使用带有缓冲的操作符 (如 buffer、conflate、flowOn 或 shareIn) 的冷流的数据是不安全的,除非您在 Activity 进入后台时手动取消启动了协程的 Job。这些 API 会在内部生产者在后台发送项目到缓冲区时保持它们的活跃状态,而这样一来就浪费了资源。
  注意:冷流是一种数据流类型,这种数据流会在新的订阅者收集数据时,按需执行生产者的代码块。
  例如下面的例子中,使用 callbackFlow 发送位置更新的数据流:  // 基于 Channel 实现的冷流,可以发送位置的更新 fun FusedLocationProviderClient.locationFlow() = callbackFlow {     val callback = object : LocationCallback() {         override fun onLocationResult(result: LocationResult?) {             result ?: return             try { offer(result.lastLocation) } catch(e: Exception) {}         }     }     requestLocationUpdates(createLocationRequest(), callback, Looper.getMainLooper())         .addOnFailureListener { e ->             close(e) // 在出现异常时关闭 Flow         }     // 在 Flow 收集结束时进行清理操作      awaitClose {         removeLocationUpdates(callback)     } }
  注意:callbackFlow 内部使用 channel 实现,其概念与阻塞队列十分类似,并且默认容量为 64。
  使用任意前述 API 从 UI 层收集此数据流都会导致其持续发送位置信息,即使视图不再展示数据也不会停止!示例如下:  class LocationActivity : AppCompatActivity() {     override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)          // 最早在 View  处于 STARTED 状态时从数据流收集数据,并在         // 生命周期进入 STOPPED 状态时 SUSPENDS(挂起)收集操作。         // 在 View 转为 DESTROYED 状态时取消数据流的收集操作。         lifecycleScope.launchWhenStarted {             locationProvider.locationFlow().collect {                 // 新的位置!更新地图             }          }         // 同样的问题也存在于:         // - lifecycleScope.launch { /* 在这里从 locationFlow() 收集数据 */ }         // - locationProvider.locationFlow().onEach { /* ... */ }.launchIn(lifecycleScope)     } }
  lifecycleScope.launchWhenStarted 挂起了协程的执行。虽然新的位置信息没有被处理,但 callbackFlow 生产者仍然会持续发送位置信息。使用 lifecycleScope.launch 或 launchIn API 会更加危险,因为视图会持续消费位置信息,即使处于后台也不会停止!这种情况可能会导致您的应用崩溃。
  为了解决这些 API 所带来的问题,您需要在视图转入后台时手动取消收集操作,以取消 callbackFlow 并避免位置提供者持续发送项目并浪费资源。举例来说,您可以像下面的例子这样操作:  class LocationActivity : AppCompatActivity() {      // 位置的协程监听器     private var locationUpdatesJob: Job? = null      override fun onStart() {         super.onStart()         locationUpdatesJob = lifecycleScope.launch {             locationProvider.locationFlow().collect {                 // 新的位置!更新地图。             }          }     }      override fun onStop() {        // 在视图进入后台时停止收集数据         locationUpdatesJob?.cancel()         super.onStop()     } }
  这是一个不错的解决方案,美中不足的是有些冗长。如果这个世界有一个有关 Android 开发者的普遍事实,那一定是我们都不喜欢编写模版代码。不必编写模版代码的一个最大好处就是——写的代码越少,出错的概率越小!  LifecycleOwner.addRepeatingJob
  现在我们境遇相同,并且也知道问题出在哪里,是时候找出一个解决方案了。我们的解决方案需要:  简单;  友好或者说便于记忆与理解;  更重要的是安全!无论数据流的实现细节如何,它都应能够应对所有用例。
  事不宜迟——您应该使用的 API 是 lifecycle-runtime-ktx 库中所提供的 LifecycleOwner.addRepeatingJob。请参考下面的代码:  class LocationActivity : AppCompatActivity() {     override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)          // 最早在 View  处于 STARTED 状态时从数据流收集数据,并在         // 生命周期进入 STOPPED 状态时 STOPPED(停止)收集操作。         // 它会在生命周期再次进入 STARTED 状态时自动开始进行数据收集操作。         lifecycleOwner.addRepeatingJob(Lifecycle.State.STARTED) {             locationProvider.locationFlow().collect {                 // 新的位置!更新地图             }          }     } }
  addRepeatingJob 接收 Lifecycle.State 作为参数,并用它与传入的代码块一起,在生命周期到达该状态时,自动创建并启动新的协程;同时也会在生命周期低于该状态时取消正在运行的协程。
  由于 addRepeatingJob 会在协程不再被需要时自动将其取消,因而可以避免产生取消操作相关的模版代码。您也许已经猜到,为了避免意外行为,这一 API 需要在 Activity 的 onCreate 或 Fragment 的 onViewCreated 方法中调用。下面是配合 Fragment 使用的示例:  class LocationFragment: Fragment() {     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {         // ...         viewLifecycleOwner.addRepeatingJob(Lifecycle.State.STARTED) {             locationProvider.locationFlow().collect {                 // 新的位置!更新地图             }          }     } }
  注意:这些 API 在 lifecycle:lifecycle-runtime-ktx:2.4.0-alpha01 库或其更新的版本中可用。
  使用 repeatOnLifecycle
  出于提供更为灵活的 API 以及保存调用中的 CoroutineContext 的目的,我们也提供了 挂起函数 Lifecycle.repeatOnLifecycle 供您使用。repeatOnLifecycle 会挂起调用它的协程,并会在进出目标状态时重新执行代码块,最后在 Lifecycle 进入销毁状态时恢复调用它的协程。
  如果您需要在重复工作前执行一次配置任务,同时希望任务可以在重复工作开始前保持挂起,该 API 可以帮您实现这样的操作。示例如下:  class LocationActivity : AppCompatActivity() {     override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)          lifecycleScope.launch {             // 单次配置任务             val expensiveObject = createExpensiveObject()              lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {                 // 在生命周期进入 STARTED 状态时开始重复任务,在 STOPED 状态时停止                 // 对 expensiveObject 进行操作             }              // 当协程恢复时,`lifecycle` 处于 DESTROY 状态。repeatOnLifecycle 会在             // 进入 DESTROYED 状态前挂起协程的执行         }     } }Flow.flowWithLifecycle
  当您只需要收集一个数据流时,也可以使用 Flow.flowWithLifecycle 操作符。这一 API 的内部也使用 suspend Lifecycle.repeatOnLifecycle 函数实现,并会在生命周期进入和离开目标状态时发送项目和取消内部的生产者。  class LocationActivity : AppCompatActivity() {     override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)          locationProvider.locationFlow()             .flowWithLifecycle(this, Lifecycle.State.STARTED)             .onEach {                 // 新的位置!更新地图             }             .launchIn(lifecycleScope)      } }
  注意:Flow.flowWithLifecycle API 的命名以 Flow.flowOn(CoroutineContext) 为先例,因为它会在不影响下游数据流的同时修改收集上游数据流的 CoroutineContext。与 flowOn 相似的另一点是,Flow.flowWithLifecycle 也加入了缓冲区,以防止消费者无法跟上生产者。这一特点源于其实现中使用的 callbackFlow。配置内部生产者
  即使您使用了这些 API,也要小心那些可能浪费资源的热流,就算它们没有被收集亦是如此!虽然针对这些热流有一些合适的用例,但是仍要多加注意并在必要时进行记录。另一方面,在一些情况下,即使可能造成资源的浪费,令处于后台的内部数据流生产者保持活跃状态也会利于某些用例,如:您需要即时刷新可用数据,而不是去获取并暂时展示陈旧数据。 您可以根据用例决定生产者是否需要始终处于活跃状态。
  您可以使用 MutableStateFlow 与 MutableSharedFlow 两个 API 中暴露的 subscriptionCount 字段来控制它们,当该字段值为 0 时,内部的生产者就会停止。默认情况下,只要持有数据流实例的对象还在内存中,它们就会保持生产者的活跃状态。针对这些 API 也有一些合适的用例,比如使用 StateFlow 将 UiState 从 ViewModel 中暴露给 UI。这么做很合适,因为它意味着 ViewModel 总是需要向 View 提供最新的 UI 状态。
  相似的,也可以为此类操作使用 共享开始策略 配置 Flow.stateIn 与 Flow.shareIn 操作符。WhileSubscribed() 将会在没有活跃的订阅者时停止内部的生产者!相应的,无论数据流是 Eagerly (积极) 还是 Lazily (惰性) 的,只要它们使用的 CoroutineScope 还处于活跃状态,其内部的生产者就会保持活跃。
  注意: 本文中所描述的 API 可以很好的作为默认从 UI 收集数据流的方式,并且无论数据流的实现方式如何,都应该使用它们。这些 API 做了它们要做的事: 在 UI 于屏幕中不可见时,停止收集其数据流。至于数据流是否应该始终处于活动状态,则取决于它的实现。在 Jetpack Compose 中安全地收集数据流
  Flow.collectAsState 函数可以在 Compose 中收集来自 composable 的数据流,并可以将值表示为 State,以便能够更新 Compose UI。即使 Compose 在宿主 Activity 或 Fragment 处于后台时不会重组 UI,数据流生产者仍会保持活跃并会造成资源的浪费。Compose 可能会遭遇与 View 系统相同的问题。
  在 Compose 中收集数据流时,可以使用 Flow.flowWithLifecycle 操作符,示例如下:  @Composable fun LocationScreen(locationFlow: Flow) {      val lifecycleOwner = LocalLifecycleOwner.current     val locationFlowLifecycleAware = remember(locationFlow, lifecycleOwner) {         locationFlow.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)     }      val location by locationFlowLifecycleAware.collectAsState()      // 当前位置,可以拿它做一些操作 }
  注意:您需要记得生命周期感知型数据流使用 locationFlow 与 lifecycleOwner 作为键,以便始终使用同一个数据流,除非其中一个键发生改变。
  Compose 的副作用 (Side-effect) 便是必须处在受控环境中,因此,使用 LifecycleOwner.addRepeatingJob 不安全。作为替代,可以使用 LaunchedEffect 来创建跟随 composable 生命周期的协程。在它的代码块中,如果您需要在宿主生命周期处于某个 State 时重新执行一个代码块,可以调用挂起函数 Lifecycle.repeatOnLifecycle。  对比 LiveData
  您也许会觉得,这些 API 的表现与 LiveData 很相似——确实是这样!LiveData 可以感知 Lifecycle,而且它的重启行为使其十分适合观察来自 UI 的数据流。同理 LifecycleOwner.addRepeatingJob、suspend Lifecycle.repeatOnLifecycle 以及 Flow.flowWithLifecycle 等 API 亦是如此。
  在纯 Kotlin 应用中,使用这些 API 可以十分自然地替代 LiveData 收集数据流。如果您使用这些 API 收集数据流,换成 LiveData (相对于使用协程和 Flow) 不会带来任何额外的好处。而且由于 Flow 可以从任何 Dispatcher 收集数据,同时也能通过它的操作符获得更多功能,所以 Flow 也更为灵活。相对而言,LiveData 的可用操作符有限,且它总是从 UI 线程观察数据。
  数据绑定对 StateFlow 的支持
  另一方面,您会想要使用 LiveData 的原因之一,可能是它受到数据绑定的支持。不过 StateFlow 也一样!更多有关数据绑定对 StateFlow 的支持信息,请参阅官方文档。
  在 Android 开发中,请使用 LifecycleOwner.addRepeatingJob、suspend Lifecycle.repeatOnLifecycle 或 Flow.flowWithLifecycle 从 UI 层安全地收集数据流。  最后
  在这里就再分享一份由大佬亲自收录整理的Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
  这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
  当然,你也可以拿去查漏补缺,提升自身的竞争力。
  真心希望可以帮助到大家,Android路漫漫,共勉!
  如果你有需要的话,只需私信我【进阶】即可获取

区块链技术分享初步应用2区块链技术分享起源1简单分享了比特币的起源。既然比特币可以脱离物理介质,仅通过互联网就可以流通,那么比特币到底存在哪?比特币是怎么交易的?区块链到底是什么?今天这篇文章将一一进行解新基建提了三年,谈起区块链你竟然只知道比特币今天跟大家分享新基建与区块链。01区块链有多受重视党的十九大以来,中共中央政治局已进行19次集体学习,其中三次与数字经济相关,主题分别是大数据人工智能和区块链,区块链的地位被提的最酷睿i912900KROGZ690EXTREME首测重返战力之巅文章开头不妨先说结论1Corei912900K提升非常大,部分场景对比10900K提升能超过502Corei512600K性能已经与10900K持平,甚至略优3最高功耗是给时刻保持区块链技术之哈希指针hello,大家好,我们第三期的区块链技术分享来啦,那么话不多说,我们开始吧。提起区块链,大家可能都会提到不可篡改。但是为什么区块链不可篡改呢?先给出答案,这与区块链的数据结构哈希12代酷睿正式发布Corei912900K拉满5。2GHz,DDR5PCIe5。0齐发就在今天,英特尔正式发布第12代酷睿系列处理器,首发非锁频CPU包括酷睿i912900KKF酷睿i712700KKF酷睿i512600KKF。对于12代酷睿,英特尔做了数个总结,可为什么有的网址开头是https,有的却是http?hello,大家好,我们第二期的区块链技术分享来啦,本期是candy分享公钥加密,也就是非对称加密。提到加解密,密码学这些词汇,很多人都退避三舍,如临大敌,觉得晦涩难懂,自己不想懂区块链复盘及规划hello,大家好,好久不见分享区块链有一段时间了,也有近两周没有更新了。确实遇到了点小瓶颈,因为越研究越发现区块链涉及的技术领域很广,很多东西想讲清楚非一时之功,所以自己也有些困英特尔AI顾问穿越星际守护宇航员健康近日,英特尔人工智能(AI)顾问与前沿开发实验室(FDL)的研究人员进行了一项关乎宇航员健康的具有里程碑意义的研究,以便更好地了解辐射暴露对宇航员的生理影响。利用英特尔的人工智能技区块链技术分享起源1现下区块链大火,聊到相关话题,我们常常的反应年轻人怎么可能轻易认输呢?所以,我们打算写一系列文章揭开区块链的神秘面纱。提到区块链,常常会谈到一个高频词比特币。说到比特币,有个神秘的12代酷睿战斗力顶梁柱ROGMAXIMUSZ690EXTREME登场在性能尚未解禁之前,玩家们对新一代酷睿的战斗力所有猜测,有许多是来自于ROGMAXIMUS平台运行时的曝光。无疑作为主板选择中的金字塔级产品,ROGMAXIMUS几乎坐稳了不可动摇英特尔加速高性能计算技术创新以XPU架构引领E级计算时代2021CCF全国高性能计算学术年会(HPCChina2021)于今日正式拉开帷幕。此次会议期间,英特尔及其合作伙伴就如何通过高性能计算应对当今世界的重大挑战展开探讨,并展现了英特
中国钍基熔盐堆和美国小型核聚变的一次交集什么是科技布局?编者按钍基熔盐堆因其以熔盐为冷却剂,还具有常压工作无水冷却特性,可建于地下和干旱地区。熔盐堆输出温度为700度以上,可用于高温制氢二氧化碳加氢制甲醇等高温热利用领域,对减少温室气体共和国摇篮红都瑞金处处都是革命遗址从赣州坐着火车来到了瑞金。瑞金市是共和国摇篮中央苏区时期党中央驻地中华苏维埃共和国中央政府诞生地中央红军二万五千里长征出发地,是名副其实的红都。航拍家家户户的红房顶栏杆上的中华苏维如此香润玉温的万元级播放器,不想摸一摸么?我记得大概五六年前吧,安卓手机们正在玩三国演义那种剧本,打到百分之三十进度且忙着群雄逐鹿宰割天下,赌命赌身家。有些品牌几个月就挣了大钱,还有的品牌不到半年就败掉身家,浮浮沉沉全无任HidizsS9这款这尾巴,让人不得不爱不得不爱这首潘玮柏和弦子合唱的流行歌,很老了,2005年的歌,十五年了。2005的我们,大多在用着诺基亚三星们的功能机,能收发个彩信看个WAP网页就差不多了,数着字数发短信数着时间5G时代,华为手机地位进一步项固,华为Mate305G稳占65还有几天就迈进2020年,为了抢夺新一年的手机市场,不少手机厂商在2019年末就纷纷发布5G手机。不过,除了华为有自主的5G研发能力,其它手机厂商都要看高通的脸色,在5G手机的市场下一个爆款TWS?JBLLIVEPRO真无线林氏评测JBL这个品牌,实在是闷声发大财的典范。早在几年前,AKGK420卖成全网爆款的时候,同为哈曼旗下品牌的JBL就悄咪咪的开始了自己全新而宏大的市场布局,这个布局基于几个前提首先JBThinkPadX1Nano带给我的,不只是轻薄那个女孩的名字,我已经忘记了,只记得一件事那就是我曾经嫉妒过她那应该是2005年的时候,这位女同学跟我关系平平,平日里没啥交集。某天忽然就拉住我请我帮个忙,具体来说就是家里给她一笔哪吒归来!AustrianAudioHiX50评测几年前去漫步者做客,张老板讲了他们收购STAX前的一些故事,据说当时谈判的时候,俩日本老爷子可硬气可倔强,这也不许那也不行,你敢有半分违背人家原则就能立马撂挑子不谈了,宁愿牌子关张audioproADDONT5音箱北欧的欧若拉青春期那会儿,接触到一个名词,叫做北欧极简风,这词一下子就对了胃口。现在想来,原因还挺多的,比如年轻人面对成人世界的复杂莫测,会自然而然的对简这个字产生好感比如我小时候就很神往北欧产量占全球50,出口欧美净利润仅3。8,只因国产核心零件不被认可我国曾经被称为自行车王国,上个世纪80年代和90年代,北京和上海的街头几乎都是骑着自行车上班的人群,很多外国人首次到了中国感觉到很震惊,相比于当时欧美已经普及家用汽车,中国人骑自行北方最大服装批发集散地月底正式关闭,2。8万商户该去哪?作为北方最大的服装批发集散地大红门,将于10月31日正式关停。对于做了十几年的2。8万商户来说,关闭的不仅仅是商城,而是曾经的辉煌,更是一个时代。他们该何去何从?大红门完成了它的历