导致mutableData.setValue()的递归导致在backstack中生成相同的活动?

时间:2016-09-30 16:30:23

标签: android firebase firebase-realtime-database

编辑:2016年10月23日未解决,我仍在寻找答案。我将重写这个问题,使其更加清晰,因为我现在知道造成这个问题的原因。

编辑:2016年10月26日:发现的事情:在尝试找到问题时,我收到了一个帮助我找到问题的错误。事实证明,如果我在Firebase数据库中有这个错误:

c:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -ExecutionPolicy RemoteSigned -Command .\.git\hooks\pre-commit.ps1

而不是

Campaigns{
   UNQ_KEY: 1 //This is being set in the transaction
}

问题不会发生

因此,总之,它可能是一个递归错误。

我有这个Firebase交易:

Campaigns{
   UNQ_KEY:{
    count: 1 //this is being set in the transaction
  }
}

这一行:

database.runTransaction(new Transaction.Handler() {
        @Override
        public Transaction.Result doTransaction(MutableData mutableData) {
            Long preUserIncrementedInt = Long.parseLong(b.getText().toString());
            Long userIncrementedInt = ++preUserIncrementedInt;
            mutableData.child("users").child(getUid()).child("count").setValue(userIncrementedInt);
            Long preIncrementedTotalCount = mutableData.child("count").getValue(Long.class);
            Long incrementedTotalCount = ++preIncrementedTotalCount;
            mutableData.child("count").setValue(incrementedTotalCount);
            return Transaction.success(mutableData);
        }

        @Override
        public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
            if (databaseError != null)
                Log.wtf(TAG,databaseError.getMessage());
        }
    });

和这一个:

mutableData.child("users").child(getUid()).child("count").setValue(userIncrementedInt);

当我运行事务时,会再次创建相同的活动并打开。当我单击后退按钮时,它将转到backstack中的上一个活动。但是,后台的先前活动本身就是同一活动。像这样:

IMAGE

每次单击按钮,都会在backstack中生成一个新活动(有问题的活动)。

为了向您展示它的样子,这里是一个GIF:

GIF

为什么会这样?

4 个答案:

答案 0 :(得分:1)

它会重新启动您的活动,因为它会在某处崩溃。试试这段代码:

public void onClick(View v) {
    if (mp == null){
        mp = new MediaPlayer();
    } else {
        mp.reset();
    }
    try {
        AssetFileDescriptor afd;
        afd = getAssets().openFd("click.mp3");
        mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
        mp.prepare();
        mp.start();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalStateException e){
    } catch (IOException e){
    } catch (Exception e){
    }
    v.setEnabled(false);
    final View clicked = v;
    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            clicked.setEnabled(true);
        }
    };
    handler.postAtTime(runnable,System.currentTimeMillis()+100);
    handler.postDelayed(runnable,100);

}

答案 1 :(得分:1)

(我会在这里张贴,因为它更容易管理我的评论)...

找不到解决方案。

*更新4:找到解决方案**

经过多次尝试和大量评论后,终于找到了解决方案,显然OP在另一个类中有增量方法,并将其移动到用于解决问题的同一个类。

至于为什么会发生这种情况,在我看来,也许它与交易上的并发问题有关,也许问题在于它自己创建了一个循环。您的活动开始了,稍后您实例化FirebaseController,在某些时候,它会启动increment方法,最终执行异步执行(以某种方式失败?)并再次启动您的活动< /秒>



请参阅下面的失败(但是问题排查步骤)尝试


  

我试着调试。它没有在任何这些线上激活。它需要   我来了一堆Android文件(比如View.java)。当我&#34;跑到   光标&#34; (跳过下一个调试断点),它重新启动。

您是否可以尝试检查runnable中单击的视图是否为空?

Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // if not null do this
            clicked.setEnabled(true);
            // if null print some log
            // ...
        }
    };

您在调试时是使用Step Into还是Step over(如果您深入了解可能使用Step Into(F5)的类的层次结构,是否可以尝试使用Step Over(F6)进行调试)?

如果我们找到答案,我会在这里发布:

更新1:

MutableData#setValue(java.lang.Object) documentation解释了这是如何工作的:

  

将此位置的数据设置为给定值。原生类型   此方法接受的值对应于JSON类型:

     

Boolean
  Long
  Double
  Map<String, Object>
  List<Object>

     

此外,您可以将自己的类的实例设置到此位置,前提是它们满足以下约束条件:

     

该类必须具有不带参数的默认构造函数   class必须为要分配的属性定义公共getter。   没有公共getter的属性将设置为其默认值   当一个实例被反序列化时

尝试使用其他开发人员建议的其他数据类型:

#setValue参数更改为上面列表中的对象,#getValue 也返回一个对象,尝试强制转换为正确的类型,也许你可以使用Integer

更新2:

您的数据架构看起来像这样吗?

{
    "campaings": {
        "key": {
            "count": "1",
            "users": {
                "-JRHTHaIs-jNPLXOQivY": {
                    "count": "1",
                    ...
                },
                ...
            }
            ...
        },
        "other-key": {
            ...
        }
        ...
    }
}

这就是我从那里的代码中推断的,如果我犯了错误,你可以澄清我。

// here we are searching for the current reference in Campaings/key
DatabaseReference database = FirebaseDatabase.getInstance().getReference().child("Campaigns").child(key);

int preIncrementUserCount = Integer.parseInt(button.getText().toString());
final int incrementedUserCount = ++preIncrementUserCount;

button.setText(String.valueOf(incrementedUserCount));
database.runTransaction(new Transaction.Handler() {
    @Override
    public Transaction.Result doTransaction(MutableData mutableData) {

        // here we are searching for the current count value in 
        // Campaings/key/count
        Integer currentValue = mutableData.child("count").getValue(Integer.class);

        if (currentValue == null) {
            // do something...
        } else {
            // here we are setting the count value in Campaings/key/count
            mutableData.child("count").setValue(++currentValue);
        }

        // here we are setting the count value in Campaings/key/users/UUID/count
        mutableData.child("users").child(getUid()).child("count").setValue(incrementedUserCount);

        return Transaction.success(mutableData);
    }

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

       if (databaseError != null) {
            Log.e(TAG, "Error: " + databaseError.getMessage());
       }

       System.out.println("Transaction completed");
    }
});

代码示例的目的是说明您正在进行的数据模式搜索(如果我犯了错误,再次在任何部分纠正我),但请放置databaseError登录onComplete以进行调试

更新3

来自documentation

  

doTransaction()将被多次调用并且必须能够   处理空数据。即使您的遥控器中存在现有数据   数据库,它可能不是在事务功能时本地缓存   跑了。

尝试更改此部分:

  if (currentValue == null) {
      // do something...
  } else {
      // here we are setting the count value in Campaings/key/count
      mutableData.child("count").setValue(++currentValue);
  }

类似于:

  if (mutableData.child("count").getValue() == null) {
      // print something...
      // Log.d(TAG,"Value at some point was null");
      // or maybe (just to test)
      // mutableData.child("count").setValue(1);
  } else {
      // here we are setting the count value in Campaings/key/count
      mutableData.child("count").setValue(++currentValue);
  }

P.S:如果任何人在可预见的未来可能会对类似的事情进行排查,我会离开解决方案

答案 2 :(得分:1)

也许它正试图启动毫无准备的媒体播放器。像这样开始:

 mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener(){
       public void onPrepared(MediaPlayer p){
            p.start();

       }       
 }

修改

查看Firebase doc MutableData setValue(..)方法,它只接受以下类型。但是你使用的是原始类型的int。

  • 布尔
  • Double
  • 地图
  • 列表

所以你应该改变这个

 int totalCount = mutableData.child("count").getValue(int.class);

 Long totalCount = mutableData.child("count").getValue();

答案 3 :(得分:0)

在这一行中,

        mutableData.child("count").setValue(newTotalCount);
你正在通过一个&#39; int&#39; setValue()方法的值。它应该是此方法支持的Object。

As per the documentation

   public void setValue (Object value)

   Set the data at this location to the given value.
   The native types accepted by this method for the value correspond to the JSON types:

   Boolean
   Long
   Double
   Map<String, Object>
   List<Object>

你正在传递一个&#39; int&#39;那里的价值。所以将该值更改为相应的Object。

在这一行中,

 int totalCount = mutableData.child("count").getValue(int.class);

你正在通过&#39; int.class&#39;获得价值。它不是一个对象。它也应该是一个对象。改变这个。我希望它会对你有所帮助。