【问题标题】:Firebase How to increment counter while offline?Firebase 如何在离线时增加计数器?
【发布时间】:2017-01-12 16:35:48
【问题描述】:

在我的 Firebase Android 应用程序中,每次用户喜欢一个帖子时,我需要在该用户和帖子帖子之间创建一个链接,并增加该帖子的“点赞数”(这个数字可以> 10000 或更多)。
根据文档。这里:https://firebase.google.com/docs/database/android/read-and-write#save_data_as_transactions
我可以使用事务来增加这个计数器。 但我想启用离线容量。

问题:在 Firebase 文档中写道“Transactions are not persisted across app restarts”。

那么我该如何管理这个用例:“一个用户在离线时喜欢 20 个帖子然后停止应用程序”?

【问题讨论】:

  • 是否可以在这里帮助自己使用 SharedPrefs?就像存储键值对以便您在下一次网络连接时理解它会读出这些并将它们发布到 Firebase 一样?
  • 管理许多用例可能会变得很复杂
  • 同意。我只是在想一些开箱即用的解决方案。
  • 这很关键

标签: android firebase firebase-realtime-database


【解决方案1】:

由于事务需要访问字段的当前值以确定字段的新值,因此在未连接到数据库服务器时无法运行事务。

Firebase 客户端不会在应用重新启动时保留事务,原因相同:事务的概念在用户未连接时无法正常工作。

如果您想在未连接时记录用户操作,您应该将其存储在数据库中:用户操作。

因此,您可以为用户保留likedPosts 的列表,而不是尝试通过事务增加likeCount

likedPosts
  uidOfTooFoo
    post1: true
    post3: true
  uidOfTooPuf
    post2: true
    post3: true

使用这种结构,您不需要交易来增加计数器,因为每个用户基本上都与其他人隔离。

或者,您可以保留类似操作的队列:

likesQueue
  -K234782387432
    uid: "uidOfPoofoo"
    post: post1
  -K234782387433
    uid: "uidOfPuf"
    post: post2
  -K234782387434
    uid: "uidOfPuf"
    post: post3
  -K234782387434
    uid: "uidOfPoofoo"
    post: post3

使用最后一个结构,您将启动一个使用此队列的小型后端服务(通过侦听child_added 事件,或者最好使用firebase-queue),然后增加共享计数器。

【讨论】:

  • 要使用 firebase-queue 我需要第三方服务器(使用 node.js)吗?此功能是否与 firebese 集成?
  • 使用 Firebase Queue 时,工作进程确实会在节点服务器上运行。您还可以在客户端上运行一个简单的 child_added 侦听器,但随后您将接近使用 @DavidK 评论的 SharedPrefs。
  • 我希望有一天 Firebase 将提供一个集成的触发服务器(即,当一个值保存在数据库中时,可以直接增加一个计数器。这也可以简化计数器上的一些写入规则访问跨度>
  • 我们现在可以为此使用 Firebase 函数。但是只有在客户端再次联机后,客户端才能使用更新的共享计数器。因此,如果我们想在离线时向用户显示计数,这无济于事。有什么解决办法吗?
  • 在这个意义上没有任何改变。如果您想优雅地处理用户离线的情况,您通常应该将用户的操作存储在数据库中。然后,当用户重新在线时,您可以使用云函数来聚合这些操作。
【解决方案2】:

一个简单的方法是使用WorkManager运行事务:

WorkManager API 可以轻松安排可延迟的异步任务,即使应用退出或设备重新启动,这些任务也会运行。

所以您的交易保证可以运行!

示例代码:

public class TransactionWorker extends Worker {

private static final String TAG = "TransactionWorker";

private boolean success = false;
private boolean started = false;

public TransactionWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
    super(context, workerParams);
}

@NonNull
@Override
public Result doWork() {

    //database path
    String path = getInputData().getString("path");
    //increment amount
    long increment = getInputData().getLong("increment",0);

    DatabaseReference ref;
    try{
        //if database reference does not exist yet
        // (e.g. app not closed or database not started yet)
        ref = FirebaseDatabase.getInstance().getReference(path);

    }catch (NullPointerException error){
        error.printStackTrace();
        return Result.retry();
    }

    //wait while the transaction did not succeed yet
    while(!success){

        //only sleep if the transaction already started
        if(started){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            continue;
        }

        //start transaction only if not started yet
        ref.runTransaction(new Transaction.Handler() {
            @NonNull
            @Override
            public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
                long value = 0;

                try{
                    value = mutableData.getValue(Long.class);
                }catch (Exception e){
                    e.printStackTrace();
                }

                //increment value
                mutableData.setValue(value + increment);
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(@Nullable DatabaseError databaseError, boolean b, @Nullable DataSnapshot dataSnapshot) {

                Log.d(TAG, "onComplete: " + b);
                success = b;

            }
        });
        started = true;
    }

    //work succeeded
    return Result.success();
}}

来自您的活动:

public static void doIncrementTransaction(String path,final long increment){

    Constraints constraints = new Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED).build();

    Data transactData = new Data.Builder().putString("path",path)
            .putLong("increment",increment).build();

   OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.
            Builder(TransactionWorker.class).setInputData(transactData)
            .setConstraints(constraints)
            .setBackoffCriteria(BackoffPolicy.LINEAR,3000, TimeUnit.MILLISECONDS)
            .build();

    WorkManager.getInstance(this).enqueue(oneTimeWorkRequest);
}

就是这样。

希望对你有帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-25
    • 2012-03-20
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多