Firebase - адаптер вызывается до того, как в моем массиве есть какие-либо элементы, и возвращает пустой RecyclerView. - PullRequest
0 голосов
/ 01 декабря 2018

Я столкнулся с действительно странной проблемой: я вызываю два узла в моей базе данных, с одного узла я получаю все свои идентификаторы пользователя, а затем просто зацикливаюсь внутри своего узла пользователя, чтобы получить метаданные пользователя.Проблема в том, что когда я запускаю отладчик, кажется, что mAdapter вызывается beign перед тем, как данные извлекаются.Таким образом, мой адаптер установил обзор переработчика без данных.После вызова адаптера начинается мой запрос на выборку пользователя, и массив заполняется нормально

Это то, что я сделал до сих пор

 mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        for(DataSnapshot snapshot : dataSnapshot.getChildren()){

            mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                    UserModel model = dataSnapshot.getValue(UserModel.class);
                    String playerName = model.getName();
                    model.setName(playerName);
                    mArrayPlayers.add(model);
                }


                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) { }

            });

        }
        mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
        if(mAdapter.getItemCount()>0)
            mRecyclerView.setAdapter(mAdapter);

    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) { }

});

Как видите, я установилАдаптер к обзору реселлера после первого foreach, который выбирает все данные, я знаю, что beign асинхронный, мне нужно подождать, пока внутренняя база данных mDatabase будет выполнена, но если я переместу настройку адаптера на одну строку выше, чтобы дождаться завершения второго вызоваон делает то же самое, любая идея?

Структура базы данных такова

teamsNode
|__Jm2KSMjslpow2Ipoasz : true
|__601KSMjsldfjd2Ipos0 : true
|__asgm2Kshalpow2IposJ : true

users
|__Jm2KSMjslpow2Ipoasz 
   |_____name: randomname1
|__601KSMjsldfjd2Ipos0
   |_____name: randomname2
|__asgm2Kshalpow2IposJ
   |_____name: randomname3

Recyclerview не заполнен.Любая идея?

Странная вещь в том, что иногда запуск его с помощью отладчика заполняет данные, но редко случается.

Ответы [ 4 ]

0 голосов
/ 01 декабря 2018

Вы можете посмотреть на мой код, который я использую ValueEventListener, как и вы, и привязать данные к recycler view

ValueEventListener postListener = new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
           for(DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()){
               UserListModel userListModel =dataSnapshot1.getValue(UserListModel.class);
               userModelArrayList.add(userListModel);
           }
           userListAdapter.notifyDataSetChanged();
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            Log.w(">>value", "loadPost:onCancelled", databaseError.toException());

        }
    };
    //mDatabase is the reference to the Firebase Node
    mDatabase.addValueEventListener(postListener);
    userListRecyclerView.setLayoutManager(M.gridLayoutRecyclerView(getActivity() , 2));
    userListAdapter = new UserListAdapter(getActivity(), userModelArrayList);
    userListRecyclerView.setAdapter(userListAdapter);
0 голосов
/ 01 декабря 2018

Надеюсь, это поможет.

mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
                    mArrayPalyer.clear();//add this
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                        for(DataSnapshot snapshot : dataSnapshot.getChildren()){

                            mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
                                @Override
                                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                                    UserModel model = dataSnapshot.getValue(UserModel.class);
                                    String playerName = model.getName();
                                    model.setName(playerName);
                                    mArrayPlayers.add(model);
                                }



                                @Override
                                public void onCancelled(@NonNull DatabaseError databaseError) {

                                }

                            });

                        }
                        mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
                        mRecyclerView.setAdapter(mAdapter); //makes changes here because you are checking for adapter item count before setting it and this always return null.

                    }
0 голосов
/ 01 декабря 2018

Если вы хотите инициализировать адаптер только после загрузки всех данных, вам нужно сделать это во вложенном обратном вызове onDataChange:

mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshotTeam) {
        AtomicInteger loadedUserCount = new AtomicInteger(0);
        for(DataSnapshot snapshot : dataSnapshotTeam.getChildren()){

            mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                    UserModel model = dataSnapshot.getValue(UserModel.class);
                    String playerName = model.getName();
                    model.setName(playerName);
                    mArrayPlayers.add(model);
                    if (loadedUserCount.getAndIncrement() + 1 == dataSnapshotTeam.getChildrenCount()) {
                        mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
                        if(mAdapter.getItemCount()>0) {
                            mRecyclerView.setAdapter(mAdapter);
                        }
                    }
                }

                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) { 
                    throw databaseError.toException(); // don't ignore errors
                }
            });
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) { 
        throw databaseError.toException(); // don't ignore errors
    }
});
0 голосов
/ 01 декабря 2018

Проблема в том, что вызовы методов addListenerForSingleValueEvent являются асинхронными, поэтому они выполняются в другом потоке.Когда вы вызываете второй addListenerForSingleValueEvent, он выполняется в другом потоке, в то время как основной поток следует за его выполнением.Вы должны выполнить вызов вашего адаптера в ответ на ваш второй вызов addListenerForSingleValueEvent в методе onDataChange.Таким образом, чтобы избежать аномальных операций вашего приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...