Подтвердить что ты не робот

Рекурсия, вызывающая mutableData.setValue(), вызывающая создание одной и той же активности в backstack?

Изменить: 23/10/2016: Это не решено, и я все еще ищу ответ. Я собираюсь переписать этот вопрос, чтобы сделать его более ясным, поскольку теперь я знаю, что вызывает эту проблему.


Редактировать: 26/10/2016: SOMETHING FOUND: При попытке найти проблему у меня появилась ошибка, которая помогла мне что-то найти. Получается, если у меня есть это в моей базе данных Firebase:

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

Вместо:

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

Проблема не выполняется.

Итак, в заключение, это, вероятно, ошибка рекурсии.


У меня есть эта транзакция Firebase:

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);

и этот:

mutableData.child("count").setValue(incrementedTotalCount);

Когда я запускаю транзакцию, тот же актив снова создается и открывается. Когда я нажимаю кнопку "Назад", она переходит к предыдущей операции в задней части. НО, предыдущее действие в backstack - это та же самая деятельность. Вот так:

IMAGE

Каждый раз, когда я нажимаю кнопку, в заднем стеке создается новое действие (действие с проблемой).

Чтобы показать вам, как это выглядит, вот GIF:

GIF

Почему это происходит?

4b9b3361

Ответ 1

(я буду размещать здесь, поскольку его легче управлять моими комментариями)...

Решение не найдено.

* ОБНОВЛЕНИЕ 4: найденное решение **

После многих попыток и большого количества комментариев решение было окончательно найдено, очевидно, что OP имел метод инкремента в другом классе и переместил его в тот же класс, который использовался для решения проблемы.

Насколько это возможно, на мой взгляд, возможно, это связано с проблемой concurrency в транзакции, возможно, проблема заключалась в том, что она создавала цикл самостоятельно. Ваша деятельность начинается, позже вы создаете экземпляр FirebaseController, который в какой-то момент уволил метод increment, выполнив асинхронное выполнение, которое заканчивается (каким-то образом заканчивается?), И оно снова запускает вашу активность.



См. ниже о неудачных попытках (но устранение неполадок).


Я пытался отлаживать. Он не активировался ни на одной из этих линий. Занимает меня к группе файлов Android (например, View.java). Когда я "Бегу в курсор" (пропустить следующую контрольную точку отладки), перезапускается.

Можете ли вы проверить, не является ли отображаемый клик в 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) В документации объясняется, как это работает:

Установите данные в этом месте на заданное значение. Нативные типы принятые этим методом для значения, соответствуют типам JSON:

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

Кроме того, вы можете установить экземпляры своего собственного класса в это место, если они удовлетворяют следующим ограничениям:

Класс должен иметь конструктор по умолчанию, который не принимает аргументов. класс должен определять публичные геттеры для назначаемых свойств. Свойства без публичного getter будут установлены в их значение по умолчанию когда экземпляр десериализован

Попробуйте использовать другой тип данных, предложенный другими разработчиками:

измените параметр #setValue на объект вышеприведенного списка и #getValue также верните объект, попробуйте применить его к правильному тип, возможно, вы могли бы использовать Integer class

ОБНОВЛЕНИЕ 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

Из документации:

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

Он перезапускает вашу деятельность, потому что он где-то падает. Попробуйте этот код:

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);

}

Ответ 3

Возможно, он пытается запустить неподготовленный медиаплеер. Начните так:

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

       }       
 }

EDIT:

Взгляните на Firebase doc Метод MutableData setValue (..) принимает только следующие типы. Но u использует int, который является примитивным типом.

  • Boolean
  • Long
  • Двойной
  • Карта
  • List

Итак, u должен изменить этот

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

с

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

Ответ 4

В этой строке

        mutableData.child("count").setValue(newTotalCount);

вы передаете значение 'int' методом setValue(). Этот метод должен быть поддерживаемым объектом.

В соответствии с документацией,

   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>

Вы передаете значение 'int'. Поэтому измените это значение на соответствующий объект.

В этой строке

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

вы получаете значение с 'int.class'. Это не объект. Он также должен быть Объектом. Измените это. Надеюсь, это поможет вам.