基于 Android 的文件同步设计方案_android同步文件夹(1)

/** Check is file not changed remotely by last modified time. */
private fun isFileNotChangedRemotely(
syncState: NoteLastSyncState,
remoteFile: CloudResource
): Boolean = syncState.serverLastModifiedTime != null
&& remoteFile.lastUpdate != null
&& !remoteFile.lastUpdate.after(syncState.serverLastModifiedTime)

最后值得一提的字段是 lastSyncMd5,顾名思义,它是文件的 md5 值,是在文件被写入到本地磁盘之后记录到数据库中的。使用该字段,在远程和本地文件的 md5 不一致的时候,我们可以和之前的方案一样,判断文件是本地还是远程的文件发生了改动。

3、同步方案

3.1 流程图

整个流程图比较长,大致可以几个部分,我已经在图中标出。

顶部是对之前生成的一些文件的删除和对图片信息的同步,属于本软件特有的部分,可以忽略。然后是整体的循环结构。流程图比较复杂,实际编码会清晰一些。即,我是通过 BFS 算法遍历本地文件树进行同步的。在对目录进行遍历的时候会先读取其对应的服务器目录下所有文件的状态以及本地存储的所有子文件的状态到 remoteFiles。然后,通过对比本地的文件状态和远程的文件状态进行同步。一个文件或者目录同步完成之后会从 remoteFiles 中移除。

runBackground(onFinished, onInterrupted) { failures ->
val visitors = mutableListOf(File(path))
val count = AtomicInteger(0)
while (visitors.isNotEmpty() && !interrupted) {
try {
val directory = visitors.removeAt(0)
val dirRelativePath = sm.relativePathOf(directory.path)

// Read contents of directory from cloud.
val listResult = server.list(dirRelativePath)
val remoteFiles: MutableMap<String, CloudResource> = if (listResult.isFailed) {
log { “failed to read contents of directory [ d i r R e l a t i v e P a t h ] f r o m c l o u d , c o d e [ dirRelativePath] from cloud, code [ dirRelativePath]fromcloud,code[{listResult.code}], msg [${listResult.message}] .” }
val synced = syncDirectoryWhenFailedReadRemotely(directory, failures)
if (synced) {
continue
}
mutableMapOf()
} else {
insertDirectoryLastSyncState(directory)
listResult.data.toMutableMap()
}

// Read last sync records from local database.
val syncRecords = mutableMapOf<String, NoteLastSyncState>()
DB.get().noteLastSyncStateDao().getByParent(dirRelativePath).forEach {
syncRecords[it.path ?: “”] = it
}

// Travel under directory and handle files.
directory.listFiles()?.forEach { file ->
if (interrupted) {
return@forEach
}

val fileRelativePath = sm.relativePathOf(file.path)

// Files should be ignored.
if (fileRelativePath == “/ S E T T I N G F O L D E R N A M E / SETTING_FOLDER_NAME/ SETTINGFOLDERNAME/SETTING_IMAGE_MODEL_DATA”) {
return@forEach
}

val syncRecord = syncRecords[fileRelativePath]

if (file.isDirectory) {
visitors.add(file)

// The directory exists in cloud: remove from remote files.
if (remoteFiles.containsKey(fileRelativePath)) {
remoteFiles.remove(fileRelativePath)
}
} else if (file.isFile) {
Thread.sleep(timeDelayMillis.toLong())
val remoteFile = remoteFiles[fileRelativePath]

// Sync a single file.
syncFile(file, syncRecord, remoteFile, failures)

if (remoteFile != null) {
remoteFiles.remove(fileRelativePath)
}
notifyProgressChanged(count.addAndGet(1), onProgress)
}
}

// Handle left remote resources.
syncRemoteResourcesNotFoundLocally(remoteFiles, failures, count, onProgress)
} catch (t: Throwable) {
t.printStackTrace()
log { “failed to sync folder with exception: $t” }
}
}
}

remoteFiles 剩下的部分就是远程存在而本地不存在的文件或者目录。它们又可能存在几种情况,被本地删除、远程新增或者被本地移动到其他目录。然后,再根据数据库中的状态记录,对三种情况进行判断。

具体同步流程代码比较长,不便于贴出,后续我会将文件同步逻辑提取出来,开源出一个通用的框架。

3.2 类设计

由于后续考虑支持更多的云服务器,所以,在新的同步方案中,我也对类结构进行了设计。首先是针对服务器的设计,

/** 云同步服务器接口封装 */
interface ICloudServer {

/** 读取文件内容 */
fun readText(rp: String): Resources

/** Write text to given file with relative path [rp]. */
fun writeText(text: String, rp: String): Resources

/** Read bytes of a cloud file. */
fun readBytes(rp: String): Resources

// …
}

这个类中定义了服务器需要实现的方法。比如,WebDAV 对应的实现是 WebDAVServer. 当后续需要支持 OneDrive 同步的时候,基于该接口进行实现即可。

另外是同步工作类,也是以上流程图逻辑存在的地方。这里定义了 ICloudSyncWorker 这个接口,

interface ICloudSyncWorker {

/**

  • Sync a file.
  • @param file the file to sync
  • @param syncRecord note last sync state, might be null
  • @param remoteFile the file info in cloud server
  • @param failures the failures to report, failures will be added to this list.
    */
    fun syncFile(
    file: File,
    syncRecord: NoteLastSyncState?,
    remoteFile: CloudResource?,
    failures: MutableList<ISyncManager.SyncFailure>
    )

// …
}

ICloudSyncWorker 中再引用 ICloudServer 进行网络请求。这样,我们就提高了以上同步流程的拓展性。类结构如下,

总结

根据以上分析和实际测试结果,第一次同步的时候,两个方案速率相近,而第一次同步完成之后,新的方案效率就高得多。因为第一次同步的时候,两种同步方案可能都需要对远程的全部文件进行拉取。而第一次之后,新的同步方案只需要判断文件的上次修改时间,因此请求的数量和所有目录、子孙目录的数量相近(每次至少请求一次目录下的文件/目录信息)。实际测试结果表明,600 个文件同步一次只需要 60s (其中,为避免向服务器请求过于频繁,每个文件处理延时时间为 50ms).

以上就是基于 Android 系统的文件同步设计思路的分享。

Android 学习笔录

**Android 性能优化篇:https://qr18.cn/FVlo89
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 车载篇:https://qr18.cn/F05ZCM

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

且会持续更新**

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-x2AAeqB9-1712756859296)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值