V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
KunMinX
V2EX  ›  Android

真香警告:就算不封装商业级 SDK,也请务必掌握的 Android 状态管理最佳实践!

  •  
  •   KunMinX · 2019-07-03 13:29:51 +08:00 · 12450 次点击
    这是一个创建于 2000 天前的主题,其中的信息可能已经有所发展或是发生改变。

    很高兴见到你!

    上上周我在掘金碰巧遇到了一篇用设计模式管理状态的文章,一时兴奋不已,在评论区安利了,一直以来我司在封装商业级 SDK 时,使用的十六进制状态管理机制。

    原以为会无人对此感兴趣,没想到,留言很快就收到文章作者的回复,并且在评论区耐心地和我探讨了设计模式的 独占式状态机,和十六进制的 复合状态管理 在使用场景上的区别。

    遗憾的是,通过评论区的只言片语,并不能让人体会到 十六进制状态管理 的真正魅力。

    于是作为回馈,我特地分享了这一篇:当我们封装商业级 SDK 时,我们是怎么使用十六进制来完成状态管理。

    😉

    此外,是只有封装 SDK 这种大动作,才值得使用十六进制吗?不是的,恰恰相反,正因为 十六进制状态管理是如此地普适,乃至于连封装 SDK 都优先使用这种方式。

    考虑到部分读者可能对十六进制本身不太了解,本文会连同十六进制一起介绍。

    所以如果阅读完这篇文章,你对 十六进制的状态管理 有了感性的认识,那我的愿望也就达到了。

    https://juejin.im/post/5d1a148e6fb9a07ea6488ba3

    38 条回复    2019-07-06 16:46:43 +08:00
    also24
        1
    also24  
       2019-07-03 13:53:40 +08:00   ❤️ 5
    你这明明是二进制状态管理,关十六进制什么事情……

    写成十六进制还不方便理解,要思考一下:
    private final int STATUS_1 = 0x0001;
    private final int STATUS_2 = 0x0002;
    private final int STATUS_3 = 0x0004;
    private final int STATUS_4 = 0x0008;


    直接移位多方便:
    private final int STATUS_1 = 1 << 0;
    private final int STATUS_2 = 1 << 1;
    private final int STATUS_3 = 1 << 2;
    private final int STATUS_4 = 1 << 3;


    或者干错手动补 0 也方便理解啊:
    private final int STATUS_1 = 0b1;
    private final int STATUS_2 = 0b10;
    private final int STATUS_3 = 0b100;
    private final int STATUS_4 = 0b1000;
    whileFalse
        2
    whileFalse  
       2019-07-03 13:55:48 +08:00   ❤️ 11
    各位不用点进去了,就是把多个 bool 放到一个 int 里面。洋洋洒洒写了四千字。
    busymilk
        3
    busymilk  
       2019-07-03 13:59:12 +08:00
    不是 flag 那一套么。。。
    WebKit
        4
    WebKit  
       2019-07-03 14:02:35 +08:00 via Android
    这种文章写这么详细,应该比较适合刚入门什么都不会的。
    KunMinX
        5
    KunMinX  
    OP
       2019-07-03 14:04:10 +08:00
    @also24 @whileFalse @WebKit

    那你很棒哦
    AndroidEngineer
        6
    AndroidEngineer  
       2019-07-03 14:24:15 +08:00
    来点高端的,比如 dex 热修,so 混淆加密,现在 Android 不往底层走等于菜鸡
    ShangShanXiaShan
        7
    ShangShanXiaShan  
       2019-07-03 14:32:34 +08:00
    @AndroidEngineer "你很棒"警告
    Lucups
        8
    Lucups  
       2019-07-03 14:53:06 +08:00
    感叹号结尾的文章标题一律不看
    ys1992
        9
    ys1992  
       2019-07-03 14:57:23 +08:00   ❤️ 1
    楼主分享思路不错,赞一个!一楼优化解释也很好,确实更通用且便于理解吧,本质都是通过位运算来解决多状态集合问题
    ostholz
        10
    ostholz  
       2019-07-03 15:05:09 +08:00
    @also24 节约我时间, 没点进去。
    agostop
        11
    agostop  
       2019-07-03 15:08:05 +08:00
    之前在一个接口上用过类似的算法,和题主描述的场景差不多,不过是用的二进制……没看懂为什么非要用 16 进制?
    ice000
        12
    ice000  
       2019-07-03 15:11:07 +08:00
    标题党
    zjiecode
        13
    zjiecode  
       2019-07-03 15:13:44 +08:00
    感谢楼主,虽然很基础,但是还是很不错的,另外,权限之类的,也可以考虑这种思想。
    不过,要是状态超过了 32,就用 long,那超过 64 了呢?有没有啥好的解法呢?
    KunMinX
        14
    KunMinX  
    OP
       2019-07-03 15:23:40 +08:00
    @Lucups @ostholz @ice000

    那你很棒哦

    @ys1992 @mrjiejiejie

    感谢你的阅读!

    我司目前封装的 SDK,一个功能的状态一般不超过 12 个,所以就算 int 也足够。

    64 个状态的话,到底是什么功能才会存在这样的设计呢?
    SuperNovaSonic
        15
    SuperNovaSonic  
       2019-07-03 15:24:35 +08:00
    来自掘金:
    SuperNovaSonic
        16
    SuperNovaSonic  
       2019-07-03 15:24:44 +08:00   ❤️ 1
    金 1998
    你应该没怎么玩过 C 语言
    21 分钟前
    KunMinXlv-3(作者)
    Android @ ViaBus-Architecture
    回复 金 1998: 那你很棒哦
    4 分钟前
    KunMinX
        17
    KunMinX  
    OP
       2019-07-03 15:40:25 +08:00
    @SuperNovaSonic

    有什么依据呢?
    yusuzhan
        18
    yusuzhan  
       2019-07-03 15:55:20 +08:00
    这个东西在 Java 里很常用,叫做位掩码。
    alextang95
        19
    alextang95  
       2019-07-03 16:02:52 +08:00
    @KunMinX 这种状态处理属于基本操作。
    C 语言用的更多,因为要用那么一点点内存作更多的事。
    另外这和十六进制没什么关系,就是二进制状态。
    状态的转移用位运算。

    @also24 @whileFalse 二位省了大家好多时间,感谢
    lastpass
        20
    lastpass  
       2019-07-03 16:06:07 +08:00 via Android   ❤️ 1
    楼主快坐下,快坐下。
    不过一些常规操作而已。
    快翻开你大学时候的计算机导论,
    c 语言基础,
    汇编基础的书的前两章温习一下。
    cizeZSY
        21
    cizeZSY  
       2019-07-03 16:06:14 +08:00 via Android
    这不是很常见的东西吗。。。
    quaack
        22
    quaack  
       2019-07-03 16:11:39 +08:00
    哈哈哈哈营销鬼才,这不是最简单的位运算吗

    要是楼主知道各种 bit magic 可能会吹上天吧
    quaack
        23
    quaack  
       2019-07-03 16:12:30 +08:00
    maninfog
        24
    maninfog  
       2019-07-03 16:15:33 +08:00 via iPhone
    很棒的文章,支持一个。
    Mithril
        25
    Mithril  
       2019-07-03 16:16:46 +08:00
    上古时代 Windows API 里的各种 HRESULT 不就是这么干的么。。。
    xenme
        26
    xenme  
       2019-07-03 16:17:37 +08:00
    @whileFalse 你这一句就把楼主打醒了。

    估计楼主又要写四千字反省下了。
    alextang95
        27
    alextang95  
       2019-07-03 16:19:38 +08:00
    感觉我有点歪楼了,抓紧掰回来

    1. 给 LZ 的分享精神点赞
    2. 反应比较平淡的是见多了就不惊讶了
    3. 恰到好处地解决问题的感觉很爽
    4. 文章是有点啰嗦哈
    5. 1L 讲的是挺棒的啊……

    LZ 别 block 我……
    lastpass
        28
    lastpass  
       2019-07-03 16:33:04 +08:00 via Android
    另外:我再补充一下。为何用二进制的形式存储状态被淘汰,除非是状态特别多,否则大家都不会使用的原因。
    因为现代级的 CPU 寄存器非常之多,还有大量的 l1,l2,l3 缓存,这些状态会大部分情况下会被缓存到 cpu 的寄存器。直接从寄存器取数据可比 cpu 从内存里面取快多了。而且,很多时候。我们通常都只需要某个状态。直接取值和算一遍再获得值,后者的效率明显会略低(虽然大部分情况下,这点速度差距可以忽略)
    lastpass
        29
    lastpass  
       2019-07-03 16:35:17 +08:00 via Android
    当然,更重要的原因是,用二进制存储状态,太特喵不直观,代码阅读起来太费劲了。所以,大家都不愿意使用这个东西。
    huijiewei
        30
    huijiewei  
       2019-07-03 17:22:50 +08:00 via iPhone
    位运算非要强行扭成 16 进制,写技术文章最好多参考
    KunMinX
        31
    KunMinX  
    OP
       2019-07-03 17:54:02 +08:00
    @maninfog 感谢你的阅读!

    @lastpass 感谢你的补充!
    no1xsyzy
        32
    no1xsyzy  
       2019-07-04 10:07:22 +08:00
    @mrjiejiejie 编程珠玑里面不是有过了吗?状压存电话号码,何止 64 个状态,是 1,000,000 个以上的状态,全按位压缩。
    no1xsyzy
        33
    no1xsyzy  
       2019-07-04 10:22:15 +08:00
    《编程珠玑》( Programming Pearls )
    《算法心得:高效算法的奥秘》( Hacker's Delight )
    这些方法现在越来越少了,原因:
    1. 内存不缺了
    2. #28 缓存更快
    3. 影响可读性
    4. 现在编程语言常常提供 Set 类型,可以兼具高效和可读性

    老古董 Pascal 就支持 set of <enumerable>,可以当作位压编译期展开(可能需要看具体实现)。
    甚至可以定义 enum 类型然后做集合,不比你这直观得多?
    https://en.wikibooks.org/wiki/Pascal_Programming/Sets
    no1xsyzy
        34
    no1xsyzy  
       2019-07-04 10:36:49 +08:00
    另一方面,为何采用 8 个 status (无论是 bool 还是位压 int )标记按钮可用性是逻辑上错误的。
    不应该上游控制下游,而应该下游因变于上游。
    fn (ui:UI).setModeA(){
    ui.mode = A; ui.updateButtonsAvailability();
    }
    // same for modeB, modeC
    fn (ui:UI).updateButtonsAvailability(){
    for(button in ui.buttons){
    button.updateAvailability(mode=ui.mode)
    }}
    fn (button:Button).updateAvailability(mode:EnumMode){
    button.availability = mode in button.availableModes
    }
    keymao
        35
    keymao  
       2019-07-04 11:05:41 +08:00   ❤️ 1
    还是那句话 有事儿说事儿,上来就是这也简单那也存在,人家就是分享点东西而已,有错误可以合理指出,不足之处补正,愿意讨论讨论,不愿意讨论看看关了就完了,来句不甜不咸的很简单书本里有,有啥意思么? 愁了。 讨论问题,能不能别像个小孩一样。
    mmdsun
        36
    mmdsun  
       2019-07-05 08:17:52 +08:00 via Android
    Mark。二进制状态?
    nicoljiang
        37
    nicoljiang  
       2019-07-05 18:56:01 +08:00
    @keymao 可能是因为楼主没管理好读者的预期(标题起的太大)
    karllynn
        38
    karllynn  
       2019-07-06 16:46:43 +08:00
    楼主你没学过 C 语言么…

    bit flag 是基本知识吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5530 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 05:54 · PVG 13:54 · LAX 21:54 · JFK 00:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.