Android端推送至Azure Mobile Apps数据库时数据变更丢失问题
Android Azure Mobile App Offline Sync Update Issues with Node.js Easy Tables
我来帮你分析下这个离线同步的问题,看起来你已经踩了几个Azure离线同步的常见坑,咱们一步步拆解解决:
首先,解释两种Pull方式的差异
你遇到的两种Pull行为是正常的:
- 当调用
pull(null).get()时,因为没有指定查询ID(第二个参数),Azure会执行全量拉取,直接用远程数据库的内容覆盖本地所有数据,这就是为什么你的本地变更会丢失。 - 而
pull(null, "inventory").get()使用了固定的查询ID,这会触发增量同步——Azure只会拉取自上次同步以来远程更新的数据,不会覆盖本地未同步的变更,所以你的本地修改能保留,但问题出在Push环节没生效。
排查Push失败的核心原因
你的本地变更没同步到线上,大概率是Push操作其实失败了,但你用.get()阻塞调用时没捕获异常,导致没发现问题。先修改代码捕获Push的错误:
try { syncContext.push().get(); Log.d("SyncStatus", "本地变更已成功推送到Azure"); } catch (Exception e) { Log.e("SyncError", "Push失败,错误信息:", e); }
常见的Push失败原因包括:
1. 实体缺少必要字段
Azure Easy Tables依赖几个系统字段来处理更新:
id:必须是从本地同步表中获取的原始实体ID(不能是你手动生成的无效ID)__version:乐观并发控制的关键字段,本地更新时这个字段会被标记,同步时Azure会对比这个版本号,如果远程版本更高,会抛出冲突异常
确保你要更新的Inventory对象是从InventoryTable查询出来的实例,而不是新建的对象,且包含这两个字段。
2. Easy Tables的更新路由被限制
检查你Azure后台的Node.js Easy Tables代码,默认的更新路由应该是这样的:
table.update(function (context) { return context.execute(); });
如果这个路由被删除、添加了权限验证(比如未登录用户无法更新),或者自定义逻辑阻止了更新,都会导致Push失败。
3. 乐观并发冲突
如果在你离线修改期间,远程数据库的这条记录已经被其他人修改过,Push时会抛出MobileServicePreconditionFailedException。你需要处理这种冲突,比如合并本地和远程的变更:
try { syncContext.push().get(); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause instanceof MobileServicePreconditionFailedException) { MobileServicePreconditionFailedException conflictEx = (MobileServicePreconditionFailedException) cause; Inventory remoteItem = conflictEx.getItem(); Inventory localItem = conflictEx.getLocalItem(); // 自定义合并逻辑:比如保留本地修改的location字段 remoteItem.setLocation(localItem.getLocation()); // 更新本地为合并后的版本,重新Push DataHolder.getInstance().InventoryTable.update(remoteItem).get(); syncContext.push().get(); } }
正确的同步流程建议
- 始终使用带固定查询ID的Pull:比如
pull(null, "inventory_sync"),确保增量同步,避免覆盖本地变更 - 先Push再Pull:你的顺序是对的,但必须确保Push成功后再执行Pull,否则可能拉取到旧数据覆盖本地
- 避免直接用.get()阻塞线程:建议用异步回调或者Coroutines(如果用Kotlin),避免主线程阻塞,同时更容易捕获异常
验证本地同步状态
可以通过以下代码查看是否有待同步的本地操作,确认Push是否真的提交了变更:
List<MobileServiceTableOperation> pendingOps = syncContext.getPendingOperations().get(); Log.d("PendingOps", "待同步操作数量:" + pendingOps.size());
如果Push后这个数量不为0,说明Push确实失败了,需要根据错误信息排查。
内容的提问来源于stack exchange,提问作者Rixium
相关产品推荐
相关产品推荐

