只需说话,就能让应用变成你想要的样子
安卓修改大师智改工坊 · 把改包这件事说清楚
升级后的数据与本地存储
升级本身不动数据,读不懂数据才是丢的原因

改一个包,最容易被忽略的不是界面,是那些没人看见的本地数据:登录状态、本地草稿、离线缓存的工单、上一次填到一半的表单。这些数据平时不存在于任何人的视线里,只在升级之后以「我的东西怎么没了」的形式出现一次。

更让人头疼的是,这种问题不会崩、不会报错、不会出现在任何日志里。应用打开得好好的,只是里面空了。用户以为是自己点错了,客服以为是个案,等到汇总上来,已经是几百台设备之后的事。

这一篇讲本地数据:它分几层、升级和卸载分别会动到哪一层、改了包名或加了字段之后为什么会读不出来,以及迁移该在什么时候做、怎么验收。官网是 www.apkeditor.cn。

一、本地数据其实分四层,各有各的命

说「数据丢了」之前,先把数据分开。四种数据住在四个地方,升级和卸载对它们的影响完全不同。

第一层,偏好与登录态。一串键值对,存着「上次登录的账号」「是否看过引导」「上次选的仓库」。它体量很小,通常几 KB,但丢了的感受最直接:要重新登录。这一层最容易被当成「不重要」,实际上它是用户每天第一眼遇到的东西。

第二层,结构化数据。通常是本地数据库,存着业务记录:工单、订单、巡检项、聊天记录。它体量中等,结构复杂,有版本号。这一层是迁移的主战场,也是加字段、改字段类型之后最容易出事的地方。

第三层,文件。照片、录音、导出的表格、下载的附件。它们住在应用的私有目录里,按路径存放。这一层的特点是:文件本身不会因为升级消失,但「找不到它」等于消失——路径规则一改,旧文件就变成了孤儿。

第四层,缓存。可以随时重建的东西:图片缩略图、接口返回的临时结果、预加载的页面。这一层丢了完全无害,也是唯一一层可以放心清理的。麻烦在于,很多应用把不该清的也放在这里。

分层之后有个判断就清楚了:清理缓存的按钮,应该只动第四层。如果用户点了「清除缓存」,结果要重新登录、草稿没了,那就是前三层被误伤了。这种误伤在真实用户那里,比崩溃更让人生气,因为它看起来像是应用自己动的手。

二、升级不动数据,卸载才清空

很多人以为升级会把数据清掉,所以每次升级前都提醒用户备份。这个印象是错的,但错得有道理——因为有些情况下,升级的感受确实和清空一样。

覆盖安装(升级)不会清数据。系统换掉的是应用的代码和资源,应用私有目录里的文件、数据库、偏好都原样保留。这也是为什么很多应用升级之后,用户不需要重新登录。这一条是系统的固定行为,不是应用自己控制的。

卸载会清空应用私有目录。这是设计如此:卸载意味着用户不要这个应用了,它的数据也不该留在设备上。所以卸载再装,等于全新开始。那些「卸载重装试试」的建议,代价就是这一条。

那么「升级完数据没了」是怎么发生的?答案在第三个地方:数据还在,但应用读不懂它了。

读不懂有四种典型形态。一是数据库版本对不上,新代码要读新结构,旧库里没有那些字段,读出来是空;二是字段类型改了,原来是文本,现在按数字读,直接异常;三是路径变了,文件还在老地方,新代码去新地方找,找不到就当成没有;四是身份变了,包名一改,系统认为这是另一个应用,它的私有目录是全新的,旧数据在新应用眼里根本不存在。

第四种最彻底,也最容易被忽略。它不丢数据,它只是让旧数据变成了「别人的数据」,新应用没有任何权限、也没有任何路径能读到它。用户看到的就是一片空白,而设备上那份数据其实完好无损地躺在那里,直到某天被系统清理掉。

所以判断「升级会不会丢数据」,要看清楚是哪一种情况:只是换了代码和资源,不会丢;动了数据结构、存储路径或者包名,就要专门处理。前一种不需要准备,后三种必须提前设计。

三、数据库版本号:迁移脚本跑在什么时候

本地数据库几乎都有一个版本号,它跟应用的版本号是两件事,但作用类似:告诉代码「这份数据是哪一代的结构」。

这个数字存在数据库文件本身里,不在应用代码里。应用启动、第一次打开数据库的时候,会拿代码里期望的版本号和文件里的版本号比一次,然后分三种情况走不同的路。

情况一:文件里的版本号更低,说明是旧数据。这时候要执行一段升级逻辑,把旧结构改成新结构,改完把版本号写成新的。这段逻辑通常叫迁移,它是唯一一个「必须写一次、以后每次升级都要能跑」的东西。

情况二:文件里的版本号相同,说明结构一致。什么都不用做,直接用。这是绝大多数正常启动的情况。

情况三:文件里的版本号更高,说明数据比代码新。这就是上一节说的「回退之后打不开」。这时候代码不认识这份数据,默认的处理方式往往是直接放弃,也就是抛异常或者清库重建。

第三种情况是最容易被忽略的,因为它只在回退的时候出现。而回退这件事,恰恰是出事那天才会做的。所以「升级要写迁移」是常识,「降级要有兜底」几乎没人写。

兜底其实不复杂,就一句话:遇到更高的版本号时,别清库,也别直接崩,而是按「不认识的字段当作没有」来处理。这样做有风险,因为新结构里的数据可能读不全,但比清空好得多。至少用户打开之后,能看到大部分内容,而不是一片空白。

还有两个细节值得写进规范。一是迁移逻辑要能重复执行。同一台设备可能因为中断、崩溃、杀进程,把迁移跑到一半;下次启动再跑一遍,如果逻辑里带着「加一列」这种不可重复的操作,就会报错。二是迁移之前先备份。把旧库文件复制一份到临时位置,迁移成功再删;迁移失败就回滚。这几行代码,能救回一整批用户的数据。

四、加一个字段,为什么旧数据读出来是空的

这是最常见的一种「数据丢了」。现象是:升级之后,列表还在,但某几列全是空白,或者所有记录的时间都变成了同一个默认值。

原因很直接:新版本的代码读一个新字段,而旧数据里根本没有这个字段。数据库里没有这一列,读出来自然是空。代码如果没有对空值做处理,界面上就是一片空白。

这件事的关键在于:它不会报错。空值和「这一条记录本来就没填」在界面上长得一模一样,开发自测的时候很难分出来。只有拿一份真实的旧数据来跑,才能看出来。

所以迁移这件事的验收,不能只测「新装的应用能不能用」,必须测「旧数据升级上来之后,每一条内容还在不在」。这两件事的差别,就是「功能对不对」和「数据丢没丢」的差别。

具体怎么验?三个动作。第一,在一台装着旧版本的设备上造几条真实数据,最好每条都填满,不要留空。第二,装上新版本,不要卸载。第三,逐条打开,看每一个字段。不是看列表页有没有内容,是点进去看详情页的每一行。

第三个动作最容易被简化成「看一眼列表有数据就算过」。列表页往往只显示三四个字段,而新加的字段通常出现在详情页。只验列表,等于避开了最容易出问题的地方。

还有一种变形:字段没加,但含义变了。比如原来那一列存的是「状态码 1 表示待处理」,新版本改成「1 表示已处理」。数据一个字没变,含义整个反了,用户看到的是所有工单状态都错了。这种错比空白更危险,因为它看起来是对的。

所以每次动数据结构,都应该问一句:这次改动,旧数据读出来会变成什么?答案必须在设计阶段就有,不能等到升级之后由用户来告诉你。

五、包名一改,数据等于从零开始

改包名,是改包里后果最重的一件事。前面专门用一篇讲过它的身份连带,这里只说数据这一面。

应用私有目录的路径,是由包名决定的。系统给每个应用划一块地盘,地盘的地址里就带着包名。包名改了,地盘就换了,新应用拿到一块全新的空地,旧的那块还在,但它不属于新应用了。

这意味着一件很干脆的事:改包名之后,用户原来的一切都不存在了——登录状态、草稿、本地历史、下载的文件。而且不是「读不出来」,是「根本不在同一个地方」。新应用甚至没有权限去看旧目录。

有人会想:那我把旧目录里的东西搬过去行不行?技术上,应用没有权限访问别的应用的私有目录。所以这条路走不通,除非两个应用配合,或者数据本来就在公共位置。

那么改包名之后要迁数据,就只剩三条路。一是让用户重新登录,数据从服务端重新拉,前提是服务端有这份数据;二是导出再导入,旧应用提供一个导出,新应用提供一个导入,中间由用户操作;三是走公共存储做中转,把要带过去的东西先写到用户可见的位置,新应用再读回来,代价是多几步操作,而且要考虑权限。

三条路里,第一条最省事,但它有个前提:数据在服务端。而很多「本地优先」的应用,恰恰把最关键的东西放在本地,比如离线巡检记录、现场拍摄的照片。这些数据上不去,第一条路就断了。

所以改包名之前,必须回答一个问题:用户手上那些只存在于本机的数据,怎么办?这个问题在需求阶段没人问,到了交付现场就变成「为什么我昨天的活全没了」。

我们内部现在的做法是:凡是要改包名的交付,一律先做一次数据盘点,把四层数据全列出来,逐层判断能不能重建。能重建的不管,不能重建的必须给迁移方案。这个盘点大概半天,能避免一整批客户的投诉。

本地数据的四个层次
偏好、结构化数据、文件、缓存,四层对升级和卸载的反应各不相同

六、第一个实例:改包名换品牌,所有人都要重新登录

我们自己维护的门店经营管理平台,做过一次品牌更换:白牌交付给一个新客户,按规矩要改包名,让两个客户的应用互不干扰。

改包名这件事本身很快,界面、图标、名字都换了,装上也没问题。问题出在交付当天:客户那边的店长打开应用,全都要重新登录,而且有一个人的离线盘点记录不见了。

重新登录这件事,我们提前想到了,也准备了说明。但「离线盘点记录」这件事,谁都没想起来。那份记录存在本地数据库里,是店长在仓库没信号的时候一条一条点出来的,还没来得及同步,包一换,它就留在了旧目录里。

那次处理得很难看。旧应用还在那台手机上,但客户已经卸载了;数据在旧应用的私有目录里,新应用碰不到。最后是把旧应用重新装上,让店长把记录导出来,再手工录进新应用。一台设备二十分钟,一共七台。

这件事之后,我们给这类交付加了两步。第一步,交付前发一张通知,明确写清「换包之后要重新登录,未同步的本地数据会留在旧应用里,请先在有网络的地方同步一次」。第二步,旧应用先不卸,保留一周,等确认所有人都同步完了再清理。

第二步听起来笨,但它非常有效。因为「数据没同步」这件事,只有用户自己知道。系统不知道,服务端不知道,我们更不知道。唯一能判断的人就是那个店长,只要旧应用还在,他随时能回去看一眼。

还有一个细节值得记下来:那次之后,我们在新包里加了一个「首次启动提示」,明确告诉用户这是新应用、数据不会自动带过来。提示很短,但它把「为什么空了」这个疑问,变成了「我知道为什么会空」。同样一件事,说清楚了就不算事故,说不清楚就是事故。

七、第二个实例:巡检端加了两个字段,历史记录全是空白

另一个实例是现场巡检端。有一次需求是给巡检项加两个字段:一个是「整改期限」,一个是「复查结果」。加完之后功能都正常,测试也过了。

上线第三天,有客户反馈:历史巡检记录点进去,整改期限那一栏是空的。不是所有记录,是三个月以前的记录全空,最近几天的正常。

原因很快就找到了:那两个字段是新加的列,旧记录里没有值,读出来就是空。这本身是符合预期的——三个月前的记录,当然没有「整改期限」这个概念。

问题在于界面上怎么显示。代码把空值直接显示成了空白格,于是用户看到的是一排没有内容的表格,第一反应是「数据丢了」。如果它显示成「不适用」或者「—」,同一个事实就变成了「这些旧记录没有这项」。

这件事教了我们一个很具体的做法:新增字段的界面,必须定义「旧数据怎么显示」。三个选项:显示为「不适用」、显示为默认值、整块隐藏。三个都可以,唯独不能什么都不做,让它空着。

还有一层更细的:写死的默认值也有风险。如果「复查结果」的默认值设成了「合格」,那么所有三个月前的记录都会显示为合格,而实际上它们从来没有被复查过。这比空白更糟,因为它是错的,而且看不出来。

所以我们后来的规范是:新增字段一律默认为「未填写」,界面显示为「—」,并且在详情页顶部提示一句「此记录创建于该字段上线之前」。三个动作都不复杂,但它们让「数据没有」和「数据是空」这两件事在界面上分开了。

回头看,这次事故的真正成本不在开发,在解释。客户看到空白,第一反应是不信任。我们花了三天时间,逐条对账、出具说明,才让客户相信数据没丢。而这三天,本来可以用三行界面文案省掉。

八、偏好文件的两个坑:类型变了,键名变了

偏好文件是最简单的一层数据,键值对,读写都很快。但它有两个坑,踩上去都不报错,只表现为「设置怎么没了」。

第一个坑:同一个键,类型变了。原来存的是文本,新版本按数字读,很多实现会直接抛异常;如果代码里做了容错,可能就返回一个默认值,于是用户看到的设置回到了默认。这两种结果都不好,后一种更难查,因为它不崩。

规避的办法很简单:要改类型,就换一个键名。新键读不到就回退读旧键,读到之后按新类型写一遍,旧键保留一段时间再清理。多几行代码,换掉一类玄学问题。

第二个坑:键名本身被改了。有人觉得旧键名起得不好,顺手改成更规范的写法,改完自测正常——因为自测的设备是新的,没有旧键。真实用户升级上来,读新键读不到,一切回到默认。症状是「升级之后所有设置都变回默认了」。

这个坑的隐蔽之处在于:新装的用户完全正常。所以测试如果只在全新安装的设备上做,永远发现不了。必须有一台装过旧版本的设备,才能看到。这和上一节说的「旧数据验收」是同一件事。

还有一个不算坑但值得提的细节:偏好文件里不要存敏感信息。它没有加密,任何能读到应用私有目录的手段都能看到明文。登录令牌、手机号、身份证号这类东西,应该放在更安全的地方,或者至少加密后再写。这一点在改包场景里尤其重要,因为改包往往伴随着交付给第三方环境。

总结成一句:偏好文件的所有改动,都要假设「旧值一定存在且格式可能不同」。按这个假设写代码,两个坑都能绕过去。

九、文件没丢,只是找不到了

照片、录音、导出的表格,这些是用户最有感知的数据。它们的丢失往往不是因为被删了,而是因为路径变了。

文件在设备上是以完整路径定位的。应用存一张照片,实际上是把文件写到某个目录下,然后在数据库里记一行:这条记录对应哪个路径。以后要显示,就拿着这个路径去读文件。

所以只要路径规则一变,就会出现一种很别扭的状态:文件还在,记录也还在,但记录里那个路径指向的地方没有东西了。界面上的表现是一张裂图,或者一个转不出来的加载圈。用户说「照片没了」,实际上是「照片找不到了」。

路径变化有几种常见来源。一是应用升级时改了存放规则,比如从「按天分目录」改成「按月分目录」;二是系统版本更新,对外部存储的访问方式收紧,旧路径不再可读;三是改包名,私有目录整体换位置;四是用户手动清理,把整个文件夹删了或者移走了。

对应的处理办法也不同。第一种最好办:迁移的时候扫一遍旧路径,把文件搬到新位置,同时更新数据库里那一行。第二种要适配:在新系统上把文件迁到应用自己的目录里,不再依赖外部路径。第三种只能靠导出导入,前面讲过。第四种没法救,只能做到「找不到时给出明确提示」,而不是一直转圈。

最后一种最值得说。很多应用遇到文件读不到,界面上的处理是「加载中」,然后一直加载。用户等三十秒,退出重进,还是加载。他得到的结论是「这个应用坏了」,而不是「这张照片不见了」。

正确做法是三步:读之前先判断文件在不在;不在就直接显示占位图和一句「文件已不存在」;同时提供一个「重新上传」的入口。三步都不难,但它们把「卡住」变成了「可理解」。可理解的失败,用户能接受;不可理解的失败,用户只会卸载。

十、迁移的三条原则,少一条都会出事

迁移这件事,写起来不复杂,出问题往往是因为少考虑了一种情况。下面三条是我们踩过之后总结的,基本能覆盖绝大多数事故。

第一条,只进不退,但要有兜底。数据的版本号只能往上走,不允许往回改。但代码必须能处理「数据比代码新」的情况——不是清库,而是尽力读、读不到的当空。这一条前面讲过,它是回退能不能成功的关键。

第二条,迁移之前先备份。把旧的数据库文件、偏好文件复制一份到临时目录,迁移成功再删,失败就回滚。这一步的成本是十几行代码,收益是「迁移出错时用户还有救」。没有备份的迁移,一旦失败就是永久性的。

第三条,迁移必须能重复执行。同一台设备可能跑两次:第一次跑到一半被系统杀进程,第二次启动又从头开始。如果迁移里有「加一列」这种操作,第二次就会报「列已存在」然后整体失败。做法很简单:每一步之前先判断「这一步是不是已经做过了」,做过了就跳过。

这三条之外,还有两个实践层面的建议。

一是迁移要在启动时尽早做,且要有一个明确的界面。如果迁移要跑几秒,用户看到的是白屏,会以为卡死。给一个「正在升级本地数据,请稍候」的提示,哪怕只有一秒,也比白屏好。而且这个界面不能跳过——迁移没完成就进主界面,读到的还是旧结构。

二是迁移失败要能上报。成功没有人关心,失败必须留下记录:设备型号、系统版本、旧数据版本号、失败在哪一步。这些信息在排查时是决定性的。没有上报的迁移,等于在黑箱里运行。

最后一句:迁移的复杂度,跟「一次改多少」直接相关。一次只加一个字段,逻辑就三行;一次把三个表重构成一个表,逻辑就是三百行。所以能拆就拆,每次发版只动一小步,让每一次迁移都简单到可以一眼看懂。复杂的迁移不一定错,但它一定难验。

数据迁移的时机与顺序
先备份、再判断、后执行,三步缺一步都可能丢一片数据

十一、验收:六种设备状态,一种都不能少

数据迁移的验收,关键在于「设备处于什么状态」。同一个包,装在不同状态的设备上,走的路径完全不同。下面六种状态,缺一种就等于漏一条路径。

序号设备状态怎么准备验的是哪条路径
1全新安装从没装过这个应用的设备验证初始结构是否正确
2旧版升级装着上一版、有真实数据的设备最核心的一条,必须逐字段看
3跨版升级装着更早的两三个版本、没升过中间版的设备迁移链要能连续跑通
4升级后回退装过新版、再装旧版功能的包数据比代码新时的兜底
5清除数据后启动在系统设置里清过数据的设备相当于全新安装,但残留目录可能还在
6低存储空间升级剩余空间很小的设备迁移中途失败时能否恢复

第三种最容易被忽略。真实的用户不会每次都升级,有人从半年前的版本直接跳到最新版。如果你的迁移只写了「从上一版到这一版」,跨版升级就会漏掉中间几步,结果是字段对不上、数据读不出来。

第六种也很少有人测,但它对应的是最恶劣的场景。迁移跑到一半没空间了,应用是崩掉、是回滚、还是留下一个半新半旧的数据?这个答案在代码里写着,但没人验证过。有了备份和「可重复执行」两条,这种场景通常能自己恢复;没有的话,就是永久损坏。

六种状态里,第二种和第三种要花的时间最多,因为它们需要「造旧数据」。造数据的成本,是这套验收唯一的门槛。我们的做法是准备一个旧版本的安装包和一键灌数据的脚本,每次发版前跑一遍,十分钟就能把六种状态过完。

十二、七个问题,都和「数据还在不在」有关

问题一:升级会清数据吗?不会。覆盖安装只换代码和资源,私有目录原样保留。数据「看起来没了」,几乎都是读不懂,不是被删了。

问题二:用户说数据丢了,第一步查什么?查他装的是哪个包。如果包名变了,那不是丢,是换了一个应用,旧数据在新应用看来不存在。这一步能排掉一半的「丢数据」投诉。

问题三:数据库版本号能不能手动改?能改,但改之前要确定结构真的对得上。改高了,代码不认识;改低了,迁移会重跑一遍。它不是用来「试一试」的东西。

问题四:清缓存会不会清掉登录态?不该会。清缓存只应动缓存目录,不该动偏好和数据库。如果会,那是实现的问题,不是用户的错。

问题五:卸载再装能不能保住数据?不能,除非数据在服务端或者你提前做了导出。「卸载重装试试」这条建议,应该附带一句「本机数据会清空」。

问题六:迁移写好了,怎么知道它对不对?拿真实的旧数据跑一遍,逐字段看。不要用新造的、字段填满的数据,因为真实数据里一定有空的、有异常的、有格式不规范的。

问题七:改包名之后,能不能让旧应用把数据传给新应用?可以,但需要用户操作:旧应用导出,新应用导入。中间的文件要放在两边都能访问的位置。这条路可行,但必须提前做,不能等到交付当天。

十三、一次返工的账:三行文案,三天对账

上一节那个「历史记录全是空白」的事故,值得单独算一遍账。

开发成本:几乎为零。字段是新加的,界面照常显示,测试在全新安装的设备上跑了一遍,一切正常。事故成本:三天。客户发现空白,第一反应是数据丢了,要求核对;我们逐条比对服务端和本地的记录,出具说明。三天里没有一行代码是在修功能,全在修复信任。

如果当时做了三件事,这三天不会发生。第一,用一台装着旧版、有旧数据的设备升级一次,空白立刻就能看到。第二,给空值定义一个显示方式,比如「—」。第三,在详情页加一句「此记录创建于该字段上线之前」。后两件事加起来是三行文案,第一件事是十分钟。

这就是数据类问题的成本结构:发现成本极高,修复成本极低。和崩溃正好相反——崩溃一发生就有人报,修起来可能要改架构;数据问题没人报,改起来只要几行。所以它的问题从来不是「难修」,是「没人发现」。

这也解释了为什么「验收」在这类问题上比「开发」重要。你多写一段迁移逻辑,也许只是让某个字段从空变成有;但你多验一次旧数据升级,能让一整批用户不必经历一次「我的东西没了」。

后来我们把这条经验固化成了一个动作:每次改动涉及数据结构,发版单上必须多一行——「用旧数据升级验证过」,并且写清在哪台设备上、装了哪个版本、造了几条数据。这一行没有具体信息,就不算验过。

升级后的数据核对
旧数据升级要逐字段看,列表页看不出问题

十四、把「不要丢数据」写成一句能执行的话

「这次改包别把用户数据弄丢了」是一句正确的废话。它没有说清改什么、验什么、谁来验。要能派下去,得写成下面这样。

一句能执行的话:这次给巡检端加「整改期限」和「复查结果」两个字段,数据库版本从 4 升到 5;迁移只做加列,加之前先判断列是否存在,保证重复执行不报错;迁移前把数据库复制一份到临时目录,成功后删除;验收用一台装着上一版的设备,先造五条填满字段的记录,再覆盖安装,逐条打开详情页确认原有字段一个不少、新字段显示为「—」;再用一台装着更早版本的设备跨版升级一次。

这句话里有五个要素,缺一个就会出问题。第一,改什么、版本从几到几,写清数字,避免两个人都以为对方改了。第二,迁移的写法约束,明确「可重复执行」「先判断」这些要求,不能只说「写个迁移」。第三,备份与回滚,写清备份到哪儿、什么时候删。第四,验收的设备状态,必须写明「装着上一版的设备」,而不是「一台手机」。第五,新字段的显示方式,这一条最容易被漏,也最影响用户感受。

还有两条值得加进去。一是「谁会看到这份数据」:如果这次改动会影响到导出、打印、报表,那些地方也要一并验,因为它们读的是同一份数据,但走的是另一条代码路径。二是「出问题怎么回退」:数据版本升上去之后,回退包能不能打开?答案要么是能(写了兜底),要么是不能(那就不能回退,只能往前修)。这个答案必须在发版前就有,不能等出事再讨论。

我们内部现在有一句话,专门用在这类需求上:改结构的需求,验收的重点不是「新功能对不对」,是「旧数据还在不在」。两句话听起来近似,落到动作上完全不同:前者点新功能,后者翻旧记录。

十五、三句话,把这件事收住

关于本地数据,能带走的是三句。

第一句:升级不动数据,卸载才清空;所谓「丢了」,多半是读不懂。想清楚这一点,排查方向就不会跑偏——先去查结构和路径,而不是去查「谁删了」。

第二句:改了结构、改了路径、改了包名,三件事都要提前设计迁移。这三样是数据问题的全部来源。改动本身没错,错的是改完就发。

第三句:数据问题的成本在发现,不在修复。修一行显示文案是一分钟,让客户相信数据没丢是三天。所以验收要多花十分钟,去装一台有旧数据的设备。

三句合起来是一件事:数据是用户的东西,不是应用的附属品。应用可以换皮、换名、换代码,但用户在里面存下的东西,应该在每一次升级之后都还在原来的位置。做到这一点,靠的不是小心,是流程里那几个固定的动作。

十六、附:一页速查

下面这张表把全文压成八行,改数据结构或者排查「数据没了」的时候可以对着看。

要点内容注意
四层数据偏好 / 结构化数据 / 文件 / 缓存清缓存只该动最后一层
升级与卸载升级不清数据,卸载清空私有目录「卸载重装」的代价是全部本机数据
读不懂的四种版本对不上、类型变了、路径变了、包名变了最后一种最彻底,旧数据变成别人的
数据库版本低则迁移,同则直用,高则兜底第三种只在回退时出现,最容易被漏
新加字段旧记录读出来是空界面要定义空值怎么显示,别留白
迁移三原则只进不退有兜底、先备份、可重复执行缺一条都可能永久损坏
验收六态全新、升级、跨版、回退、清数据、低空间跨版和低空间最常被漏
成本结构发现贵,修复便宜多验十分钟,少解释三天

最后补一句:上面这些动作,最好和改包放在同一条流程里。改完结构、回编、对齐签名、真机预览,中间少一次手工衔接,就少一次「忘了验旧数据」的机会。安卓修改大师智改工坊把这些环节收在一次操作里,产品介绍页在 这里。

十七、改包之前的六个问题:一份数据盘点清单

凡是改动会碰到数据的交付,动手之前把这六个问题过一遍,大概半小时。它能提前暴露掉大部分「交付当天才发现」的问题。

问题一:这次改动碰不碰包名?碰,就意味着数据彻底隔离,必须给迁移方案或者至少给一份说明。不碰,就继续往下问。

问题二:碰不碰数据结构?加字段、改类型、改含义、拆表合表,都算碰。碰了就要写迁移,并且要定义旧数据显示成什么。

问题三:碰不碰存储路径?换目录、换命名规则、从外部存储搬到应用私有目录,都会让旧文件变成孤儿。碰了就要写搬迁逻辑。

问题四:用户手上有没有只在本地、服务端没有的数据?这一问最关键。有,就必须准备导出或者提示;没有,风险立刻小一半。离线记录、现场照片、草稿,这三样是常见的答案。

问题五:这些数据如果丢了,用户会怎样?答案分三档:无所谓(缓存)、要重做(偏好与登录)、会出事(业务记录与照片)。第三档必须准备迁移和验收,第二档至少要提前通知。

问题六:出问题的时候,谁能第一时间知道?如果没有答案,就说明这件事只能等用户来报。那就至少加一条上报,让「迁移失败」这件事在后台能被看见。

六个问题都过完,会得到一张很短的清单:哪一层要迁、迁到哪里、谁来验、出问题怎么回退。这张清单不需要很长,但它必须在动手之前存在。我们现在的做法是把它作为交付单的第一页,没有这一页,交付单不往下走。

最后提醒一句:这六个问题里,最容易给出错误答案的是第四个。因为「服务端有没有」这件事,开发和业务的理解经常不一样:开发觉得「接口里有」,业务觉得「我本地能看到的就是我的」。判断标准只有一个:断网之后,这条数据还在不在。在,它就是本地的,就必须管。

十八、用过的人怎么说

换包名交付那天,七台设备全要重新登录,还有一份没同步的盘点记录留在旧应用里。现在交付前先发通知,旧应用留一周再卸。

—— 阿凯 · 门店端开发

加了两个字段,历史记录一片空白,客户以为数据丢了。后来把空值统一显示成「—」,再没人问过。

—— 老宋 · 现场系统负责人

数据库版本从 4 升到 5,只测了新装。结果跨版升级的用户字段全错。现在验收必须有两台旧设备。

—— 方工 · 移动端测试

迁移跑到一半被系统杀了,第二次启动报「列已存在」,直接进不去。加了「先判断再做」之后才稳。

—— 陶然 · 交付工程师

清缓存把登录态清了,用户天天要重新登录。查出来是缓存目录和偏好目录写混了,改完就好了。

—— 李工 · 内部工具维护

最有用的是「读不懂不等于丢」。以前一出这问题就慌,现在先去看包名和数据库版本。

—— 陈默 · 企业应用维护

这一篇里的两个实例,都是我们自己维护的应用:一个门店经营管理平台,一套现场巡检端。数据这件事的特点是:做对了没人夸,做错了没人报,等有人报的时候已经过去好几天。所以文中那些设备状态、迁移原则和验收动作,都是从「怎么让问题当天被看见」这个角度长出来的。

合规提醒:本文所有实例均针对自有版权或已获授权的应用,用于企业内部维护、白牌交付与合法定制。请勿通过改动包名、签名或存储路径读取他人应用的数据,也不要为未授权应用制作可绕过数据隔离的版本。改包前请确认你对目标应用及其数据拥有相应权利,并遵守个人信息保护的相关要求。

用户不会记得你的界面改过什么,但他一定记得自己的东西丢没丢。这是应用和用户之间最基础的一次信任,也是最不该省的那一步。

安卓修改大师智改工坊能做的,是把改包之后那一串动作一次落到位:改完回编、对齐签名、真机预览,装上去就能看到旧数据还在不在。说清楚,就够了。

只需说话,就能让应用变成你想要的样子

安卓修改大师智改工坊 · 数据结构、包名、存储路径,一句话就改

自动回编 · 对齐签名 · 真机预览 · 每一处改动可追溯

立即下载智改工坊(AI 版)

Windows 7 SP1 及以上 · 产品介绍页 https://www.apkeditor.cn/ai-version.aspx