同一台机器,卡与不卡差在三处设计
"这个工具挺顺的"——这句话背后其实没什么玄学。——它不卡,不是因为代码写得多么精妙,是因为有三件事它从一开始就没打算让界面去扛。——而这三件事,恰好是同类工具最容易卡的地方。
第一件:那些要跑几分钟的活。——打包要回编、反编译要解包,都是几分钟起步的阻塞操作。——如果放在界面线程上跑,界面就真的"不动"了——不是慢,是不动:点不动、拖不动、连窗口都画不出来。
第二件:切页面这件事。——大部分工具切页面是"销毁旧的、重建新的"——而重建意味着重新加载数据、重新算布局、重新滚到顶部。——于是你切回去的时候,之前看的东西全没了。
第三件:换主题。——换配色听起来简单——把颜色改一遍就行。——而在 WPF 这类框架里,"改颜色"这件事有个坑:加载进来的资源里,颜色对象会被冻结,改不动。——所以想换主题,只有一条路:换掉整支调色板。
只需说话,就能让应用变成你想要的样子。而"说话"这件事对环境是有要求的:一个转圈等三分钟的界面,你连想说什么都会忘。想亲手感受这三处设计的差别,可以从 https://www.apkeditor.cn/ai-version.aspx 下载「安卓修改大师智改工坊」,一边打包一边切页面、一边切主题——这三件事同时做,界面还是跟手的。
一、先分清"慢"和"卡":两种完全不同的体验
这两个词经常被混着用,而它们的解法完全不同。先把它们分开。
"慢"是:事情要花时间,但你知道它在花时间。——进度条在走、日志在刷、按钮变成"正在打包"——你能看见它在干活。——这种慢是可以接受的——因为你的预期和现实一致。
"卡"是:界面不响应,你不知道发生了什么。——点一下没反应、拖窗口拖不动、想切到别的页面也不行——而任务管理器里 CPU 可能只占了百分之十几。——这种卡最让人恼火的地方是"无从判断":到底是还在算,还是已经死了?
而这两种体验的技术成因是同一个:界面线程被占住了。——界面的所有动作——响应点击、重绘、滚动、动画——都排在同一条线程上。——一旦有个长任务占着这条线,后面所有事都得等——而这个"等",用户看到的就是"卡死"。
所以"不卡"的第一原则,不是"让任务变快",是"别让任务占着那条线"。——而这条原则听起来简单,做起来有个前提:那个长任务不能碰界面——因为界面只能在界面线程上改。——于是所有跨线程的设计,本质上都在解决同一个问题:怎么把一个"想说话的长任务"和"正在画画的界面"隔开。
顺便说清一个常见误解:多开线程不等于不卡。——如果长任务在后台跑,但它每隔一秒就想更新一次界面(比如刷一行日志),而更新方式又不对——那界面反而会更卡,因为它在不停地被"叫醒"。——所以真正的设计是两件事:任务挪走,更新收敛。——而这两件事,下面几章会分别讲到。
二、第一处:把"几分钟"整个挪到后台
这个工具里有四件"会跑很久"的事:反编译、回编打包、生成源代码工程、以及"把一句话送进右边那个工作区"。
前两件的处理是标准的:整个调用放到后台线程,主线程只负责"接消息"。——打包那一路会通过回调把每一步的名字(回编/对齐/签名/校验)和工具输出的实时行传回来——而界面拿到的只是"一行文本",不是"等三分钟"。——于是你在打包的时候,界面仍然是活的:可以拖、可以切页面、可以翻日志。
第三件更有意思:生成源代码工程。——它跑的时间最长(反编译到源码,再交给 AI 工程化),所以它不只是"放后台"——它还得有一套自己的等待机制:等那个 source_ok.flag 出现。——也就是说,这个任务的"完成"不靠函数返回,靠一个文件出现——因为真正干活的是右边那个独立进程。
第四件最容易被忽略:把需求送进右边那个程序。——听起来只是"填个输入框、按个回车",实际要做一串动作:把对方窗口叫到前台、找到输入框、把文本放进去、模拟按键、还要处理"它已经关了"、"它缩在托盘里"、"它卡死了"这几种情况。——而这些动作有一个共同的前提:它们都必须在"对方是当前活动窗口"的前提下做——而"切前台"这件事本身,就可能要等。
所以这一整套被放在一个独立的、单线程单元的线程上跑。——为什么必须是"单线程单元"?因为剪贴板和窗口焦点这两件事,天生要求"在同一个线程里连续做完"——中间被别的线程插一脚,剪贴板里的内容可能就变了。——而"独立"是为了不干扰界面线程——毕竟这一串动作里有等待、有重试,全都不能压在界面上。
这里有个很实际的后果,值得说清:这条线上的失败,是"异步报告"的。——你点了「立刻修改」,界面立刻回到可用状态,然后过几百毫秒,状态栏上出现"已发送到某某"或者"发送失败:某某原因"。——而这句"失败"不是弹窗——它写在状态栏里——因为它不是"必须马上处理"的事,它是"这一轮没发出去,你可以再点一次"。
这个"状态栏而不是弹窗"的选择,其实是"不卡"的一部分。——弹窗会挡住你、需要你点一下才能继续——而如果每一条提示都弹窗,你会在十分钟里点掉二十个窗。——而状态栏是"你可以不看"的提示——它不打断你,只在你需要的时候在那儿。
把这些放在一起看,"挪到后台"这件事其实有三个层次:——第一层是"别占着界面线程"(打包、反编译);第二层是"别让界面等结果"(用标志文件当完成信号);第三层是"别用弹窗报告"(不打断你的注意力)。——三层都做到,界面才会给你一种"它一直在"的感觉——而"它一直在",就是"不卡"的真正含义。
这三层里,第二层最容易被做错。——很多工具把长任务挪到后台之后,界面就开始"每秒钟问一次好了没"——而这个"问"本身会占住界面。——而这个工具的做法是反过来的:不是界面去问,是后台在完成时把结果交上来——界面只需要"接住"。——一字之差,代价差很远:一个是一直在打扰,一个是一次性通知。
三、第二处:两个插槽,一次只亮一个
页面切换这件事,大部分工具的做法是"一个容器,换内容"——而这个工具的容器里有两个插槽。
为什么是两个?因为"交叉过渡"必须同时有两个页面在场。——旧的要淡出、新的要淡入——如果只有一个插槽,你只能"先清空、再放新的"——而那一瞬间界面是空的。——两个插槽让这件事变成:旧的慢慢退,新的慢慢进,中间没有空白。
动效的时长是有讲究的:新页进场三百六十毫秒,旧页淡出两百七十毫秒。——为什么旧的要比新的短?因为如果两个页面在同一时刻都半透明,它们会"糊在一起"——你会看到两层内容重叠,像重影。——而让旧的先退干净,新的再补上最后那九十毫秒的实心——视觉上就干净了。
新页的进场不是简单的淡入,是三件事叠加:淡入、从侧向滑进来、轻微放大回弹。——而这三个动作各有各的作用:淡入解决"突然出现",滑动解决"从哪儿来",回弹解决"生硬"。——顺便还有一道斜向的玻璃高光扫过整个内容区——它的作用不是装饰,是"遮盖":扫过去的那一下,正好盖住两个页面交替时的细节瑕疵。
而这一整套设计里,最容易被低估的是一个数字:过渡代数。——它的作用是"丢弃过期回调"。——你可以试试连续快速点五个标签页——如果没有这个机制,第二个页面的"收尾动作"(把旧页隐藏、把变换归零)可能会在第四个页面进场之后才执行——结果就是画面突然一跳。——而有了代数,每个过渡只认自己那一代——过期的收尾一律不执行。
这件事值得多说一句,因为它是"手感"和"能用"的分界线。——动画本身不难写——难的是"人在乱点的时候它不崩"。——而真实使用里,人就是会乱点:切过去看一眼、马上切回来、再切过去——如果动画系统不能承受这种乱点,它就会在某个瞬间展示出一个错误的中间态。——而这个工具的解法非常工程化:给每一代过渡编号,过期的直接扔掉。
最后是这一处设计里最有价值的一条:两个插槽里的页面实例,一直保留着。——也就是说,切走的时候页面没有被销毁——它只是被藏起来了。——于是你切回来的时候:滚动位置还在、截图还在、输入框里刚打了一半的字还在、列表展开的项还是展开的。
为什么这条最值钱?因为它省掉的是"重新建立上下文"的成本。——你在一张截图前想了半分钟"这个图标是不是歪了",然后切到另一个页面看一眼、再切回来——如果截图没了,你得重新选、重新放大、重新想。——而如果它还在,你那半分钟的思考是可以接着用的。——这就是"不卡"的另一层含义:不只是帧率,还有"思路不被打断"。
而"保留实例"这件事有个前提:页面本身要经得起"暂停很久再恢复"。——如果某个页面在显示时会一直跑动画或者定时刷新,那它被藏起来之后还在耗资源——于是"保留"就变成了负担。——所以设计上还得配一条纪律:不可见的时候,停掉该停的东西。——保留的是状态,不是"活动"。
四、第三处:换主题不是"改颜色",是"换一整本字典"
这一处最技术,也最容易被误解。很多人以为换主题就是"把界面上的颜色挨个改一遍"——而这个做法在这里根本走不通。
为什么走不通?因为颜色对象被冻结了。——在 WPF 里,资源字典一加载,里面的画刷、变换这类对象就会被冻结——冻结之后不能再改。——更坑的是,就算你在 XAML 里写上"不要冻结我"(有个属性可以标),对于从资源包地址加载进来的字典也不生效——它照样冻结。
于是只剩一条路:不改变量的值,换掉整个变量。——具体做法是:按当前色系重新生成一份调色板字典,然后把应用资源里"旧的那一支"整支替换成新的。——而替换这一下会触发资源变更通知——界面上所有以动态资源方式引用的颜色,会立刻重新解析。——结果就是:换主题即时生效,不用重启程序。
这里有两个细节,能看出这套做法的成熟度。
细节一:字典里放了一个"标记"。——因为应用资源里可能合并了好几支字典(样式一支、控件模板一支、调色板一支),换的时候得知道"哪一支是调色板"。——于是调色板那一支里放了一个特殊的键,靠它来认。——为什么不用"按顺序取第几支"?因为顺序是会变的——今天加一支字典进去,明天那个"第几支"就指错了。
细节二:换之前先把选择记下来。——代码里先记"当前主题是哪个",再去替换字典——这样即使替换那一步失败(比如资源还没就绪),选择也不会丢。——而这个"先记下来"的顺序,在实际使用里体现为:你在设置页点了某个配色,就算界面还没刷完,下次打开也还是那个。
顺带说清一个相关的问题:为什么有些动画对象必须在代码里创建,不能写在 XAML 里?——因为同一个原因——写在界面描述文件里(内联声明)的变换对象,在加载时也会被冻结。——而冻结的对象挂不上动画——一挂就报"已密封或已冻结"。——所以你会看到代码注释里写着"统一在代码里创建"——这不是风格选择,是被框架逼出来的正确做法。
换掉一整本字典,而不是改每一个字
这套"换字典"的做法,最后带来一个很实际的体验:换主题不用重启,也不丢状态。——你在某个页面上、滚动到中间、切一下配色——页面还在那儿,只是颜色变了。——而如果换主题要重启程序,你大概率不会去试第二套配色——因为试一次的成本太高。——而"可以随手试",正是这套设计真正想要的东西。
五、三处设计的共同点:没有一处需要"重启"
把前面三处放在一起看,会发现它们其实是同一个思路的三个应用。那个思路是:不要重建,只改变。
打包的时候,界面不重建——它只是继续画自己,顺便显示一行"正在对齐"。——切页面的时候,页面不重建——它只是被藏起来,等一下再露出来。——换主题的时候,控件不重建——它们只是把"用哪个颜色"这件事重新问一遍。
而"重建"这件事,代价远比看起来大。——重建一个页面意味着:重新创建控件的视觉树、重新测量、重新排列、重新加载数据、重新绑定、重新触发一遍进场动画。——而这一串动作里,任何一步有阻塞(比如读磁盘、等网络),界面就会在那一步停一下。——停一下不要紧,但如果你在快速切页面,就会看到断断续续的顿感。
同样的道理,也体现在窗口本身。——这个程序没有用系统自带的标题栏——它自己画了一条。——为什么?因为系统标题栏的样式、高度、按钮位置都不由你控制,而自己画就能做到"和整体视觉一致"。——代价是要自己处理拖动、最大化、贴边这些行为——而收益是"窗口看起来是这个应用的一部分",而不是"一个套着系统的框"。
顺便说一个很细的处理:窗口的圆角交给了系统合成器,而不是自己画一层蒙版。——自己画圆角意味着每次缩放、每次移动都要重算一遍裁剪区域;而交给系统,它是合成阶段顺手做的事。——这类"能交给系统就别自己做"的判断,也是"不卡"的一部分——因为省下的每一帧,都是给人的反应留的余量。
三处设计的同一个思路
把这条思路说得更直白一点:一个"顺手"的界面,不是因为它的每一项操作都很快,而是因为它在你不注意的地方,替你省掉了重建。——而重建这件事有个特点:做的人知道成本,用的人只觉得"怎么有点顿"。——所以这类优化往往不会被夸,只会被"没被骂"。
顺便说清一个容易混的地方:不重建不等于不刷新。——页面被保留着,但它显示的数据仍然是新的——因为数据变化会通知它去更新,而"更新"和"重建"是两件事。——重建是把整个控件树推倒重来;更新只是把变了的那一处改掉。——而"不卡"的秘密,八成就在这两者的区分上。
六、两个例子:不卡到底省下了什么
"顺手"这个词太抽象,换成两件具体的事。都是我们自己在改内部应用时遇到的。
例子一:打包那三分钟,能不能顺手做别的事。
以前怎么做:——早先用命令行那套流程,打包是"敲一条命令,然后盯着黑窗口"。三分钟里你什么都不能做——因为你不确定它是否还在跑,也不敢开别的窗口去动那个目录。——而那三分钟的真实成本不是三分钟——是你被打断了:你原本想接着看下一处要改哪儿,结果只能等。
现在怎么做:——点打包,界面立刻回到可用——你可以拖窗口、切到另一个标签页看历史、甚至打开项目目录看 build 下面三个文件依次出现。——而进度是一行一行刷的:正在回编、正在对齐、正在签名、正在校验。——于是那三分钟变成了"可以顺手做别的事的三分钟"。
改完怎么验证:——打包进行中,去拖动窗口边缘——如果窗口跟着鼠标走,说明界面线程没被占;再切一次标签页——如果能切、而且动效正常,说明渲染也没被占。——这两下试完,你就知道"后台"这件事是真的做到了,还是只是"按钮变灰了"。
例子二:改图标时要来回切的那几十次。
以前怎么做:——给一个内部应用的首页换图标,改法是"看一眼设计稿、改一处、再看一眼设计稿"。——而设计稿在另一个工具里,或者干脆就在这个程序的某个页面上——于是你来回切。——而在"切回来就重建"的界面里,每次切回来你都要重新找到刚才那一屏——滚动回原来的位置、重新点到那个图标列表。——一次两次没关系,几十次之后,你会开始烦。
现在怎么做:——切过去、切回来,位置还在——滚动条停在原处,列表还是展开的,输入框里打到一半的需求也还在。——于是"来回切"从一件有成本的事,变成了一件没有成本的事。——而"没有成本"带来的是行为的改变:你会更愿意去核对,而不是"差不多就这样吧"。
改完怎么验证:——把某个长列表滚到中间,切走,再切回来——如果还在中间,说明实例被保留了;再随手往上滚两下看有没有卡顿,说明重建确实没发生。——两下就能判断,不用看任何设置。
这两个例子放在一起,能看出"顺手"的真正价值:它改变的不是速度,是你的工作方式。——能顺手切一眼的时候,你会去核对;不能的时候,你会赌。——而改包这件事,赌的代价往往是一次装机、一次卸载、一次"数据没了"。——所以"不卡"最后落到的是准确性——这也是为什么它值得被当成一个功能来做,而不是当成性能优化。
七、这些设计各自的代价
只讲好处不公平,也不实用。这三处设计每一处都有代价,而知道代价在哪,你才知道它们的边界在哪。
代价一:页面常驻要占内存。——两个插槽意味着同时最多有两个页面活着——它们的数据、它们加载的图片、它们的控件树都在内存里。——而这个工具的取舍很清楚:只保留两个,不是"全保留"。——所以你会感觉到"最近切过的那个页面回来很快",但"很久没去的页面回来还是要等一下"——那不是缺陷,那是刻意的上限。
代价二:三百六十毫秒的动效,对熟练用户来说是"多等的"。——如果你只是快速扫一眼,动效确实挡住了你。——而这个时间的取法是"够看出方向、又不够让你烦"——它比常见的两百毫秒慢一点,但换来的是"能看清是从哪边来的"。——这类参数没有标准答案,只有"这一版先这样"。
代价三:换字典的瞬间,界面会重刷一次。——因为所有动态引用都要重新解析——如果某个控件自重绘比较重,你可能会看到它闪一下。——这是"不重启"换来的:它用一次瞬时重刷,换掉了"关掉再开"的三十秒。——而这个交换在大多数场景下是赚的——除非你在做很重的东西(比如正在播放动画),那时候切主题会看到一点顿。
代价四:后台线程里的失败,需要一条明确的"回来"的路。——如果打包在后台炸了,界面得知道——而这个工具的做法是把结果和错误一起交回界面线程,再决定是显示在状态栏还是弹窗。——而这个"回来"的过程如果设计得不好,就会出现"后台已经失败了,界面还在转圈"这种状态。
还有一处不属于"界面设计"但影响极大:跨权限的窗口通信。——如果右边那个工作区是以管理员身份运行的,而主程序不是,那"把话送过去"这个动作会被系统拦掉——这是 Windows 的机制,不是程序的 bug。——而这类问题不在"卡不卡"的范畴里,它属于"能不能"——但它同样是"顺不顺手"的一部分。
把这四条代价放在一起,会看到一个共同的取舍方式:所有优化都是"用一点资源,换一段不被打断的时间"。——内存换页面的状态、三百毫秒换方向感、一次重刷换不重启——而每一项都设了上限:两个页面、三百六十毫秒、一次重刷。——有上限的优化才是可持续的——没有上限的优化,最后会变成"占着内存但不快"。
八、三个小实验:自己验证这三处设计在不在
读完前面几章,你大概会想"这些我怎么能感觉到"。下面三个实验,每个三十秒,不需要任何工具。
实验一:打包中拖窗口。——点一次打包,然后在"正在回编"的时候,抓住窗口边缘拖动。——如果窗口跟着鼠标走,说明界面线程空着——长任务确实在后台。——如果窗口僵在那里、鼠标变成一个等待圈——说明长任务占着界面线程。
这个实验为什么可信?因为"拖窗口"是界面线程最基础的能力——它要是都不响应,别的更不用试。——而很多工具在这一项上就不及格:它们会把按钮变成"正在打包,请稍候",然后整个窗口一起僵住。
实验二:快速连点五个标签页。——用最快的速度连点,别等动画结束。——如果最后停在第五个、画面干净、没有重影也没有突然一跳——说明"过期回调"被正确处理了。——如果画面在某一刻出现了两层内容叠在一起,或者动画突然加速、跳帧——那就是过渡代数没做。
这个实验测的是"抗乱点",而它比"动画好不好看"重要得多。——因为真实使用里没人会按设计好的节奏点——你会切过去、觉得不对、马上切回来——而这正是最容易露馅的时刻。
实验三:换主题时看页面还在不在。——进某个页面,把列表滚到中间,然后去设置页换一套配色,再回来。——如果滚动位置还在、页面内容没重新加载——说明换的是字典,不是重建界面。——如果回来发现滚到顶了、或者数据重新加载了一遍——那说明换主题顺带把页面重建了。
这个实验的意义在于:它区分了"换颜色"和"换主题"两种实现。——前者的体验是"颜色变了";后者的体验是"还是这个地方,只是颜色变了"。——而后者需要的正是第四章讲的那套东西。
三个实验合起来,其实在测同一件事:这个界面有没有"守住你的位置"。——拖窗口时它守着你能动;乱点时它守着画面不错乱;换主题时它守着你所在的页面。——而这些"守"的背后,都是"不重建"这一个决定的连锁结果。
九、把这三处搬到你自己的项目里:三条原则
如果你也在做桌面工具,这三处设计可以直接搬。搬的时候记住三条原则,比记住具体做法更有用。
原则一:任何"不知道要多久"的操作,都别放在界面线程上。——注意是"不知道要多久",不是"超过三秒"。——因为界面的价值不在"快",在"有反应"——而只要操作时长不可预测,它就会在最糟的时候占住界面。——而判断标准可以更狠一点:如果这个操作会碰磁盘、会碰网络、会启动别的进程——它就不该在界面线程上。
原则二:把"完成"设计成一个可观察的信号,而不是一个返回值。——这个工具用标志文件,因为真正干活的是另一个进程。——而你自己的项目里,这个信号可能是一个队列、一个事件、一个数据库里的状态。——形式无所谓——关键是"界面上能看到它变了",而不是"你得去问它好了没"。
原则三:宁可保留,不要重建——但一定要设上限。——保留让状态不丢,上限让内存不炸。——这个工具的取值是"两个页面"——而你的取值可能是"最近三个"或者"一个"。——重要的是有这个上限,并且它是刻意的——而不是"先写出来,以后再说"。
顺便讲一个反例,因为它太常见了:把长任务放后台,但让后台线程直接改界面。——结果是有时候好、有时候崩——因为跨线程改界面在框架里是不允许的,运气好它自己抛异常,运气不好画面就花了。——正确做法是"后台算、前台改":后台把结果交给界面线程,界面线程再更新。——而这个"交给"的动作,才是这套设计里最容易写错的一行。
如果只记一件事,记这个:把"卡"当成一个缺陷来报,而不是当成性能问题来优化。——缺陷的意思是"它不该发生",而性能问题的意思是"它能更好"。——而这两句话带来的行为完全不同:前者会让你去查"哪一步占住了界面",后者只会让你去优化算法。——而绝大部分"卡",根本不是算法慢——是界面线程在等一个不该它等的东西。
十、用过的人怎么说
"我上一台机器很旧,最怕的就是打包时整个窗口僵住。这个不会——打包时我还能拖着窗口去另一个屏幕上,一边看日志一边干别的。"
—— 老周 · 做了十几年运维
"最舒服的是切回来东西还在。我改图标要来回看设计稿几十次,以前每切一次就得重新滚、重新点,现在位置原样在那儿。"
—— 小林 · 电商公司内容负责人
"换颜色不用重启这件事,我一开始以为是理所当然。后来用别的工具改个主题要关掉重开,才知道这一下有多省。"
—— 小唐 · 独立开发者
"我习惯连着点标签页,这界面不跳不闪,最多就是动画快了一点。用过几个会跳的,那种一跳你就得重新找自己在哪。"
—— 阿凯 · 小型软件工作室负责人
"我最在意的是它不弹窗。失败了的提示在状态栏里,不挡我。以前那种每步都弹一个的,点确认点到手酸。"
—— 陈工 · 制造企业信息化主管
五条里,有两条提到的是"位置"——一个是窗口能拖到别的屏幕,一个是切回来还在原处。————而"位置"这件事,恰好是界面设计里最容易被忽略的:它不体现在功能清单上,只体现在"你有没有被打断"。
还有一条提到"不弹窗"——这一点其实和前三处设计是同源的。————弹窗的本质是"抢注意力",而抢注意力是一种成本——你在一个需要连续思考的任务里,被抢一次就要重新进入状态。————所以"不打断"这件事,比"跑得快"更接近"顺手"的本意。
十一、把一天的工作流串起来看
三处设计单独看是三个技术点,串成一天的工作流就是三种"没被打断"。
上午:导入、解包、写下第一句需求。——反编译在后台跑,进度一行一行刷——你可以趁这几分钟去把设计稿找出来、把要用的图标整理到同一个文件夹。——等它解完,你把那句话写进输入框、把附件加进去,点「立刻修改」——界面立刻回到可用,然后状态栏告诉你"已发送"。——这一整套里,你没有一次"盯着不动的东西等"。
中午:等 AI 改完的那一段。——这里用的是另一种"不打断"——程序每两秒看一次那个完成标志,读到就开始打包。——而这段时间你可以去吃饭——因为等待是有上限的(一小时),不会永远挂着。——回来的时候,要么已经打好包了,要么明确告诉你"没等到"——两种都是可行动的状态。
下午:反复微调的阶段。——这个阶段是三处设计同时起作用的时候:打包在后台跑,你在两个页面之间来回切(看历史、看日志、看详情),偶尔去设置页换一套配色因为在强光下看不清楚。——而这三件事可以同时进行——它们互不打断。——而"互不打断"带来的直接结果是:你会在一天里做完更多的核对。
把这个流程和"会卡"的版本对照一下,差别就很清楚了。——在会卡的版本里:解包时你去倒水;打包时你刷手机;切页面时你等重建;换主题时你重启程序等三十秒。——这些等待单次都不长,但它们把你的一整天切成了碎片——而碎片化的时间里,人是做不了"核对"这种需要连续的活的。
所以"不卡"这件事,最后落在的是一个很朴素的收益:它让你的注意力可以停留在一件事上。——而改包这件事最需要的恰恰是注意力——因为绝大多数错误不是"不会做",是"没看仔细"。——同一个包,注意力在的时候你能看出图标偏了两像素;注意力散的时候,你只会觉得"差不多"。
还有一层更隐蔽的收益:不卡让人愿意多用几个功能。——如果切一次页面要等两秒,你不会去翻历史;如果换一次主题要重启,你不会去试第二套配色。——而这些功能之所以存在,本来是为了让你少走弯路——它们被用起来,才有价值。——而"用起来"的前提,是它们不给人添麻烦。
最后补一段收尾的观察:这三处设计,会让你在一天结束时"记得住自己做了什么"。——因为你的连续性没被打断那么多次——你知道上午改的是哪一处、下午调的是哪个图标、中间那次打包用的是哪份签名。——而如果一天里被打断了二十次,到了晚上你只会记得"改了挺多东西",具体改了什么要靠翻历史。
十二、三条判断标准:一个界面顺不顺手,看什么
写到这里,可以给出一套不依赖任何测量工具的判断方法。三条,都能在五分钟内试出来。
标准一:长任务能不能"放在一边"。——试法就是第八章那个拖窗口——能拖,说明它在后台;不能拖,说明它占着界面。——而这一条之所以排第一,是因为它决定了"你会不会被打断"——被一个三五秒的操作打断还无所谓,被一个三分钟的打断,一天就碎了。
标准二:乱点的时候它崩不崩。——试法是快速连点、快速来回切——看画面会不会重影、跳帧、错位。——而这一条的重要性在于:它测的是"边界情况",而真实使用里全是边界情况——按脚本点的人不存在。
标准三:它记不记得你在哪儿。——试法是滚到中间、切走、切回来、换主题、再切回来——位置还在不在。——而这一条最容易被忽略,因为"记住位置"不出现在任何功能列表里——但它是"连续思考"的前提。
这三条有一个共同的特征:它们都不是"性能指标"。——你没法用帧率或者响应时间去完整描述它们——因为它们的本质是"有没有打断你的工作"。——而这个东西只能靠体验判断,不能靠跑分。
顺便说一句:这三条也能反过来用在"选工具"上。——你要挑一个天天用的桌面工具,先去试这三条——比看十页功能对比有用。——因为功能决定"能不能做",而这三点决定"愿不愿意天天做"。
十三、为什么这三处是"为改包"专门做的
最后回到一个更有意思的问题:这三处设计,为什么改包这件事特别需要?
因为改包的工作节奏是"等待 + 核对 + 微调"的循环。——等待(解包、打包)本来就要花时间;核对(装上看、对比设计稿)需要连续注意力;微调(改一点、再打一次)会把前面两步重复很多遍。——而在这样的节奏里,"界面卡住"的伤害是乘起来的——它打断的正好是核对那一段。
更具体一点:改图标这件事,一天可能要来回切几十次。——每次切走再切回,如果位置丢了,你就得重新找——而"重新找"这件事有个隐性代价:你会开始偷懒,不再回去核对,而是凭记忆改。——而凭记忆改图标,几乎一定会错一个。
所以我们说这三处设计是"为改包做的",意思是:它们不是通用的界面美化,而是针对这件工作的节奏做过取舍的。——两个插槽而不是更多——因为改包时你通常只需要在两个东西之间来回看(工程和参考);三百六十毫秒而不是一百五——因为你要看清"从哪边来的"。——这些数字背后都有一个具体的工作场景。
而这也回答了最开始那个问题:"这个工具挺顺的"到底是哪儿顺。——顺在它把三件"会让你等"的事都搬走了;顺在它不重建、只改变;顺在它在你乱点的时候不崩、在你切回来的时候还在原处。——而这三件事,没有一件写在功能列表里——但它们决定了你一天能改多少个包。
十四、还有三处更小的"不打断"
除了那三处大设计,还有三个很小的处理,作用一样:不抢你的注意力。
第一处:点下去,立刻有变化。——点「去打包」,按钮马上变成"正在打包"、旁边开始出第一行状态——而不是先静半秒再反应。——这半秒的差别听起来微不足道,但它决定了你会不会"再点一次"——而重复点击是很多工具出问题的起点。——所以"立即反馈"不只是体验问题,它还顺带防住了误操作。
第二处:失败写在状态栏,不弹窗。——前面提过一次,这里补上它的边界:不是所有失败都不弹窗——需要你"现在就看"的(比如会话上下文不够用、必须手动新建会话)还是会弹。——而"发送失败"、"这一轮没等到"这类只需要你知道、不需要你立刻处理的,就写在状态栏。——这个区分的标准很清楚:会不会让接下来的动作做不成。
第三处:首页那条提示,每十二秒换一条。——它一共备了一百九十多条使用技巧,轮着显示。——而这个设计的妙处在于时机:它在你"没有操作"的时候才轮换——你一动手它就停在那儿。——所以它更像"放在桌角的一张小纸条",而不是"一直在动的广告位"。
小处理的原则只有一个:别抢注意力
这三处加起来,说明"不卡"这件事不只发生在渲染层。——它还发生在"你点了之后有没有回应"、"出错了会不会打断你"、"空闲时它会不会自己动"这些地方。——而这三处的共同做法是:把"主动性"交给用户——你不动,它才动;你需要,它才出现。
合规提醒
本文讨论的所有改动,都应在自己拥有版权、或已获得权利人明确授权的应用上进行。请勿用于破解他人软件、绕过授权验证、去除版权标识或任何侵犯他人权益的用途。文中提到的自家应用实例,均为我们有权修改的项目。另外提醒一点:把长任务放到后台,意味着你可能在打包的同时还在做别的操作——如果那期间你手动改了工程目录里的文件,打出来的包就会混进意料之外的内容。所以后台跑着的时候,尽量让工程目录"只被程序碰":要看文件,看 build 目录和日志;要改文件,等这一轮结束。
安卓修改大师智改工坊
等的时候,界面还是你的
导入 → 一句话描述 → 打包 → 装机确认,四步走完
官网 www.apkeditor.cn
立即下载智改工坊(AI 版)
支持 apk / apks / xapk / apkm 等包型 · Windows 桌面端
只需说话,就能让应用变成你想要的样子。——而这句话能说出口,得先有一个不打断你的界面:这就是为什么安卓修改大师智改工坊把那么多功夫花在了"你看不见的地方"。