出海日记——单机版应用如何做数据同步

·· 0 · 0 ·
Google Play 开发日记 flutter
超级浩码
作者
超级浩码
数字手艺人,独立开发体验师。
目录


如果你是刚入门 独立开发 ,你可能有过这样的思考,我做的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




评论