Share logo
Created with Sketch.
Created with Sketch.
首页
发现
小书
登录


注册小专栏
友情提示
Login FAQ .

邮箱登录

邮箱注册

66a7bbb41096d9d742d4553a9b59f6af
icon_photo_1 Created with Sketch.

KunMinX

乐于_深度思考现象_和_形成自恰体系
100 / 140
关注 5 被关注 737 获得赞 370
Created with Sketch. Created with Sketch.
Created with Sketch. Created with Sketch.

个人书籍

个人专栏

A5b5f83693c712de7e1c4f197fb3e1b5
深度写作输出
专栏简介 “深度写作专栏” 的由来: 1. 经过过去 1 年对《重学安卓》专栏的阅读,小伙伴们多已见证...
已发表 1 订阅数 534
Ccdc5a476b22a6b99e4226a0f00224bd
重学安卓
· 付费读者加微信进群:myatejx 【专栏简介】: 你是想在含糊、冗长的网文中痛苦地打滚,还是跟随我的文字...
已发表 28 订阅数 909
  • 文章
  • 订阅
  • 回复
  • 喜欢
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @刘磊47 非常感谢你的认真阅读和指出错误!现已修正 😉。 此外,我在网上搜到一篇关于 FLAG_ACTIVITY_NEW_TASK 使用场景的文章,感兴趣的话欢迎移步阅读 ~ 😉 https://blog.csdn.net/qq_27247601/article/details/84564923
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
说的好!最讨厌被人指指点点了。放下手头的事不做,跑来凑热闹、发表高见、掣肘、侮辱、诽谤、恶意曲解、人身攻击。说到底,还是吃得太饱了。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
感谢分享!可以这样理解吗?FFmpeg 提供了一套视频操作的标准,于是任何平台都可以通过同一套协议去声明想要干的事?
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @mdj456000 此外,非常感谢你在专栏还没有太多内容时 便选择支持我!这个专栏是出于个人情怀而开设的,因而我一定会负责到底。现在的价格,只给一直以来支持我的你(们)而准备,后续一定会涨价的。再次感谢你(们)的关心和支持!
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @mdj456000 很抱歉上周因为身体原因没有续更。非常感谢你的期待!新一期的文章,应该在这两日就会安排上了~
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @zengjiaozi 很抱歉给你带来了麻烦!我可能是额外上传了一些非主题相关的代码,将在今晚之前检查和移除。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
非常抱歉给大家造成困扰!文章写的时候,记录的现象是正确的。时间一久,当读者们在评论区确认情况的时候,我却给出了截然相反的、错误的答复。😵 所以,为避免对其他读者造成困扰,已及时联系管理员对我在 4、5 楼的错误评论删除。 一般我写文章都会经过反复地校对、改稿。我记得我在 Fragment 那篇改了不下 70 次,本地产生了 70 个备份。 考虑到评论区一旦评论就无法自行删除,所以未来回复也会更加负责和严谨。 再次感谢楼上读者 @刘磊47 @飘雪撒哈拉 的认真阅读!
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
关心 Jetpack 系列的朋友请麻烦再等一等 😂,好的文章需要耐心、耐心、再耐心地打磨。好的文章需要耐心、耐心、再耐心地打磨。Navigation 的章节我已经打磨好了,但它是安排在 VM 和 LD 后的。为了帮助大家明确 ViewModel 和 LiveData 存在缘由和背景状况,也是 ... 😂 请麻烦再等一等!
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
笔误:taskAffinity 是指定 SingleTask ActivityRecord 所在 taskRecord 的名字
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#4楼 @飘雪撒哈拉 很简单,因为 URI ,是明确地指定了路径的资源定位符,本质上是“显式声明”,MineType,是含糊地给出一个标准,能匹配这个标准的都行,本质上是“隐式声明”。因而他们被设计成是互斥的。 那为什么最后又给出“既显式又隐式”的设计呢?大概是考虑到,URI 的协议不仅仅是本地的 file、content,也可能是 http,这样就是从远程拿数据了,很可能 URI 本身指向的是目录,那怎么办呢?那只好再加个 MineType 来筛选我要的具体文件了。😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @飘雪撒哈拉 不是的,singleTask 的含义是,在一个 taskRecord 中只包含唯一一个该 Activity类 的 ActivityRecord。如果是不同 taskRecord 中,可以存有同一个 Activity 类的不同 ActivityRecord。taskAffinity 是指定 taskRecord 所在 taskRecord 的名字,不存在就给它新建一个。singleInstance 是你说的,保证系统只有唯一实例。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#6楼 @飘雪撒哈拉 非常感谢你的认真阅读! 之前也没深究这个情况,只是确认了 singleInstance 是系统级单例。 singleTask 如你思考的,确实也是全系统唯一实例。 刚刚试了一番,我本想给它动态赋予一个 taskAffinity 来在不同的任务中建立新的实例,没想到条件不允许啊 —— taskAffinity 只能在 Activity 所在应用的 menifest 中设置(怎么看出来的呢?在 ActivityRecord 源码中,你可以通过关键字搜索到 taskAffinity,顺藤摸瓜发现是 restoreFromXml 这个方法中,通过 ActivityStackSupervisor.resolveActivity 拿到的 ActivityInfo 中拿到的)。 所以确实也成了全系统唯一实例的情况了。恭喜你的新发现!
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#5楼 @Skyoasis 感谢你的喜欢 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @1274 没关系 时间时间会给你答案
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @飘雪撒哈拉 应该不会的,我是在 API 28 上测试的,并且阅读过源码。重学安卓项目的 dispatch 包下有打好 log 的页面,你不妨跑一下。 如果 Fragment 的 onCreate 是出现在 Activity 的 onStart 之前的话,那我想会不会是你在 Activity 子类中将 Log 打在 super.onStart 之后了。如果这样的话,对现象的描述就不正确了,因为 Fragment 的 onCreate 在 Activity 的 super.onStart 中已经走完了,也即实际上还是发生在 Activity (基类)的 onStart 时
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @飘雪撒哈拉 如果还是不太理解,我再补充说明一下吧。我在二楼对此现象做出的解释是,它是因为“没办法”,才这么设计的。 · 因为源码出于模板方法模式,要尽可能地将细节封装好,不暴露给开发者、让开发者的工作尽可能简便,所以这些复杂的工作都在基类的 super 回调中默默完成了。 · 开发者最早能动态添加 fragment 的时机是在 Activity 子类的 onCreate 中,此时已经错过了 Activity 基类的 super.onCreate,所以没办法,开发者动态添加的 frgament,就只能顺延到下一个节点 ---- Activity 基类的 super.onStart 中默默被执行。 · 了解这个现象对我们的启示是,不要在 Activity 子类的 onCreate 中调用可能触及到 Fragment 成员变量(例如 view)的 Fragment 的成员方法,即使 Fragment 本身已经被实例化。因为此时 Fragment 还未正式地被创建,它旗下的 views 也都还未被创建,此时去调用,在方法内未作判空处理的情况下,就会造成空指针异常。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @红红火火恍恍惚惚 感谢你的阅读 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @三颗猫 感谢你的阅读。上述提到的这个假设并不存在。 · 首先,系统回收的一定是处于后台的整个 App 进程,而非 App 中的单个组件或对象。 · 其次,播放时,为了进程保活,普遍的做法是开启一个前台服务,它能使 App 进程保持“可见模式”,即使 App 中的所有界面组件都已被 destory。这样,作为单例的 PlayerController 也就持续存活着。 · 再者,将状态放在专门的 PlayerController 或 ViewModel 等单例中管理,正是为了独立于 Activity、Fragment 等视图控制器组件的生命周期,在视图控制器哪怕被重建的情况下,也能第一时间继续从单例中获取状态。 · 具体可以参考《Activity 生命周期的 3 个辟谣》、《绝不丢失状态的 Activity 重建机制》这几章节。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#11楼 @KunMinX 强迫症犯了 😂 10楼 最后一段 段首 的 “还有” 俩字,替换为 “此外,”
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @Will_夏 感谢你的阅读 😉 因为工作忙碌的缘故,交流群可能会疏于打理,而暂时没有开通。 在阅读的过程中如有疑问,可直接在评论区留言和讨论。 我看到了,必给予回复,知无不答。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#10楼 @李正阳Lee 感谢指出,已及时修正。😉 · 实际上此处应该这么描述:ActivityStack 栈顶的 TaskRecord 表示当前已获得焦点的任务栈,该任务栈的栈顶 ActivityRecord 表示当前已获得焦点的窗口。 · 还有对 “前景” 和 “可见” 这两个概念不熟悉的朋友,可以详见 生命周期辟谣一文,里面通过介绍 RemixOS 等安卓桌面操作系统,为你直观地建立起关于 “前景” 和 “可见” 的感性认识 😉 https://xiaozhuanlan.com/topic/0213584967
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @三颗猫 这里我再补充说明一下。 重建的概念,只存在于视图控制器(Activity、Fragment)中。所以有些事我们放在服务或单例中完成,以免跟随视图控制器的生命周期,而被误 destory。 重建机制存在的缘由,可参考《绝不丢失状态的 Activity 重建机制》中的描述。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
临时补充一下,二楼提到的 FLAG_ACTIVITY_NEW_TASK 十分有用。例如在广播中启动一个 Activity,我们需要通过该 flag 来为 Activity 准备一个容身之处,如果没有的话。并且,在 Notification 使用的 pendingIntent 封装的 getActivity 方法中,我们同样可以看到对 FLAG_ACTIVITY_NEW_TASK 的使用。 可见,这个 Flag 还是十分常用的。各位在广播启动 Activity 时,记得加上这个 flag。😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#17楼 @Will_夏 很抱歉给你造成困扰,考虑到 android 圈子内暂时没有对 ActivityRecord、taskRecord 等概念进行公认的翻译,为不造成曲解,而直接使用英文概念。 · ActivityRecord 是一个类,职责是存储与 Activity 相关的信息,简言之,你可以把它当做被实例化过的、被 taskRecord 管理着的 Activity 实体。 · 一个Activity 可以对应多个 ActivityRecord(当启动模式为 standard 时),就像一个类可以被实例化为多个对象。 · taskRecord 即任务,任务因为本身是以栈结构来存储 ActivityRecord,所以也称任务栈。任务不同于 ActivityStack,ActivityStack 是更大的、用于存储多个任务的栈结构。 · ActivityStack 暂时没有对应的翻译,我将它理解为实际上的返回栈,因为它确实能引导来自多个不同的任务的 Activity 的按顺序回退。(详见文末动图中的实验)
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#4楼 @Darren75 感谢你的阅读和补充!😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#5楼 @2333348 目前是使用 ProcessOn 在线工具绘制 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @Will_夏 补充一下,该应用名叫《童话故事社》,是一款给宝宝讲睡前故事的音频点播电商应用。目前已拿到软著,正在筹备国内应用商店的上架工作。 我在《RxJava 才不是 LiveData 的对手》一文中提供了软件的截图,上架后会在文中提供下载链接,敬请期待 😉 https://xiaozhuanlan.com/topic/0168753249
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @Will_夏 上个月,我用了将近 3 周的时间,独立负责了 29 个页面、34 个 API、涉及 350 余个细节 的中大型电商软件的开发。 该项目正是纯粹的单 Activity 应用,29 个页面全部使用 Fragment 来管理。 并且,正因为是单 Activity 应用,使得 ViewModel 的作用域共享可以很好地发挥出作用。 · 无论手机和平板,都可以只用一个 Activity 来承载多个 Fragment。分别用一个 Activity 来承载 List 和 Detail Fragemnt,这种做法并不提倡,因为 Activity 是面向跨进程通信而生的,启动 Activity 需要消耗大量资源,且加载速度远不如 Fragment 快。 这些我在文中已经分析过了。 · 最后,关于单 Activity 应用对 Fragment 的管理,可以参考我在 V2EX 发表的帖子。 https://www.v2ex.com/t/590889#reply16
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#6楼 @Will_夏 感谢你对现象的思考和补充 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @锋。 另外 DataBinding 遗憾的是,没有实现代码自动生成。 · 我对 DataBinding 的期望是,在编译时能够根据布局直接生成 Java 代码,而不是运行时动态 inflate,这样就能给到我必用 DataBinding 的理由,哪怕项目无需考虑横竖屏布局的一致性问题。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#32楼 @结晶果 暂时没办法在控制台观察。这里有个小技巧,当你调试源码时,测试机的 API 要和项目 compileSdkVersion 一致,这样就能一步步调试。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @Will_夏 可以这么理解,DataRepository 是基础数据组件,负责响应 UI 层发起的对数据状态的请求。 · 这些数据可以来自本地数据库查询到的结果,也可以来自 http 从后端请求到的结果。具体来自哪里,UI 层无需知道,是结合具体业务情况来制定数据获取策略。 · 例如如果是新闻或社交 app,对数据时效性要求高,那么总是请求网络。如果是资源类 app,比如音乐、视频,可以先查本地缓存或数据库,没数据再从网络获取。 · 但就像最开始说的,无论如何,UI 层无需知道数据怎么来的。这就是 DataRepository 作为基础数据组件的意义所在。可以近似于 Model 层吧,因为 Model 层实际上还可以容纳其他数据组件,例如多媒体组件等等。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @锋。 刚刚在《重学安卓》项目的 jetpack third fragment 中测试了一把 DataBinding,如我所料,配合 LiveData 可以实现数据驱动,这可真是 大型真香现场啊。。😆
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#27楼 @结晶果 关于这个问题,请运行《重学安卓》项目。在 task 篇中,通过简单的修改,即可验证你的疑问。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#4楼 @锋。 建议分层。视图控制器层,专门发起请求和处理 UI 逻辑,那么当 EditText 发生变化时,可调用 ViewModel 的方法去向数据层发起请求。 · 该方法中注入 liveData,从而在数据层能够拿到 liveData 并 setValue 从而通知 视图控制器层 observe 最终的 UI 逻辑。 · 此外不需要任何注册和解绑的工作。毕竟基于 LifeCycler 的架构组件,本身就是为了消除这些“一致性问题” 而存在的。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
很棒,配套的案例让人直观地明白了效果。 分散互补色 一节 的 Lunar 和 便签 的插画很好看,只是难以辨认它们具体是属于哪三个分散互补色,是不是说,两个分散的互补色不一定非得是互补色的相邻色呢
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
色彩第一课 看完觉得耳目一新。 不同于网上支离破碎地介绍色彩概念,作者从 人类色彩感知 入手,通过自己的思考,形成了对色彩自恰的认识。那我想这才算真正的知识吧,因为就连设计小白的我,看完也能印象深刻。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#4楼 @luckykelan 你好,AS 3.5 开始提供 Navigation 资源的创建、预览等功能,如果在早期的 AS 中运行带有 Navigation 代码的项目,AS 会提示让你升级到 3.5。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @锋。 你好,这个根据实际情况来重写。 例如重写方法中,直接对 replace 那行做个改写:如果 backStack count 大于 0,就 show hide。这样就可以。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#10楼 @KunMinX 如果能自动生成布局代码,我就可以直接将 build 的代码布局 copy 到项目中加混淆,然后清空 xml 布局,以防止破解者通过布局顺藤摸瓜,提高破解的难度。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
很棒!是对着红色图片码字的缘故吗?可以感觉到文字的潇洒~ 文章开头对红色感觉由来的猜想十分精彩。视觉是很重要的感官感觉,好似从有利于生存的角度去琢磨,总能想明白色彩的意义之所在。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#20楼 @结晶果 非常感谢你的细心阅读和指出错误!已及时纠正文中的不妥当描述。 · singleTask Activity 所属任务,是处于该 Activity 所属 App 中的任务。在该 Activity 被启动时,若无任务可寄生,会新建一个任务。该任务来自所属 App,但会移动到当前系统中处于焦点状态的 ActivityStack。 · 换言之,如果是别的 App 的组件启动了这个 singleTask Activity,那么该 Activity 及其所属任务,都会跑到处于焦点的 App 的 ActivityStack 中。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#6楼 @KunMinX 简言之,复杂情况下的 UI 逻辑照写不误。 · 原先在视图控制器中引用 视图去刷新状态,现在将那些无需复杂逻辑的状态变化,直接改为在 DataBinding 中绑定 ViewModel 中的 LiveData,而无需再在 Fragment 中通过 LiveData.Observe 观察和调用视图去刷新。 · 原先复杂的 UI 逻辑,现在仍使用 LiveData.Observe 观察,但通知 视图变化的方式一律仅限于 LiveData 的 setValue,不直接引用 视图。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#26楼 @结晶果 感谢你对问题的不懈思考 😉 很简单,其实站在源码设计师的角度,就不难理解为何如此设计。 · 如果我是 Google 源码设计师,我一定会将 HomeStack 和 RecentStack 分开管理。为什么这么设计呢?因为 HomeStack 要是以 RecentStack 的模式去生存,那么理应我在“最近访问列表”中就可以找到 Launch App 的记录,那我将其清理了怎么办?何况作为桌面,本来就不应该展示在最近访问中。 · 关于上述现象,如感兴趣的话,可以自行在 ActivityStackSupervisor 类中找到自己想要的答案。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @锋。 抱歉,刚刚没审清楚。目前主要有两种办法,一种是完全重写,也即替代 androidX 的 navigation 包,另一种是继承 fragmentNavigator,就像你提到的,backStack 部分就不去管了,因为它作用的对象是作为 container 的 Fragment 中的 childFragments,使它们状态不被销毁。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#24楼 @KunMinX 这里更严谨地补充说明一下,“最近访问列表” 中是以任务为单位展示的。ActivityStack 的存在,可以间接地从 “最近访问记录” 中看出来 —— 通过 “最近访问列表” 中,处于同一 ActivityStack 的任务之间的 “返回体验” 感受到。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#7楼 @宋港 Souffle 😄 它的名字叫作 卡片日记,iOS Android 都有
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
补充一下。网上老看到有人说,在 ViewModel 中写业务逻辑。对此我是持反对意见的。理由如下: · ViewModel 的生命周期是跟随 ViewModelStoreOwner 的,Owner 一旦 destoryed,那么 ViewModel 也会走 clear,而触发 onClear。如果你在 onClear 中手动置空了业务变量,而业务方法此时又在执行中,那么就容易造成空指针异常。 · 业务逻辑应该放在具体的数据组件中写,例如 DataRepository,作为基础数据组件,既持有了 httpManager,又持有了 dbManager,各种缓存策略,以最合适的方式拿到前台 UI 想要的状态数据。 · 在数据组件中处理业务逻辑,通过唯一可信源,以数据驱动的方式来统一将状态分发给前台 UI,于是,当前台 UI 健在,就会接受数据、响应 UI 逻辑,否则,就会因 LifeCycler 的加持,而不至于让数据接触到空的视图引用、造成不可预期的错误。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
涨知识了,以前没思考过为何要存在不同的色彩模式。 另, { 与CMYK 模式相反,RGB 模式利用的是“减色原则”成色 } 此处貌似笔误了
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#34楼 @结晶果 很抱歉给你造成困扰,是这样的,一个 Activity 类对应多个 ActivityRecord,ActivityRecord 就是作为被管理的 Activity 实例。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#11楼 @KunMinX 并且代码布局执行效率比 inflate 高出不少。。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#15楼 @锋。 没关系,DataBinding 确实没有 LiveData 和 ViewModel 影响来得深远。改变观念是不可能的,因为观念本身就是脱离事实的。如果你对 DataBinding 感兴趣的话,不妨开源一个小项目,让更多的有识之士认识到 DataBinding 的妙用。😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#38楼 @Sky63 感谢你的阅读。 确是走 D-C-B-A 的,因为 D 所在的任务因 D 被 B 唤起,而获得了前景焦点,那么唯有该任务中的所有 ActivityRecord 都出栈了,该任务才会出栈,而轮到 B 所在任务置于前景。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#6楼 @锋。 嗯,LiveData 被设计为是应被 ViewModel 或其他单例持有,来保证单向依赖和唯一可信源分发。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @宋港 Souffle 作者可以专门找一期,用自己的知识来 点到为止地解构一下 这款广受欢迎的日记 App 的设计秘诀吗? https://images.xiaozhuanlan.com/photo/2019/5bb7ef1dc2f4844d05a415275792dc51.jpg https://images.xiaozhuanlan.com/photo/2019/85d676c39271eb12acd4580134388fa4.jpg 一直以来,我都觉得设计是一门神秘的、难以掌握其依据的学科。 优秀的设计一经面市,让人耳目一新,但我根本搞不明白为什么能够造成这种感觉。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @宋港 Souffle 可能是我太缺乏设计方面的知识了,没有一个立足点。 刚刚摘掉眼镜后发现,即使画面模糊得不成形状,可好看依然是让人觉得好看。 不如我就趁着中秋假期多读几遍作者的专栏 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @Sky63 大概明白你提到的、造成困扰的地方了。 D 和 C 是由 app1 启动,在 IDE 中可通过 app1 的视角观察到。同理,当 app2 的 B 唤起 D 时,你在回退 D 的时候能在 app2 和 app1 的视角中同时观察到 D 的销毁。而紧随其后再回退一次, 便能在 app1 中观察到 C 被销毁。再下一次才轮到 app2 中 B 被销毁。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
大家不用理会这个巨婴,我已联系管理员退款、并将其拉入黑名单,永久不得订阅本专栏。 · 小专栏的 每篇文章 都提供 40% 的预览,是不是自己需要的,在订阅前 就有责任 透过文章 免费提供的预览 去尝尝看。 · 为了推卸责任,不惜逾越规则和编造理由,不仅是对作者、对其他读者的不尊重,也是对自己的不尊重。 · 我不是他爸爸,没有义务教育这样的人。只求某天不要为了逃票,而侥幸翻入动物园的猛兽专区吧。 · 本专栏文章的设计目标,一律是以 年 为时间跨度来斟酌,一经编写,就得经得起长时间的考验,而不是过几天就过时和坍塌的快餐文。 · 何况,本人的写作目标是方便读者 快速明确状况,绝不为了凑数而 发表 东拼西凑、人云亦云、徒添困扰、不负责任 的地摊文。 · 很高兴从 8 月起,越来越多的读者 开始在文章下方 积极地留言讨论。 · 其中最受读者欢迎的是讲解 任务栈和返回栈 的这篇,截至目前已累积 37 条讨论 😉 https://xiaozhuanlan.com/topic/7812045693
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#9楼 @GetCore 感谢你的阅读和支持! 对的,有这个计划,将在 10 月中旬开源一份 基于音乐播放器 的 Jetpack 最佳实践。 · 此外,重学安卓专栏有自己配套的《重学安卓》项目,在 DatabindingLiveDataFragment 中提供了极简的 Jetpack MVVM 最佳实践。 GitHub 链接:https://github.com/KunMinX/Relearn-Android · 悄悄告诉你:如果不希望这个项目被非读者的人发现、又希望及时收到项目更新的通知,可以点击项目右上角的 “Watch” 按钮,这样就能悄咪咪地 收藏和收到推送啦。😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
原本只是提个需求,没有抱太大期望,没想到很快就在评论区收到作者的答复,并在假期内特别准备了这篇文章,在此非常感谢作者对设计的热爱和付出! · 本人从接触到从事 Android 开发已有 4 年之久,在经历过近 3 年的架构实践、琢磨 和 反复设计后,标准化的开发模式已完全确立。现如今,我独立完成一款 29 个页面、34 个 API、350 余项 细节的项目,在自动化脚本的帮助下,只需令人咂舌的 5 天时间。 · 我承认比起 工程设计,我在 视觉设计 和 交互设计 上毫无天分。我常常为 此处应摆放什么样的控件、控件应呈现什么样式 纠结不已。网上收集的飞机稿看似都可以,但我没有一个唯一正确的标准来指导、来相信、从而毫无疑问地做出正确的、自己能够笃信的 视觉设计。因而只要谈到 产品设计,我随时处于崩溃的边缘。 · 在初学 Android 时,我的目标是成为 会开发又会自主设计 的产品经理,现在看来,我还有很多要学习的东西。 · 最后,再次感谢作者的分享!
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
看到救生衣想起了第一课提到的 色彩的前进性。 小时候在溪里游泳经常看见,在夜幕降临的时候,橙色的前进性尤为突出。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @宋港 Souffle 成为产品经理是我的初衷,但我尚未看见自己具备产品思维、能协调人力资源、能把握用户的需求。 · 当前我是在朋友的介绍下,给合作方公司的小而美产品做 Android 的代工(他们有成熟的 iOS 实例)。每次看到自成一体的设计,我都不免感慨,真美,真好,我愿意给这样的软件代工。 · 和作者您一样,我出于对自身领域的热爱,而开设了自己的小专栏,希望把自己对该领域的理解,分享给同样 热爱思考、勇于行动 的后起之秀。 · 作者最后的建议也十分中肯。未来我打算继续做一个纯粹的技术负责人,去和热爱自己领域的产品、运营合作。至于设计,我多多少少也去了解一点,相信有一天我一定能掌握的 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @KunMinX 这里说的“倒灌”的情况是指,当 页面B observe 了某单例共享的 liveData 时,在下次重新进入该页面时,livedata 的旧数据会通过 observe 的 forwardPass 倒灌进来。这是 livedata 本身为了让“最新”数据能够在页面回到 start 或 resume 时恢复用的,没想到在共享作用域的情况下,这造成了不可预期的错误。所以也在想解法,看看怎么规避这种麻烦。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
补记:这个 ViewModel 的“页面共享” 特性,还是要小心使用啊。使用不当,容易造成 LiveData “倒灌” 的情况。 页面之间直接通信(比如回调的情况),这就违背了通过唯一可信源完成状态分发的理念,也即和 LiveData 的存在使命相抵触了。 所以还是要想个办法,无论如何都通过唯一可信源通知目标页面,即使在旧时候是回调的情况。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#12楼 @Skyoasis 感谢你的期待 😉 视图和动画的解析,紧随其后。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#9楼 @KunMinX 已查明原因! 本质上是一种粘性的事件,在 observe 该 LiveData 时自动推送最后一次的数据,通过 lastVersion 和 version 的比较。 然而这并不总是符合我的预期,我要的是收到 observe 后新拿到的数据。 因而解决办法是,通过 hook 去干预 lastVersion,使 observeWrapper 在实例化时,便直接为 lastVersion 赋值 version 的值。具体代码可参见这篇文章: https://blog.csdn.net/geyuecang/article/details/89028283
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
LiveData 的粘性设计有个令人头疼的 bug,在共享作用域 ViewModel 中使用 liveData,可能会造成“旧数据倒灌”的问题,具体的缘由和解决方案可见 ViewModel 一章评论区第 10 楼。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
发现 4 楼 @Darren75 同学分享的 包装类方式处理 LiveData 粘性问题,对于我这样依然托管给 ViewModel 从而维持 唯一可信源状态分发 的情况,其实更合适。😉 · 只有在纯粹使用 BUS 的情况,适合使用反射的方式。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
关于 LiveData 粘性事件,确认有更好的解法,详见 Darren75 同学在 LiveData 一章的评论区 4 楼分享的 包装类方式。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @GetCore 😄
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#6楼 @三颗猫 是的,改的。detach attach 还要移除视图和重新渲染,是有一定几率会造成转场卡顿的。 在 fragment 绘制内容轻量的情况下,可直接使用 show hide。 在 fragment 绘制内容重量(例如使用毛玻璃特效)的情况下,可考虑开启一个新的 Activity,为单个 fragment 争取到独立的 window(绘制资源)。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#20楼 @dizh 哈哈,源码设计者考虑的事情不一样啦。 LiveData 就是专门辅助 唯一可信源的事件推送。有了它,任何新手老手都可以不假思索地遵守,从而规避 90% 不可预期的错误。这就是这个设计存在的全部意义所在。 RxJava 的话,不是每个人都熟悉,更不要说自觉遵守 唯一可信源分发状态的理念了。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#4楼 @GetCore 非常感谢您的 信任、理解 和 对文章内容的肯定! 您非常有眼光,精心打磨的作品 或许会迟到,但绝不会缺席 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#42楼 @L YT ActivityStackSupervisior 的本质是 Manager。将 Activity 实例的管理分层交付给 TaskRecord、ActivityStack,是为了 维持连贯的用户体验(这些在文中都是划过重点的)。 · Manager 的设计十分常见,是一种遵循 依赖倒置原则 和 单一职责原则 的实现,在 Navigation 等框架都能见到它的身影。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#7楼 @L YT 只看文末结论的话,便错过了前文 对 前景 和 可见模式 本质的思考。 辟谣 3 是针对 “从 前景 到 可见” 的切换,以及 “从 可见 到 不可见” 的切换,这两种情况来辟谣的。 网上讹传的观点,都是来源于 2015 年出版的某本知名书籍。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
我已联系管理员 全额退款,并且拉入黑名单,永久禁止 该巨婴 订阅本专栏。 · 并且 原定于第 15 篇文章面市后 再涨价的,现在提前执行涨价,以避免非目标人群的再度混入。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#7楼 @轩志强 感谢你的喜欢 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @🌸 🌸 🌸 🌸 感谢你的期待!! 无论是写作还是开源项目,我对分享的每一个作品负责。 这个最佳实践案例,我希望设计成 我的读者一看就懂、轻松就能效仿 和 编写出 标准化的代码,所以项目代码 就和我的每一篇文章一样,也需要 反复地琢磨、长足地思考 和 调整到我个人满意为止。 到时候我一定会以各种方式通知大家,再次感谢 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#7楼 @🌸 🌸 🌸 🌸 你可以质疑我的人品,但请不要质疑我的专业性!我不仅在 标准化 视图控制 架构设计领域 有着行业领先的建树;在交互设计领域,也有深入的思考、能提供专业的见解。 · 你根本就没有在思考,不过是想 无脑地、被动地、贪婪地 吞吃别人思考过的结论。 如果你觉得 跟不上我的思维节奏,大可通过 Email 等方式 私下找我沟通退款,你有什么资格 对我的写作 指手画脚? · 我就是我,我依靠自己独立的愿景来行动。走好不送。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#10楼 @🌸 🌸 🌸 🌸 我给你一天的时间,在这里 向我 以及我的读者们 赔个不是。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
@App 小公主
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @GetCore 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @GetCore 感谢你的阅读 😂 您的这个比喻有点意思哈,曲高和寡,怕摔怕痛,说明飞得还不够高吧。那一心一意飞得再高点,直至脱离地心引力 到达外太空,也就再不会摔了 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#7楼 @王认怂 状态保存和恢复,针对的是 视图控制器重建 的情况。 你这里提到的 开发者选项设置的 不保留活动,不在状态重建的考虑范围。 此外,我在《重学安卓》项目的 test01_lifecycle_test 包下,提供了 View 状态保存和恢复的示例。 Google 默认实现的那些 View,包括 TextView 在内的,都是具备了状态保存和恢复功能。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @Will_夏 其实可以这么理解,此处提供的傻瓜式解决方案,正如同 Jetpack MVVM 之于视图控制器,是一种标准化的、面向软件工程快速开发的、以避免不可预期错误的 良好架构设计。 换言之,使用它们,可以不去手工操作内部的细节,从而规避大量因人工操作容易导致的疏忽,而大幅提升开发效率。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#45楼 @各生欢喜ᝰ 感谢你的阅读和观测 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @GetCore 并且,这也是为什么,我开设了这个付费专栏。因为相对来说,主动订阅的读者大都是 有思考、会思考、独立自主 的高质量读者。 我给目标读者 分享这些 稀有钥匙 的时候,心理负担也会小很多,我可以完全按我的视角 来分享 自恰的观点,不用担心 得不到回应、更不用担心像 日心说 哥白尼。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#5楼 @del_bug 首先,这篇文章是写给新手看的,经过我的调研,80% 的新手在遇到这种情况时,第一个想到的就是通过 数据的持久化存储。持久化未必就是专指 SQLite,像 SharePreference 这种都算。 再者,本文讨论的是 视图控制器 状态的存储和恢复,onSaveInstanceState 和onRestoreInstanceState 绝不仅仅是保存和恢复 View 的状态,也包括开发者手动保存和恢复的 视图控制器成员变量。 最后,状态管理的最佳实践是,通过 ViewModel 来托管状态。具体可参考 ViewModel 一章。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#6楼 @Skyoasis 感谢你的喜欢 😄
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @路遥90 他曾在专栏中提到,他开发的个人应用,连俄罗斯人都无法破解(按我的理解,市场上很少有直接篡改软件然后上架的情况,目前大概就是针对 幸运破解器 这种东西)。 他的专栏主要是 分享反破解、前沿消息、解答提问,目前定价 399 。 我的建议是,根据自己的实际情况 来决定是否订阅。 如果自己对 反破解 感兴趣、有一定的基础,在遇到问题时希望有人能提供一些参考的建议,那么可以考虑他负责的这个专栏。 此外他还有 以自己昵称 命名的免费 tg 群,也可以先进这个群 看看内容是不是对自己胃口。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @Angki. 感谢你的阅读和交流 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#15楼 @小暑知秋 非常感谢你的阅读和喜欢 😄
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#6楼 @德龙15 是的,DataBinding 相当于通过编译时生成 Java 代码,来曲线救国(伪“动态声明”),相信今后新推出的 基于 kotlin 的视图系统 Jetpack Compose,能更好地实现 视图的动态声明,和解决视图调用的一致性问题 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#9楼 @陈振 感谢你的阅读和测试。是的,如果耗时操作放在主线程中,是会阻塞本用于在主线程中完成的流程任务。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#4楼 @德龙15 感谢你的阅读和喜欢 😉 就喜欢你这样说的,事实上正因为付费,所以对内容也会更加负责。 读者们看到的每一篇文章,在面市之前或之后,都可能经过不下 50 次的通读和修整。 · 此外,我们刚刚开通了读者群,加微信 myatejx 我拉你进群。 最后,阅读过程中 有什么问题都可以留言,我一定知无不答。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#10楼 @鸿毅 哈哈,是的,DefaultLifecycleObserver 是 改进后的写法,更简洁(不用写注解,并且可以不去实现所有的接口方法)、在软件工程的背景下更安全。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#10楼 @KunMinX 使用 单Activity 架构,还是 多Activity 架构,根据实际情况来。目前开源的 PureMusic 中的SharedViewModel 已设计为 Application 持有,可在全应用范围内,为 视图控制器的页面通信服务。 https://github.com/KunMinX/Jetpack-MVVM-Best-Practice/blob/master/app/src/main/java/com/kunminx/puremusic/bridge/callback/SharedViewModel.java · 因为每个 Activity 持有一个 Window,申请的资源有限,单Activity 架构的多 Fragment 在转场动画时,容易存在不可预期的绘制错误,如果 fragment 中采用 毛玻璃等重量级绘制任务的话
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#16楼 @KunMinX 笔误,更精确的说法是,结党营私、指手画脚。 我的底线是 实事求是、就事论事,在阅读过程中 对技术存在疑惑 可以私下找我交流沟通,对于逾越底线的行为,绝对零容忍。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @空大 可以见我在 PureMusic 中,“最近播放” 列表基于 binding 的用法,这在点击任一 item 切换右侧的当前播放标识时 没任何问题, 但如果你将该列表的 item 改为直接在 xml 中绑定 播放标识状态的,问题就来了,你会在切歌的时候发现整个列表都在不停地闪烁、每个 item 都接连出现 播放标识的 “鬼影”。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @zhoujunn 感谢你的期待 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#10楼 @Hanks23 感谢你的阅读和喜欢 😄
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#5楼 @空大 感谢你的尝试。 刚刚我看了你的这套代码,并不是按我提到的那种情况编写 乃至复现。 文中我想表达的意思是,同样是使用 notifyDataSetChanged, 在 adapter 中通过 java 代码 binding.视图.set data,能给到正常的刷新表现, 而如果采用在 视图控制器 中使用的,直接在 xml 中绑定 data,此处 100% 必现 播放标识的 全列表 跳动(历经 500~1000ms 才停留在预期的 position)。 最后,在 综上 之前的这一小节 旨在分享 额外附赠的经验之谈,有些细节暂时没有精力去深究 实属遗憾。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#5楼 @空大 重现步骤: 1.MainViewModel 下添加一个当前 musicId 的状态。 public final ObservableField<String> currentMusicId = new ObservableField<>(); 2.adapter 的布局中注入: <variable name="vm" type="com.kunminx.puremusic.bridge.status.MainViewModel" /> <variable name="music" type="com.kunminx.puremusic.data.bean.TestAlbum.TestMusic" /> 3.并为 iv_play_status 绑定 visible 状态: <net.steamcrafted.materialiconlib.MaterialIconView android:id="@+id/iv_play_status" visible="@{vm.currentMusicId.equals(music.musicId)}" 4.MainFragment 中,adapter 的 onSimpleBindItem 回调中注入: binding.setMusic(item); binding.setVm(mMainViewModel); binding.setClick(mClickProxy); 并注释回调中除 click 外的其他逻辑。 5.在切换音乐的回调中 set musicId: PlayerManager.getInstance().getChangeMusicLiveData().observe(this, changeMusic -> { mMainViewModel.currentMusicId.set(changeMusic.getMusicId()); mAdapter.notifyDataSetChanged(); });
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#7楼 @KunMinX 笔误,binding.setClick(mClickProxy); 这行多余。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
刚刚在思考,如果将来计算机芯片可以集成得更小,且存在一种可发出强光而随时可以充电的设备时,或许投影手环可以替代手机成为下一代智能移动设备。 · 目前市面上存在的类似产品如 “投影键盘 + 触摸板”,它的坐标系设计 同样也符合文中提到的 左上角为原点,x 向右、y 轴向下为正方向,如此它根据对手指和投影仪的距离的测量,就能精确地判断出当前手指的移动方向、速度和距离,和在触摸屏、笔记本触摸板上的输入并无二致。 · 以下是 投影键盘 的说明书: https://images.xiaozhuanlan.com/photo/2019/b501b3f71c5b0d69973b954f3c091bb1.jpg
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#12楼 @luan823230 感谢你的阅读 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#10楼 @luan823230 为了更好的拓展性,因为并不能确保 项目中的每个 Activity 都适合使用 DataBinding。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @macproz 任何技术都绝非凭空存在,每一项技术都是为特定场景下解决特定问题而生。把这个背景状况摸透,就能笃信地使用该技术。 dataBinding 是为了在 java 语境的软件工程下解决视图调用的一致性问题,若非如此,他就不是这么的“非他不可”,例如在 kotlin 语境下它随时可以被换下。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#11楼 @KunMinX 并且,这个项目的目标 主要是为了方便读者了解 某一项技术的存在缘由,因而将 主角 摆在台面,会更容易理解。 如果自己 已对某项技术 的作用和使用 十分熟悉,那不妨 在自己的项目中 考虑进一步地封装。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
在我忙着为读者解决困扰的同时,就是会有那么一两颗老鼠屎 在背地里议论是非。 所作所为 和澳大利亚选手霍顿 没什么两样。 不是坏到骨子里的人,做不出这样的事来。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#12楼 @KunMinX 我大概能猜到,不生成代码布局,是因为第三方控件的自定义属性,DataBinding 不一定知道。所以这个单靠 android 目前的机制是办不到了,需要第三方民间团体去开源和维护一个民间 binding 库。。 一不小心遐想了这么多 😂
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
文章开头提到的优秀用户体验之:“用尽可能小的 大拇指的划动幅度,来实现本不大可能、但心中预期的结果 和 大动作”,有没有读者对这句话高度留意呢? · 刚刚我在操作 MacBook Pro 2015 的触摸板浏览网页时,再次惊觉,原来不止是触摸屏,就是连触摸板,也是如此设计。 · 我不知道 MacBook 的这套手势 最早是什么时候被设计的,但它们的存在显然远早于 Windows 笔记本,且,这样的操作真的太舒服了 —— 在没有鼠标的情况下,当我浏览网页想要返回上一页时,我无须移动光标到返回按钮再点击,我只需双指向右滑动 —— 这样一种尽可能小的 划动幅度,就能实现的 返回上一页的大动作,是不是很厉害?!
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#12楼 @officeyuli 非常感谢你的阅读和喜欢 😄 目前我们在生产环境中的做法是,将业务逻辑全部压在 DataRepository 层去处理,所以对于数据请求响应的情况,ViewModel 十分轻盈,只是作为一个中间桥梁。 onClear 方法主要是用于 手动置空 “ViewModel 为视图控制器托管的 重量级数据”,使得在 ViewModel 跟随 视图控制器 被销毁时,内存 100% 地有机会得到释放。 这些重量级数据并非用于 业务逻辑请求响应的数据,所以对它们的置空,不会影响到 数据层的业务计算。 · 对于 ViewModel 持有 factory 去依赖 数据层组件的情况,其实和我在文中提到的业务放在数据层写并不冲突,通常来说 我们不去手动清空 这些业务数据即可。 · 此外,由于 数据层最终是通过 liveData 向 UI 层回调数据,在视图控制器离开前景的情况下,数据不会送达 observe 回调,本身可以避免 空指针等“生命周期安全”问题。 · 并且,在 视图控制器 离开前景时,如希望自动叫停 不合时宜的业务计算,可以考虑为 ViewModel 添加 LifecycleObserver 支持,使得可通过 ViewModel 间接向 数据层发送 生命周期状态。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#13楼 @KunMinX 补充一下,这里提到的 ViewModel “业务数据”,即 可以跨层完成 请求响应的 liveData。liveData 其本身不需要被置空。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
强迫症犯了 …… 上一个评论末尾的 “最受欢迎” 替换为 “最受关注”。 其实我的愿望是,读者们在复盘视图控制器的知识后,能多多关注一下 Jetpack MVVM 的内容。 · 架构组件就是为 标准化开发模式 的确立 而存在,它们能够让 软件开发 快速、稳定、不产生预期外的错误 😉 · 也正是因为架构组件 确立的标准化开发模式,使得 50% 的开发工作 可以完全交给 自动化代码生成工具,从而避免手工编程 带来的各种错误。 · 其实但凡经历过 在 3 周的业余时间内,独立负责 29个页面、34个API、350余个细节的高强度软件开发,就明白上述我说的意思了。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#21楼 @officeyuli 嗯嗯。每一项技术都是为了 在特定场景下 解决特定问题 而存在,所以最初封装了 AsyncTask,一定是为了解决什么问题。现如今将其替换,一定是在解决某类问题时,不可替代性不够充分、且存在隐患。 未来当我把 任务处理大家族 的背景想明白了,我就会为 WorkManager 写上一篇, 感谢你的期待 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#20楼 @今日营业中 感谢你的阅读和思考 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#17楼 @KunMinX 补充一下 😂 为 RemoteManager 和 LocalManager 分别编写接口,是为了方便替换具体的实现,例如 LocalManager 其内部早些时候用 greenDao 实现,现在改为 Room,则从 DataRepository 角度来看,它调用的是 ILocalManager 接口,所以 DataRepository 中业务方法一行不用改,改的只是赋予给接口的实现类。 而 DataRepository 本身只有这么一个,且是单例,所以 DataRepository 本身我没有为它编写接口,是直接以单例的方式供外界访问。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#19楼 @officeyuli 感谢你的交流 😉 事后我专门去了解了一下两岸的计算机术语,因为以后还可能有更多 和你一样优秀的台湾读者 参与到 Jetpack MVVM 的交流。 术语本身并不关乎正确,是思想的相通 抹平了一切阻碍。 非常享受我们在架构上的交流~
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#23楼 @GetCore 嗯嗯,是的,出于 软件工程安全 的考虑,Google 已确立 Jetpack 作为标准化架构组件。 UseCase 的基类 晚些时候我会上传到 最佳实践项目的 app 包下,敬请留意~
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#24楼 @location ??😂 没明白具体是指哪个话题
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#11楼 @沧海之舟 可以简单地认为,当 app 当下从前景离开,且不处于可见 或 服务模式时,即进入背景模式。 · 辟谣三特指,当视图控制器 resume 时,它变为前景,而当它离开前景进入 pause,或最终回到 restart 到 start 时,这两个节点都是可见。详见文中 安卓桌面系统的 插图和讲解
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#13楼 @Walker47 复原的时候 通常是用户从 “最近访问列表” 中选择的那程序,但程序因为被杀,而点开时的表现,就和从 launcher 重新打开一个 app 是一样的。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#47楼 @Fly_with24🏀 太棒了!非常感谢你的细心和执着!😉 你在笔记中对具体数据的记录、对现象原因的猜测和测试,以及文末给出的 纯净的实验方案 十分精彩!
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
2019.11.25 Note: 其实 Activity 和 Fragment 的关系,正如 从操作系统角度来讲 进程和线程的关系 —— 进程是资源的持有者,因而在创建、撤销、切换时,需要系统付出较大的时空开销。 · 同理,Activity 是 Window 等资源的持有者,因而在创建、销毁、切换时,需要付出较大的时空开销。也即 Fragment 的创建、销毁、切换,开销能控制得更小。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#26楼 @PengHaiZhuo 哈哈 感谢你的阅读和喜欢 😄
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#15楼 @obico 感谢你的阅读 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#13楼 @obico 感谢你的期待 😉《重学安卓》项目暂无拷贝到码云的计划。 晚些时候 我会在码云上传《Jetpack MVVM 最佳实践》的项目源码。 这里介绍个妙招:在 bash 中 clone 时,可以加个 --depth 1 的前缀,这样只下载最后一次的提交,项目大小大幅减小。 例如 git clone --depth 1 https://xxxxx.git
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#13楼 @obico https://gitee.com/kunminx/Jetpack-MVVM-Best-Practice
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2019.12.12: 使用 ObservableField 时要注意,当对基本数据类型(例如 boolean、int 等)进行 set 操作时,会存在 “因 value 相同,而不予 notify 重绘” 的情况, · 例如通过 BindingAdapter 去 openDrawer 的情况,第一次给它 true,手动关掉 drawer,那么第二次再给 true 去通知就“不听话了”。对于这种情况,可改用 LiveData 的方式来绑定,具体可见 PureMusic 最佳实践 的 MainViewModel 页面。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
或者干脆 stateViewModel 中全部采用 LiveData 来保存状态。 当初使用 ObservableField 是考虑到它足够简单、不会有预期外的状况存在,没想到在某些情况下,它 出于防抖的设计 反倒坑了自己一把。 总之记住 ObservableField 的这个 防抖的特点,在特定场景下一定大有用处。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
补充:防抖可以避免重复刷新 以减少不必要的性能开销。所以大部分时候优先考虑 ObservableField 来绑定视图状态
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
刚刚在 b站收看了 Compose 在构建 Compose 视图系统时的心路历程,感觉不错,视频中对他们遇到的问题和思考都有详细的介绍,感兴趣的读者不妨在 b站 查看: https://www.bilibili.com/video/av76479221?from=search&seid=6831083488636897797
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2019.12.14: DataBinding 是在 “Java 语言构建的 Android 项目” 这个背景下,用于解决 视图调用一致性问题的 不可替代。 · 到了 kotlin 语境下,这个功能就不再那么“非他不可”,毕竟 kotlin 一个问号就能解决 Null 安全的问题。 · 同时,新出现的 ViewBinding 库,它的存在主要是为了解决 视图实例化的 类型安全问题,也即它是专门取代 findViewById 用的,和 DataBinding 在 “通过 binding 来调用视图” 这个用法上有点相似,但二者毫不相关(因目标不同 而有着本质的区别),甚至实现方式也截然不同(DataBinding 是编译时生成代码,ViewBinding 不是)。 · DataBinding 是软件工程安全的,但因为编译时生成代码的缘故,从反破解的角度来说又是不安全的。所以要根据实际的情况选择合适的库。对于不在意破解的情况(例如基于 Java 的开源项目),可以考虑 DataBinding,而对于商业项目,不建议使用 DataBinding,而考虑 kotlin + ViewBinding 来解决软件工程背景下的 Null 安全和 类型安全的问题。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#5楼 @GetCore 感谢你的阅读和喜欢 😄
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @GetCore 是的哈。我也觉得,要不是 即时预览 的缘故,Android 也不会考虑 xml 的方式另外写一堆文件,如你说的,这些 selector,shape,menu,也是因 layout 而连带地作为本不该做的资源(因为本身复用性很低)。 xml 声明式解决了即时预览的问题,却造成了视图引用的割裂。 所以 compose 是个很好的契机,期待能够早日投入生产用。😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#4楼 @xiaoyu233 哈哈,懂。之前有个中大型项目,常常因为细节上 比如 宽度、边距和圆角等 细微差异,而存在数个 shape 资源,然后整个项目下来一共 100 来个 shape,不便于查阅、查找和管理。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#5楼 @KunMinX 对 shape、selector 上头的读者,可以试试这个库: https://github.com/JiangW01/Sample01 目前除了编码时需要自行脑补外,复用和管理查阅的问题基本得到解决了。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#6楼 @KunMinX 补充:此处是针对例如 TabLayout indicator 这种“控件内元素” 的shape 解决方案。 如果是控件本身的背景,在之前 DataBinding 篇有介绍 noDrawable 库的使用,在《最佳实践》项目中也存在广泛的应用,感兴趣的读者可自行前往查阅。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @xiaoyu233 嗯嗯,我们共同期待 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2019.12.23: 在已确立 Jetpack MVVM 架构的项目中,如有个别业务需要生命周期管理,可以考虑额外安排一个 UseCase,夹在 ViewModel 和 DataRepository 之间,在 UseCase 中处理业务数据,并为这个 UseCase 单独赋能 DefaultLifecycleObserver,使能在其内部完成及时叫停业务等操作。 · 具体可在《最佳实践》项目中搜索 TestUseCase 类的应用。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @GetCore 优秀!总结的太好了。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#14楼 @Walker47 从 用户体验产品设计 的角度来看,官方封装的 navigation - navigationView 联动组件 是脱离实际且不恰当的设计。建议在实际生产中,单独使用 navigation。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#14楼 @Aiden9 12.26 晚,微信公众号发生大面积瘫痪,具体等待微信官方的修复...
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2019.12.30: 注意 如使用 lambda ,liveData 的 lambda 回调中不可为空,不然会出现 Cannot add the same observer with different lifecycles 的现象, · 详见:https://stackoverflow.com/questions/47025233/android-lifecycle-library-cannot-add-the-same-observer-with-different-lifecycle
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
太棒了!就读于 美国卡内基梅隆大学 的一名华裔学生 发明了 我在二楼提到的可触摸式投影设备!😉 https://v.douyin.com/qLMyYr/
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
近来有不少读者私下询问 MVP 和 MVVM 的区别。这里我分享一下自己的理解 😉: · 简单来说,MVP 是基于适配器模式的实现,它是在 MVC 模式泛滥的背景下,为了遵循 依赖倒置原则 以便能够随时替换 V 和 M 的实现。 · 也即,本质上它并没有解决代码耦合的问题(从前 UI 逻辑在 视图控制器中写,现在搬到了 Presenter 中写而已),它的存在只是为了 面向接口编程 以实现依赖倒置。抛开 MVC 的背景,就无法谈论 MVP 存在的意义。 · MVVM 的本质是数据驱动编程,它从本质上解决了代码耦合、实现了关注点分离:让 ViewModel 作为中间桥梁 去通知数据层处理数据业务,并将结果回调给 UI 层处理 UI 逻辑。 · Jetpack MVVM 是在 MVVM 模式的基础上 加入了 软件工程安全 和 用户体验优化 的设计。 · 例如 Jetpack LifeCycle 能在背后妥善处理 视图控制器生命周期的 Null 安全问题,而 Jetpack ViewModel 能分治视图状态的托管,使得 UI 发生重建时 无需重新向后台请求数据,这大幅节省了开销,让重建时的视图加载较以往 更快和更省电。 · 综上,MVP 和 MVVM 二者之间没有任何关系。MVP 是对 MVC 的升级,而 MVVM 是现代化软件开发模式的范例。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
近来有不少读者私下询问 MVP 和 MVVM 的区别。这里我分享一下自己的理解 😉: · 简单来说,MVP 是基于适配器模式的实现,它是在 MVC 模式泛滥的背景下,为了遵循 依赖倒置原则 以便能够随时替换 V 和 M 的实现。 · 也即,本质上它并没有解决代码耦合的问题(从前 UI 逻辑在 视图控制器中写,现在搬到了 Presenter 中写而已),它的存在只是为了 面向接口编程 以实现依赖倒置。抛开 MVC 的背景,就无法谈论 MVP 存在的意义。 · MVVM 的本质是数据驱动编程,它从本质上解决了代码耦合、实现了关注点分离:让 ViewModel 作为中间桥梁 去通知数据层处理数据业务,并将结果回调给 UI 层处理 UI 逻辑。 · Jetpack MVVM 是在 MVVM 模式的基础上 加入了 软件工程安全 和 用户体验优化 的设计。 · 例如 Jetpack LifeCycle 能在背后妥善处理 视图控制器生命周期的 Null 安全问题,而 Jetpack ViewModel 能分治视图状态的托管,使得 UI 发生重建时 无需重新向后台请求数据,这大幅节省了开销,让重建时的视图加载较以往 更快和更省电。 · 综上,MVP 和 MVVM 二者之间没有任何关系。MVP 是对 MVC 的升级,而 MVVM 是现代化软件开发模式的范例。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
文章开头提到的 “火箭发射中心”,其实是一个隐喻,想表达的核心内涵主要是: 学习是一个幂率增长的过程,而不是线性增长的过程。 也即,最初打地基的时候可能会花费很长一段时间,而一旦关键的地基确立下来以后,后续的学习效率会是指数倍地增长 —— 许多后置的知识 都能通过已确立的前置知识 轻松推导出,与 “凭感觉” 相比就好似 “降维打击”。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#19楼 @Nebula 是的,可以这么理解,严谨地说,DataRepository 是领域层和数据层的总和,也即它是专门管理业务逻辑的区域。UI 层向 DataRepository 请求数据,最终再由 DataRepository 内部通过 liveData 来响应结果给 UI。UI 和 DataRepository 互不干涉内政(最小知道原则)。 · 是的,LiveData 包含了生命周期安全的处理,当订阅者页面不处于被激活状态时,liveData 可以不对其输送回调数据,以避免 null 安全等事故。 · 建议像持有 liveData 一样持有 ObservableField,而不是将整个 ViewModel 继承 Observable,这样可以更清晰直观地对单个状态进行增删改。 在页面状态管理的场景下,ObservableField 的特点是防抖,可以减少不必要的状态刷新,LiveData 的特点是页面重建时会倒灌最后一次状态。所以请分别记住他俩的特点,在合适的场景下做出最佳选择,具体可参考《DataBinding 篇》的评论区。 · 是的,任何工具,我的建议是抓住本质去理解:它是哪个场景下解决哪个问题的不可替代,那么那就是它的本质。DataBinding 主要就是为了解决java 工程下 视图调用的一致性问题,如此理解,就能点到为止地将其用在状态绑定,而不在 xml 中写逻辑,从而避免很多不必要的错误。 · 对属性(包括 listener),属于 BindingAdapter 范畴,可酌情使用。它是 android 布局与视图控制器被割裂两地的一种妥协,以后 compose 成熟了,我们将不再需要这样的设计。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#19楼 @JOE39 感谢你的阅读 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#22楼 @JOE39 感谢你的阅读 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
最近有不少厂商在谈论 5G 时代的云游戏, 大体的思路是,游戏的运行放在服务端,客户端负责发送指令和接收实时音视频, 我对 5G 的力量不太了解,不过如果 5G 真能保证如此低的延迟(实时传输指令 和 实时传输高画质音视频),并且覆盖度足够广泛,那么未来的手机就真的能做到,只须在本地保留一丁点的算力 来维持操作系统的正常运行,就能执行任何 算力要求高 的应用程序。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @KunMinX 也不能说一丁点 … 至少要能保证 音视频解码 的流畅性。 所以未来的手机 可能对算力要求不高、导致硬件成本大幅降低 —— 产业的重心从贩卖硬件 转移到贩卖服务 —— 也许未来玩手机游戏 要按专用流量收费。 与此同时,如果说,移动开发在未来几年 有什么热门的领域的话,那我想就是音视频。 因为直到 2020 年年初的现在,音视频在安卓平台 由于机型、指令集 碎片化等因素,仍没有 标准化的、透明的 解决方案。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @GetCore 兄弟你也太快了吧 😂 沙发已被抢,前排还有少量板凳和摇椅,抢前排的兄弟们抓紧了 😝
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#3楼 @Petterp 嗯嗯,加油!
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
关于文末提到的 排版渲染 16ms 工作总和 的具体内幕,这里网上找到了一张十分详细的工序叠加图,感兴趣的小伙伴可以参考下: https://images.xiaozhuanlan.com/photo/2020/b5e733a0eae19b13dd3620c6dabce7a1.png
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#20楼 @周明长 是的,DataBinding 编译时生成的文件,没有被混淆。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
3 年前 Facebook 开源了一款名叫 Litho 的声明式 UI 框架,和今天的 Jetpack Compose 一样是借鉴了 elm 的设计。 它能够 提前 异步 执行 测量和布局 工作,以及 用 drawable 的组合 代替 view 来排版, 也即 虽然它不能减少渲染图层 以避免过度绘制,但它能够减少排版工作的耗时,从而尽可能减少排版渲染整体的耗时。 感兴趣的小伙伴可以参考这篇文章: https://juejin.im/post/5c8b0da0f265da2d864b8258
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#21楼 @KunMinX 此外,由于“需要在编译时生成文件”,以及对不同屏幕适配下的 layout 文件的 variable 做合并,而导致 布局文件不应被 mess 等混淆工具混淆,从而再一次导致线索的暴露。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note:2020.2.10 AndroidX 的 Fragment、AppCompatActivity、ViewModel 源码已发生变化。 如《ViewModel 篇》中提到的 “fragment 的 retain 被废弃”,现如今 AndroidX 已单独引入 SaveState 包来解决 重建时 状态恢复的问题。 此举是为了让 fragment 和 Activity 的 public 生命周期设计趋近(例如未来 attach、detach 方法可能被 private),以减少学习成本和维护成本。 · 如此,未来 fragment 和 Activity 的本质区别,可能主要体现在 Activity 的创建伴随着 window 的创建、能够独立申请到一块 surface,而 fragment 完全依赖于 宿主 的 surface 资源,适用于绘制任务轻量的场景,在某些重量级渲染场景的页面转场时 则更大概率地发生卡顿的现象。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#17楼 @。。99 每个进程都是独立存在的,而且如果是系统主动去杀进程,也是依据进程的模式来判断是否可杀。所以像推送的 channel 进程,理论上只要它有开启组件,让进程处于前景或可见模式,就不会被杀,不管 app 主进程本身是否被杀。 现如今国内厂商,为了省电,而对安卓深度定制和对规则进行篡改,这使得理论上有机会驻留的进程,可能也会被消灭。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
关于 “数据驱动”,这里再次小小地强调一下: 数据驱动的含义是,通过改变状态数据,来驱动视图树中绑定了相应状态数据的控件重新发生绘制。 也即 “数据驱动” 的边界仅限于 状态数据的变化,和视图的重新绘制。 数据驱动 并不关心数据的来源和获取方式。切莫将 “数据从后台推送来” 这个过程当成数据驱动。 · 目前数据驱动的实现方式主要有 DataBinding 和 elm 这两种,后者是我们经常在 react、flutter 上看到的 setState 的实现,将来的 Jetpack compose 也是采用类似的机制。 · 数据驱动的好处是,视图的生死存亡和绘制工作,全部交给框架来自动处理,避免直接接触视图实例,从而避免 null 安全等问题。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#21楼 @OldJii 哈哈 感谢你的阅读 😄
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
关于 “数据驱动”,这里再次小小地强调一下: 数据驱动的含义是,通过改变状态数据,来驱动视图树中绑定了相应状态数据的控件重新发生绘制。 也即 “数据驱动” 的边界仅限于 状态数据的变化,和视图的重新绘制。 数据驱动 并不关心数据的来源和获取方式。切莫将 “数据从后台推送来” 这个过程当成数据驱动。 · 目前数据驱动的实现方式主要有 DataBinding 和 elm 这两种,后者是我们经常在 react、flutter 上看到的 setState 的实现,将来的 Jetpack compose 也是采用类似的机制。 · 数据驱动的好处是,视图的生死存亡和绘制工作,全部交给框架来自动处理,避免直接接触视图实例,从而避免 null 安全等问题。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
关于 “数据驱动”,这里再次小小地强调一下: 数据驱动的含义是,通过改变状态数据,来驱动视图树中绑定了相应状态数据的控件重新发生绘制。 也即 “数据驱动” 的边界仅限于 状态数据的变化,和视图的重新绘制。 数据驱动 并不关心数据的来源和获取方式。切莫将 “数据从后台推送来” 这个过程当成数据驱动。 · 目前数据驱动的实现方式主要有 DataBinding 和 elm 这两种,后者是我们经常在 react、flutter 上看到的 setState 的实现,将来的 Jetpack compose 也是采用类似的机制。 · 数据驱动的好处是,视图的生死存亡和绘制工作,全部交给框架来自动处理,避免直接接触视图实例,从而避免 null 安全等问题。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#24楼 @Fly_with24🏀 感谢分享~ 是的,并不冲突,因为实现原理不同。DataBinding 主要是为了数据驱动,通过分离 视图实例 和 数据实例,来规避 null 安全问题。ViewBinding 主要是为了代替 findViewById,来简化视图的实例化和规避实例化时的类型安全问题。 我个人还是期待 Flutter 或 Jetpack Compose 的成熟,因为它是 “真 · 数据驱动”,通过函数式编程,让改变只能通过唯一的入口进入和内部消化,从而根本性地杜绝 视图实例 null 安全的问题发生。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note:2020.2.10 AndroidX 的 Fragment、AppCompatActivity、ViewModel 源码已发生变化。 如文中提到的 “fragment 的 retain 被废弃”,现如今 AndroidX 已单独引入 SaveState 包来解决 重建时 状态恢复的问题。 此举是为了让 fragment 和 Activity 的 public 生命周期设计趋近(例如未来 attach、detach 方法可能被 private),以减少学习成本和维护成本。 · 如此,未来 fragment 和 Activity 的本质区别,可能主要体现在 Activity 的创建伴随着 window 的创建、能够独立申请到一块 surface,而 fragment 完全依赖于 宿主 的 surface 资源,适用于绘制任务轻量的场景,在某些重量级渲染场景的页面转场时 则更大概率地发生卡顿的现象。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2020.2.27: 看到群里有读者受 人云亦云网文的误导,得出 “页面 onPause 时不该收到 LiveData 消息” 的结论,特此辟谣:只有 onResume 和 onPause 是介于 STARTED、RESUMED 状态之间,也即只有这两个生命周期节点能够收到 LiveData 的推送。 详见 LifeCycle 篇文末补充。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#25楼 @苏打先生 二者没有直接的关系。前者是图形化窗口的生命周期节点,后者是管理生命周期安全的状态。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#1楼 @Will_夏 感谢你的阅读。 · 关于第一个问题,我在上一篇文章的评论区中解释到:当 App 中存在活跃着的组件,并且该组件使得进程模式处于可见或前景的情况时,进程无论如何不会被系统回收,除非用户手动去设置中将进程强制结束。 通常当App 中存在获得焦点的 Activity 时,进程模式处于前景模式,否则失去焦点的 Activity,就如前台服务一样,是可见模式。 · 如果跳转到第二个Activity,一直不回第一个,第一个就一直处于 stoped 的状态,等待第二个Activity 出栈时,它再 restart - start - resume。 在等待期间,它一直处于内存当中,默认状况下不会被回收,除非某些 Activity 设置了 flag(详见《任务和返回栈》篇),让任务中的其他 Activity 被出栈和清理。(对于这种情况,被清理的 Activity 是得不到恢复的,是直接被清理走了,与被系统回收的概念和目标物 皆不同。系统回收的目的是为了回收一些优先级不高的进程,来释放资源) · 重建的概念只存在于视图控制器中,本文中介绍到,关于“为何存在重建的设计”。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#36楼 @qh 非常感谢你的 细心阅读、思考 和 指出问题!已及时修正 😉 : · taskAffinity 能够指定 任务名。standard 正常情况下 是不会创建一个新的 以 taskAffinity 为名的任务,然后存在其中。 standard 只有在 allowTaskreParenting 为 true 的时候,会依据 taskAffinity 来发生转移。allowTaskreParenting 默认情况下也都为 false 的。 allowTaskreParenting 和 singleTask 的情况是互斥的,所以 allowTaskreParenting true 对 singleTask 无效。 · 此外,如果对 allowTaskreParenting 的使用场景(例如 DeepLink )感兴趣的话,这里我为你找到了这篇: https://segmentfault.com/p/1210000009702755/read
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @OldJii 此外,你对 “显式、隐式 优先级,或排他性” 这个问题 提的很好,我先记录下。 并且,《重学安卓》github 项目有 IntentTestActivity ,可供修改代码、运行,以加深印象。😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#50楼 @OldJii 👍👍👍
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2020.2.17: getViewLifeCycleOwner 是 2020 年新增的特性, 主要是为了解决 getView() 的生命长度 比 fragment 短(仅存活于 onCreateView 之后和 onDestroyView 之前), 导致某些时候 fragment 其他成员还活着,但 getView() 为 null 的 生命周期安全问题, 也即,在 fragment 的场景下,请使用 getViewLifeCycleOwner 来作为 liveData 的观察者。 也即,Activity 不用改变。 具体可参考后续的 Jetpack 系列和《最佳实践》项目。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @OldJii 感谢你的阅读~ 本身是定义了,只存在这两种区别,分别用于 明确的目标 和 条件匹配 这两种。 文中也提供了官网的链接,其中对此也有明确的表述。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#16楼 @Fly_with24🏀 太棒了!感谢你的阅读、思考、源码确认 和 手动换行!👍
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#23楼 @IrvingTc 感谢你的阅读和喜欢 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#7楼 @xiaoyu233 听到这个消息我很高兴,感谢你的阅读和喜欢 😄
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#22楼 @布鲁兹老爷 感谢你的期待。专栏内容走心不走量,每天都有新的读者加入,所以采取宁缺毋滥的方式,方便读者都能安心地把每一篇都看完。 另外本专栏除了专栏本身,还有读者群,可通过专栏简介提供的联系方式加入,群里常有小伙伴互通有无(比如分享各种工具 和 讨论适配等问题),期待你的加入 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2020.2.27: 看到群里有读者受 人云亦云网文的误导,得出 “页面 onPause 时不该收到 LiveData 消息” 的结论,特此辟谣:只有 onResume 和 onPause 是介于 STARTED、RESUMED 状态之间,也即只有这两个生命周期节点能够收到 LiveData 的推送。 详见 LifeCycle 篇文末补充。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2020.2.27: 看到群里有读者受 人云亦云网文的误导,得出 “页面 onPause 时不该收到 LiveData 消息” 的结论,特此辟谣:只有 onResume 和 onPause 是介于 STARTED、RESUMED 状态之间,也即只有这两个生命周期节点能够收到 LiveData 的推送。 详见 LifeCycle 篇文末补充。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#8楼 @OldJii 目前可以确定的是,一旦 Intent 中有给出明确的组件包名路径,就是显式声明,不管有没有添加其他条件。并且,只要没有这明确的组件包名路径,并且有条件项,就是隐式。 显式且有条件项的,主要是当做配置来使用,在组件启动时根据配置来决定做哪些工作。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @GetCore 感谢你的阅读和喜欢!🌚😄 事实上,《重学安卓》的每一篇文章 在发表后的一到两天里,都历经数十次的通读、反复校对和修改。🧐 我的目标是用 精确而简单的话语 将原本看似混沌而复杂的事情 给讲明白、让读者们能够无痛地理解,唯恐读者在阅读新文章时 因细节含糊或逻辑不顺 而产生困扰,🙉 因此会反复通读和调整行文细节,确保读者在阅读新文章时能获得 酣畅淋漓的阅读体验。 · 读者们通常可在 每篇文章发布的一到两天后 得到最终稳定版!😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
有读者在后台留言,说 “基础数据支撑” 的话,有没有什么推荐的学习材料。 · 答案是肯定的。作为安卓开发,我们没有必要了解那么深的理论知识,但一些科普性的应用型知识还是要掌握的,了解这些,有助于我们随时随地掌握状况。 · 当然,学习也要给自己定个目标,比如在学习完后,要读懂最近华为开源的方舟编译器 —— 它到底做了哪些优化、为什么算是优化。 · 以下是推荐书籍: 《程序是怎样跑起来的》、《程序员的自我修养:链接、装载和库》、《网络是怎样连接的》
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#18楼 @Fly_with24🏀 commitNow 是 API24 加入的,所以可以当做是对 commit 的一个补充, 网文提到 “使用 commitNow 不能 addToBackStack”, 所以或许可以理解为 “添加 根碎片 时,通过 commitNow add,而不是 commit replace,会更快”,具体可修改《重学安卓》项目来验证。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#4楼 @大松 感谢你的阅读和喜欢 😉 可多多关注 Jetpack MVVM 系列文章及其评论区,祝你早日找到答案 ~
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#17楼 @OldJii 感谢你的细心阅读,已修正 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#22楼 @Fly_with24🏀 刚刚翻了一下源码,SDK API29 的 FragmentManager 已作废,这意味着 Google 已确立新的 savedState 方式来管理 Fragment 的状态,不仅适用于页面切换、页面重建,也包含 ViewPager2 等场景。 · 对于 replace 页面切换的场景,新旧版都是走 onDestroyView 为止,在此之前会保存视图的状态,但 Fragment 成员默认不作处理,需要开发者手动保存, · 具体可见 OneTestFragment 最新改造的测试案例(很多时候都可以对 重学安卓 项目直接 “改吧改吧”,验证一些现象或想法,如果觉得有益,可 pull request,我看到了会予以合并)
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#25楼 @Flynn😑 感谢阅读。本文的目标是从零到一地方便读者确立对 Activity 边界的认识,ViewRoot 的位置和地位在视图系统章节的关系族谱篇介绍。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2020.3.13: TODO 2020 年起,fragment 就算 replace,返回后仍然能恢复之前的状态, · 虽说 fragment 存在的初衷 是为了更加轻量、快速,而做了 创建和销毁视图。 然而目前 google 的意图是,让 Activity 和 fragment 的 api 和行为更加趋同。 所以未来会将许多功能抽取出来,单独维护、分别为 Activity 和 fragment 共享。 · 所以比如 Activity 被设计为,跳转返回后,状态还在, 那么为了降低复杂性,fragment 也打算走类似的路线(即使页面跳转时视图被销毁,也要让页面回退时状态被恢复), 具体可通过 重学安卓 配套项目 OneTestFragment 类 已搭建好的实验环境验证。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
(上述指的是 ViewModel 的 savedState 模块)
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2020.3.13: TODO 2020 年起,fragment 就算 replace,返回后仍然能恢复之前的状态, · 虽说 fragment 存在的初衷 是为了更加轻量、快速,而做了 创建和销毁视图。 然而目前 google 的意图是,让 Activity 和 fragment 的 api 和行为更加趋同。 所以未来会将许多功能抽取出来,单独维护、分别为 Activity 和 fragment 共享。 · 所以比如 Activity 被设计为,跳转返回后,状态还在, 那么为了降低复杂性,fragment 也打算走类似的路线(即使页面跳转时视图被销毁,也要让页面回退时状态被恢复), 具体可通过 重学安卓 配套项目 OneTestFragment 类 已搭建好的实验环境验证。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2020.3.13: TODO 2020 年起,fragment 就算 replace,返回后仍然能恢复之前的状态, · 虽说 fragment 存在的初衷 是为了更加轻量、快速,而做了 创建和销毁视图。 然而目前 google 的意图是,让 Activity 和 fragment 的 api 和行为更加趋同。 所以未来会将许多功能抽取出来,单独维护、分别为 Activity 和 fragment 共享(比如 ViewModel 的 savedState 模块)。 · 所以比如 Activity 被设计为,跳转返回后,状态还在, 那么为了降低复杂性,fragment 也打算走类似的路线(即使页面跳转时视图被销毁,也要让页面回退时状态被恢复), 具体可通过 重学安卓 配套项目 OneTestFragment 类 已搭建好的实验环境验证。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
(上述指的是 ViewModel 的 savedState 模块)
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#19楼 @十五9 听到这个消息我很高兴,感谢你的阅读和喜欢 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
Note 2020.3.13: TODO 2020 年起,fragment 就算 replace,返回后仍然能恢复之前的状态, · 虽说 fragment 存在的初衷 是为了更加轻量、快速,而做了 创建和销毁视图。 然而目前 google 的意图是,让 Activity 和 fragment 的 api 和行为更加趋同。 所以未来会将许多功能抽取出来,单独维护、分别为 Activity 和 fragment 共享(比如 ViewModel 的 savedState 模块)。 · 所以比如 Activity 被设计为,跳转返回后,状态还在, 那么为了降低复杂性,fragment 也打算走类似的路线(即使页面跳转时视图被销毁,也要让页面回退时状态被恢复), 具体可通过 重学安卓 配套项目 OneTestFragment 类 已搭建好的实验环境验证。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#19楼 @十五9 因为自己也是从新手开始走过来的,所以特别能够理解,在知识经验尚不丰厚的情况下,如果一上来就被 “怎么做、怎么做” 甩一脸,是多么得痛苦和迷茫。 · 每个领域的技术,都绝非一蹴而就,当下接触到的这个那个技术,一定是有历史渊源。所以唯有首先追根溯源,找到源头的源头,才能有机会 无痛理解 当下技术的存在缘由、以及精准地找到它的正确打开方式,从而 有的放矢 地用在恰当的地方。 · 因而 本着 "授人以鱼 不如授人以渔" 的度人之心,本专栏的目标,不仅仅是对知识点本身的解惑,更多意义上是提供 "深度思考" 的示范,让读者在无痛理解知识点的同时,能够 确立有效的 掌握和认知事物的 思维方式,从而未来能够在 独立思考的帮助下,摆脱焦虑和迷茫,轻松应对更多领域的学习和挑战。😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#23楼 @KunMinX 上一条留言有误,感谢 @Fly_with24🏀的指出。由于 fragment replace 时 fragment 本身并未销毁,因而 replace 只是针对 view 本身,即销毁 view 但保留其状态,以便释放大部分的资源 保持轻量。因而 replace 时由于 fragment 本身未被销毁,其成员也未被销毁,返回时,值仍在。 (OneTestFragment 已修正该实验代码)。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#11楼 @Fly_with24🏀 感谢补充,时机终于确定下来了。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
关于知识付费,我在掘金的评论区就已经明确声明过,唯有先掌握了深度思考,才谈得上拿钱换时间,否则资源给再多,也找不到正确的打开方式。 · 看看这个人!这个人和我在文中提到的,刚毕业一年的样子,一模一样,思维还停留在学生时代的死记硬背,只想不假思索地强迫自己蛇吞象,却从不动脑子思考,完全就是用战术勤奋来 掩盖骨子里的懒惰 和 自欺欺人。😡😠 · 更何况,这个人还是个妥妥的巨婴,一遇到消化不了的、不合意的,就急着撇清所有、全盘推卸责任到别人身上,不惜埋汰创作者作品的价值、不惜不尊重作者和其他读者,来达到自作聪明、一毛不拔、全身而退的目的。 · 再者,从这个人在 https://xiaozhuanlan.com/topic/4568971203 文章底部的评论可见,他连如何在 IDE 中找一个类都不懂。我在专栏简介中明确声明过,本专栏面向拥有 2 年以上开发经验的人,真不知道这个可能 1 年经验都没有的混子 是怎么混进来的。🙄 · 最后,我的目标读者都是十分优秀、乐于思考、交流 和 自我成长。 对于写作,我有自己的思考、自己的节奏、自己的风格,我对自己说过的每一句话、每一个观点负责。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#18楼 @有何高见 从注册页返回登录页,直接 navigateUp,而从注册页 popUpTo 详情页,就能顺带把登录页给关了,不需要额外的逻辑 …
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#20楼 @有何高见 本来就需要条件判断呀。Navigation 的边界仅限于声明式配置 页面之间一对一的路由导航关系,具体你要跳回到哪个页面,你就在 xml 配哪个 action,一个 fragment 可以配多个 action,每个 action 都可以指定一个 popUp 目标。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#21楼 @KunMinX 收回 21楼 的回复。 归根结底,这只是个人的愿望。我致力于分享的内容,不能要求每个人都领情,因为事实是,理解深度思考之道的人,在接触本专栏之前就已经懂得。他们懂得这么做是正确的、并且明确能够看得见 刻意练习该基本功 与 未来收益之间的联系。 有不少读者在加我的第一时间,激动地表达了他们对专栏理念的认同,因而有些,确实只能是来自于共鸣,而不能成为一种要求。 人们有依照自身习惯来决定如何学习的自由。 并且每认识一位 认真阅读 或 发生共鸣 的读者,我都会很开心 😊
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#52楼 @KunMinX 保活主要适用于“快速启动”的需要(提升用户体验),因而不建议用于在后台做 耗时耗电等操作,以免造成不良的用户体验。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#23楼 @KnewEasy 感谢你的阅读和喜欢。 出于负责任的考虑,基本每天我都会抽时间查看专栏的评论,以便能够及时解答读者在阅读过程中遇到的疑问。 重学安卓专栏不仅仅是我一个人的创作,也是集许许多多优秀读者参与互动的演化的结果。 所以每每看到评论区极个别人在投毒、对作品进行践踏,我都会很心痛。 会把主观臆想当做事实来对他人进行侵扰的人,说到底和我的目标读者根本就不在一个认知层次,所以毫不犹豫地将其驱逐离境。 · 截止目前,重学安卓群已经汇集了 数十位来自 BAT 等大厂的读者、十数位来自掘金和 GitHub 开源库优秀作者,以及受重学安卓专栏的启发,开始了写作之路的数位“明日之星”。 对技术交流、内推机会感兴趣的小伙伴,可通过专栏简介中的联系方式加我进群。(备注昵称请与小专栏的一致)
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
刚刚看到一篇关于知乎保活的神处理: 通过清单配置 excludeFromRecents 使任务不出现在最近任务列表中,而使得用户无法通过清除任务来结束进程, 再通过 taskAffinity 多出一个障眼的任务,让用户察觉不到真相。 感兴趣的小伙伴可以参考下: https://juejin.im/post/5e7b2d07e51d4565b778647f
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#24楼 @KunMinX 强迫症犯了 … 第五段 “根本就不在一个认知层次” 更精确的表述应为:“互通有无、携手共进的价值观并不相容”。 很遗憾身为作者却无法在小专栏的评论区删除评论、为评论区负责。 事实上,每篇文章在发表后的一到两天内 都会经历不下 50 次的通读和修改,评论区的留言对后来的读者同样有参考价值,所以对评论区的运营报以同等的重视。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#25楼 @KunMinX 要想从根本上解决这个问题,还是需要有个 作者本人能够控制的评论区。 涉及 主观价值判断 的言论,绝不允许出现。它的存在,只会伤害 对作品有过付出乃至有感情 的作者及读者, 并且技术专栏的关注点在于 客观事实判断、一起将关于某个技术点的事实不断清晰、完善,使作者与读者都能从中获益、确立正确的认知,从而在后来的日子里,能够不假思索地 应用于项目开发。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#28楼 @KnewEasy 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#54楼 @王大发🤔 👍👍👍
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#2楼 @十五9 感谢你的阅读和喜欢 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#18楼 @luan823230 感谢你的阅读 😉 我的目标是 帮助读者无痛地了解整体的状况。打个不恰当的比方,即 “将书本读薄”,如此一来,读者便有机会对一些细节 油然而生出一丝好奇心,而继续主动探索具体的实现。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#23楼 @结晶果 很简单。点击虚拟键 打开 “最近访问记录”,你就知道为何如此设计了。系统为每个 App 都准备了一个 ActivityStack,每个 ActivityStack 都维护各自连贯的“返回体验”。 · 例如如果在日记软件中调用系统相机,那么系统相机 Activity 及其任务是在日记软件的 ActivityStack,如果单独打开日记软件和系统相机,那么它们分属于两个不同的 ActivityStack。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
今天遇到了一位二手车主(也是一位写作者),虽然最后生意没谈成,不过她提了一个问题,我觉得问得很好。她说:是什么信条驱动你在互联网上的活动。我想她大概是指,我在互联网上活动 一以贯之的信念是什么。答案是:“让自己的回复 对别人有价值”。 · 事后回味一下,的确如此。 我有在 v2ex 上提问、交流问题的习惯,尽管是提问,事后我大都会在头部的附言上补充讨论的结果,以方便后来者(通过搜索引擎搜到问题的来访者), 《重学安卓》专栏的评论区 也是类似的信条在起作用。只不过,某种程度上已经成为一种下意识的举动,乃至于有时自己都没注意 为什么一有想法就要在评论区补充。 · 就比如被键盘侠喷得很惨的这一篇,由于料想到 后来定有读者会看到,并且这些读者中的一些人,很可能已是 或 即将成为写作者,那么我希望提供哪怕微不足道的一些见解,来方便这些写作者在遇到类似的问题时,有相应的认知作铺垫 来化解外界强加的困扰。(去年我遇到过两位写作者兼《重学安卓》读者,在掘金上的创作被键盘侠喷得一无是处,看后觉得很心痛) · 目前来看,其实策略很简单。键盘侠从不投入和付出,一味通过主观价值评判来诛心,那么在能删除评论的社区,毫不犹豫地删除评论即可。对于不能删除评论的社区,将键盘侠拉黑、让其回复对自己不可见,与此同时在正文结尾处 专门对理智讨论的部分网友点名感谢、让舆论的注意力保持在客观讨论问题本身。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#27楼 @NARUTONBM 当一个 window 被另一个 window(非 subWindow)覆盖但可见的时候,也即只走到 onPause。 比如在当前 Activity 中弹出 DialogActivity 或跳到透明 Activity 时,会让当前 Activity 处于可见而非前景的状况。返回时,Activity 会重走 onResume。
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#25楼 @刘德利 感谢你的期待 😉
66a7bbb41096d9d742d4553a9b59f6af
KunMinX
#30楼 @Petterp 感谢你分享的心得。说的十分中肯。 确实,抛开技术不说,我平时也会涉猎一些其他领域的书籍,来完善自己的认知体系。有些时候,难得遇到一本好书,书上说的,我阅读的时候都能理解,但读后能追溯起来的,就不是很多, 所以但凡读过一遍,觉得不错、有启发、有价值,我就会在这本书上 花费更多的时间 去读第二遍、第三遍,而且读的过程中,必定需要带着自己的思考,去尝试建立一个全新的认知体系、把书中关键的知识点给纳入到这个体系中,如此,这些信息才真正变成了我自己的东西、能抓在手上、能拎起来,能打包带走,印象很深刻、很久都难以忘记。 总之,你说的很到位,学习从本质上来说,并非是一种满足欲望的消费,而是一种 需要调动大量思考 去构建和修正认知 的投资行为。弄清楚这一点,就能摆正态度,踏踏实实地一步一个脚印走出自己的一片天。
66a7bbb41096d9d742d4553a9b59f6af
<