从AsyncTask的事务访问Firebase数据库

时间:2016-09-27 13:07:20

标签: android firebase android-asynctask transactions firebase-realtime-database

我需要从正在进行交易的asynctask访问Firebase数据库。

所以我的设置是,我有一个AsyncTask。在其doInBackground方法中,我调用另一个方法,其中包含运行事务的逻辑。在事务的doTransaction方法中,我需要从不同的路径访问数据并生成新数据(如某些统计信息)。

它看起来像这样:

public class MyAsyncTask extends AsyncTask<String,Void,Void> {

    @Override
    protected Void doInBackground(String... params) {
        String para1 = params[0];
        String para2 = params[1];

        performCalculations(para1,para2);

        return null;
    }

    public void performCalculation(String para1, String para2) {
        DatbaseReference ref = FirebaseDatabase.getInstance().getReference().child(para1);

        // Run the transaction here
        ref.runTransaction(new Transaction.Handler() {
            @Override
            public Transaction.Result doTransaction(MutableData mutableData) {

                 TaskCompletionSource<DataSnapshot> taskCompletionSource = new TaskCompletionSource<>();

                 DatabaseReference ref2 = FirebaseDatabase.getInstance().getReference().child(param2);

                 ref2.addListenerForSingleValueEvent(new MyValueEventListener(taskCompletionSource));

                 try {
                    dataSnapshot = Tasks.await(taskCompletionSource.getTask());

                    // DOING SOME AWESOME THINGS HERE 
                    // And updating the mutable Data


                } catch (ExecutionException | InterruptedException e) {
                    Log.v(TAG, "Exception Catched");
                    e.printStackTrace();
                }


                mutableData.setValue(stats);
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
                if (databaseError != null) {
                    Log.d(TAG, "onComplete: Error: " + databaseError.getMessage());
                } else {
                    Log.d(TAG, "onComplete: Transaction was successful");

                }
            }

        }
    }

    private class MyValueEventListener implements ValueEventListener {

        private final TaskCompletionSource<DataSnapshot> taskSource;

        public MyValueEventListener(TaskCompletionSource<DataSnapshot> taskSource) {
            this.taskSource = taskSource;
        }

        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            Log.v(TAG, "Looks like MyValueEventListener received the data");
            taskSource.setResult(dataSnapshot);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            Log.v(TAG, "Looks like MyValueEventListener thrown an error");
            taskSource.setException(databaseError.toException());
        }

    }

}

将日志放在一处,我遇到了它到达Tasks.await语句。但是后来永远不会调用onDataChanged回调。

在那一点上停滞不前。没有错误。没有例外。

最大的问题是,从那时起,它会停止整个应用程序的所有回调。我的所有活动都被屏蔽了,因为它永远不会调用onDataChanged。看起来它在内部崩溃或被线程阻塞。

发生了什么事?感谢。

修改

因此,为了从事务中获取ref的最新数据,我已经将该变量作为该类的一部分。并将值事件侦听器附加到它。所以它会一直保持更新。但是,如果在触发值事件侦听器之前,脱机排队事务将开始运行,该怎么办?离线一段时间后上线后首先会执行什么优先级?

这是我更新的代码:

public class MyAsyncTask extends AsyncTask<String,Void,Void> {

    DataSnapshot mDataSnapshot;

    ValueEventListener mListener;

    DatabaseReference ref2;

   @Override
    protected Void doInBackground(String... params) {
        String para1 = params[0];
        String para2 = params[1];

        performCalculations(para1,para2);

        return null;
    }

    public void performCalculation(String para1, String para2) {

            TaskCompletionSource<DataSnapshot> taskCompletionSource = new TaskCompletionSource<>();

            ref2 = FirebaseDatabase.getInstance().getReference().child(param2);

            ref2.addListenerForSingleValueEvent(new MyValueEventListener(taskCompletionSource));

            try {
                    // Get the data first time before you proceed
                    dataSnapshot = Tasks.await(taskCompletionSource.getTask());

             } catch (ExecutionException | InterruptedException e) {
                    Log.v(TAG, "Exception Catched");
                    e.printStackTrace();
             }

             // Register the value event listener to keep the data updated
             mListener = new ValueEventListener() {
             @Override
             public void onDataChange(DataSnapshot dataSnapshot) {
                mDataSnapshot = dataSnapshot
                Log.v(TAG, "Data updated");
             }

             @Override
             public void onCancelled(DatabaseError databaseError) {

             }
         };

        ref2.addValueEventListener(mListener);


        DatbaseReference ref = FirebaseDatabase.getInstance().getReference().child(para1);

        // Run the transaction here
        ref.runTransaction(new Transaction.Handler() {
            @Override
            public Transaction.Result doTransaction(MutableData mutableData) {

                 // Perform some stuff based on mDataSanpshot



                mutableData.setValue(stats);
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
                if (databaseError != null) {
                    Log.d(TAG, "onComplete: Error: " + databaseError.getMessage());
                } else {
                    Log.d(TAG, "onComplete: Transaction was successful");
                    ref2.removeEventListener(mListener);
                }
            }

        }
    }

    // My value event listener here ... 

}

2 个答案:

答案 0 :(得分:1)

您可能会从Firebaser获得更明智的答案,例如FvP。在那之前,我会提出我很确定你在runTransaction()中等待的任务正在造成死锁。您可以使用与此类似的Log语句查看回调运行的线程:

Log.i(TAG, "doTransaction: " + Thread.currentThread().getName());

doTransaction()FirebaseDatabaseWorker主题上运行。监听器回调在主(UI)线程上运行,并且(我猜)是由从DB工作线程调度的事件驱动的。

阻止数据库工作线程将导致侦听器回调永远不会触发。任务永远不会完成,因此永远不会释放Task.await()

答案 1 :(得分:1)

我还没有对TaskCompletionSource做任何事情,所以不要马上看到那里发生了什么。但总的来说,我试图避开这样的结构,因为它们使代码复杂化而没有显着的好处。

Firebase数据库客户端在单独的线程上执行所有网络和磁盘I / O,然后在主线程上显示对方法的调用。通过使用它并重新排序一些调用,我认为你可以废除很多代码:

public void performCalculation(String para1, String para2) {
    DatbaseReference ref = FirebaseDatabase.getInstance().getReference().child(para1);
    DatabaseReference ref2 = FirebaseDatabase.getInstance().getReference().child(para2);

    ref2.addListenerForSingleValueEvent(new ValueEventListener() { }
        public void onDataChange(DataSnapshot dataSnapshot) {
            ref.runTransaction(new Transaction.Handler() {
                public Transaction.Result doTransaction(MutableData mutableData) {
                    // DOING SOME AWESOME THINGS HERE 
                    // And updating the mutable Data
                }
                public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
                    if (databaseError != null) {
                        Log.d(TAG, "onComplete: Error: " + databaseError.getMessage());
                    } else {
                        Log.d(TAG, "onComplete: Transaction was successful");
                    }
                }
            });
        }
    );