出海日记——单机版应用如何做数据同步
目录
如果你是刚入门 独立开发 ,你可能有过这样的思考,我做的APP会不会有人用? 刚开始会不会有盈利? 有没有必要把数据保存在服务端?
其实在一切条件都允许的情况,我认为一个应用必然 要有一个服务器的,有了服务器,用户的数据更安全不会因为更新卸载应用而导致数据丢失,同时也方便开发者通过观察用户数据去更新和迭代应用。也能更好的对应用进行控制,如权限,公告,内购的折扣等等。
当然,服务器也会带来很多额外的成本,比如运维,安全,服务器费用,接口开发等等。你需要考虑服务器的配置,一般双11各大云厂商都有2~5折优惠,但是续费时吓死人,同时你可能也要考虑避免服务器被攻击等问题。
以我个人为例,开发了10来个工具类的应用。有时候为了更好的增加用户粘性,也会增加像打卡签到,用户日常数据的记录。有趣的是,我的这些应用都没有接服务器,原因就像上面所说。刚开始我更希望的是,APP开发完成了,就不再需要额外的维护成本,除非我想加新的功能。这样我才有更多时间去专注下一个APP, 虽然干了9年后端,前期还是不想有额外的负担。
进入正题,如果你和我想法一样,那么单机版应用要如何做数据同步?答案是,做云同步或者说类似于用用户自己的网盘去同步。接下来说一下我的
Flutter方案。
目前我的APP都发布在 Google Play,所以这里也是举例海外的一套方案,你完全可以照葫芦画瓢开发一套适应国内的方案。不过我建议如果你想针对你的某个APP发力做精品还是接个服务器会比较好。
Google OAuth + Drive这是一套谷歌方案,你可能常常看到国外的APP上看到同步的功能,一般先登录谷歌账号,之后定期自动同步。虽然不清楚老外怎么实现的,用没用上服务器。但是我自己摸索的这套方案,也能做到同样的效果,也不需要维护服务器。
可能有人会有疑问,Drive只是一个网盘怎么备份应用的数据。其实方法也不难,一般工具类APP,即使记录了一年的用户数据,数据量也是非常小的。可以写一个工具类把数据转成json存储到文件里,再写个方法把json解析成数据对象回写到你的存储引擎。或者更省事,你可以使用sqlite,直接把整个sql文件给上传了。
步骤 #
打开 Google Cloud Platform API manager [https://console.cloud.google.com/apis/dashboard],点击启用API和服务
搜索drive关键期,选择Google Drive API添加
添加API凭据,这里我是通过
Firebase自动生成。
所以也可以去
Firebase中创建项目(之后我会写篇Firebase如何创建项目的文章)
创建完项目和应用后,需要在Authentication模块中加入登入验证方式。
要求填入Oauth客户端ID
这是可以返回刚才Google Cloud Platform API manager [https://console.cloud.google.com/apis/dashboard],已经自动创建的Oauth客户端信息复制ID和密钥。
到这,配置就差不多了,来看下主要代码
在pubspec.yaml中添加依赖:
google_sign_in: ^6.1.4
googleapis: ^11.2.0
创建用来登录的变量
import 'package:googleapis/drive/v3.dart' as drive;
import 'package:google_sign_in/google_sign_in.dart';
GoogleSignInAccount? account;
...
// 调用此方法可以让APP出现登入弹窗
account = await GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope])
.signIn();
// 注销登入
await GoogleSignIn.standard(scopes: [
drive.DriveApi.driveScope
]).signOut();
根据自己的业务逻辑使用登入登出功能。
到这里我们拿到了用于调用drive接口所需的验签。
再来看一下drive上传和下载的方法。
import 'package:googleapis/drive/v3.dart' as drive;
// 获取driveApiClient
final driveApi =
drive.DriveApi(GoogleAuthClient(await account!.authHeaders));
// 上传db文件
final dbMedia =
drive.Media(File(dbPath).openRead(), File(dbPath).lengthSync());
final file = drive.File()
..name = dbPath.split('/').last
..parents = [parentFolderId]
..description = dbVersion.toString();
await driveApi.files.create(file, uploadMedia: dbMedia);
// 下载本地更新
final dbMedia = await driveApi.files.get(dbFileId,
downloadOptions: drive.DownloadOptions.fullMedia) as drive.Media;
List<int> contentBytes = [];
await dbMedia.stream.listen((bytes) {
contentBytes.addAll(bytes);
}).asFuture();
await File(dbPath).writeAsBytes(contentBytes, flush: true);
Logger().i('本地更新');
// 封装一个带验签的http client
class GoogleAuthClient extends BaseClient {
final Map<String, String> headers;
final Client client = Client();
GoogleAuthClient(this.headers);
@override
Future<StreamedResponse> send(BaseRequest request) {
return client.send(request..headers.addAll(headers));
}
}
这里只演示如何上传和下载,同步逻辑请根据自身需要来实现。
如果你是
安卓开发,那么需要使用Android对应的SDK即可,配置应该都是一样的。
强调一下,我在使用sqlite的时候,其实使用了多个db文件。原因是我不希望所有的数据都需要同步。例如下次我们开发了一个新的功能,在数据库中增加了新字段B来控制或记录这个功能。这是如果用全套db同步的方案,B字段会因为同步而消失从而导致问题。
不是所有的数据都需要同步备份,精简出业务所需的用户功能数据来进行备份,这样才能减少问题。所以具体怎么同步根据不同的需求去设计。
引用 #
https://pub.dev/packages/google_sign_in
https://pub.dev/packages/googleapis
https://cloud.google.com/docs/authentication?hl=zh_CN&_ga=2.156109883.-1604196893.1667810752
https://firebase.google.com/docs/flutter/setup?hl=zh-cn&platform=android