Java Android ViewModel和Firebase获取空列表

Java Android ViewModel和Firebase获取空列表,java,android,firebase,firebase-realtime-database,mvvm,Java,Android,Firebase,Firebase Realtime Database,Mvvm,我正在使用一个ViewModel架构和一个Firebase实时数据库来管理我的应用程序的数据 应用程序描述: 有飞溅活动、登录活动、游戏活动和排行榜活动。在splash活动中检索一次完整的用户数据,以检查用户在成功检索时是否具有internet连接,登录活动是否打开或绕过游戏活动。排行榜活动可以从LoginActivity和GameAcility打开 问题: 每当我尝试从GameActivity打开排行榜时,总会返回一个空列表。然而,当我从LoginActivity打开它时,这个问题从未出现过。

我正在使用一个ViewModel架构和一个Firebase实时数据库来管理我的应用程序的数据

应用程序描述: 有飞溅活动、登录活动、游戏活动和排行榜活动。在splash活动中检索一次完整的用户数据,以检查用户在成功检索时是否具有internet连接,登录活动是否打开或绕过游戏活动。排行榜活动可以从LoginActivity和GameAcility打开

问题: 每当我尝试从GameActivity打开排行榜时,总会返回一个空列表。然而,当我从LoginActivity打开它时,这个问题从未出现过。然而,如果我曾经登录,然后从firebase控制台更改数据库数据,我确实会在排行榜中得到一个非空列表

我试过什么?: 调试并记录存储库代码,以发现当我从GameActivity到达LeadboardActivity时,onDataChange()从未在LeadboardActivity中调用,但是ViewModel调用按预期进行

我还尝试使用model.getUsers().getValue()检索列表但返回null

代码:

GameViewModel.java

GameActivity.java

LeaderboardActivity.java


公共类排行榜活动扩展了AppCompative活动{
列表用户=新建ArrayList();
回收视图回收视图;
排行榜回收适配器;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_排行榜);
适配器=新的排行榜回收适配器(用户);
recyclerView=findViewById(R.id.rv);
recyclerView.setLayoutManager(新的LinearLayoutManager(本));
recyclerView.setAdapter(适配器);
GameViewModel模型=(新ViewModelProvider(this)).get(GameViewModel.class);
model.getUsers().observe(这个,u->{
user.clear();
users.addAll(u);
adapter.notifyDataSetChanged();
});
}
}
LoginActivity.java


公共类LoginActivity扩展了AppCompatActivity{
意图;
博弈模型;
List userList=new ArrayList();
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity\u登录);
sharedPref=getSharedPreferences(
SP_密钥,上下文。模式_专用);
model=(新ViewModelProvider(this)).get(GameViewModel.class);
model.getUsers().observe(这个,u->{
userList=u;
});
}
私有void setListeners(){
ivleadboard.setOnClickListener(新视图.OnClickListener(){
@凌驾
公共void onClick(视图){
意图=新意图(LoginActivity.this、leadboardActivity.class);
星触觉(意向);
覆盖转换(0,0);
}
});
btnLogin.setOnClickListener(新视图.OnClickListener(){
@凌驾
公共void onClick(视图){
登录();
}
});
}
私有void登录(){
//登录检查在这里
意图=新意图(这个,GameActivity.class);
星触觉(意向);
}
}

假设代码适用于其他功能,则删除了此问题不需要的部分。

在活动销毁之前手动删除附加到FirebaseReference的侦听器可以解决问题。早期的实现是在ViewModel生命周期之后进行的,因此从技术上讲,侦听器被附加并随后被删除,因此列表为空

代码更改:

GameViewModel.java

XyzActivity.java


请包含与问题相关的最小可运行代码,而不是整个应用程序的转储,以供其他人尝试和调试。很可能,您会发现在侦听器被取消时生成了一个错误,这可能是因为它是在身份验证完成解析之前建立的。@Kato谢谢您的建议,我缩短了代码!

public class GameViewModel extends ViewModel implements DataRetrievedInterface {

    private MutableLiveData<List<User>> users;
    private GameRepository repository = GameRepository.getInstance(this);

    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        repository.retrieveData();
    }

    private void loadUsers(List<User> result) {
        users.setValue(result);
    }

    public void addUser(User user) {
        List<User> us = users.getValue();
        assert us != null;
        us.add(user);
        repository.addUser(us);
        users.postValue(us);
    }

    @Override
    public void notifyDataRetrieved(List<User> result) {
        loadUsers(result);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        repository.removeListener();
    }

    public void updateScore(String userName, Long score) {
        repository.updateScore(userName, score);
    }
}


public class GameRepository {

    public final FirebaseDatabase database = FirebaseDatabase.getInstance();
    DatabaseReference ref = database.getReference("Users");
    public DataRetrievedInterface retrievedInterface;

    private static GameRepository repository = null;

    ValueEventListener eventListener;

    private GameRepository(DataRetrievedInterface retrievedInterface) {
        this.retrievedInterface = retrievedInterface;
    }

    public static GameRepository getInstance(DataRetrievedInterface retrievedInterface) {
        if(repository==null)
            repository = new GameRepository(retrievedInterface);

        repository.eventListener = (new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {

                List<User> users = new ArrayList<>();

                for(DataSnapshot snapshot1:snapshot.getChildren()) {
                    users.add(snapshot1.getValue(User.class));
                }

                retrievedInterface.notifyDataRetrieved(users);
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Log.e("Database Error", error.getMessage());
            }
        });

        return repository;
    }

    public void retrieveData() {
        ref.addValueEventListener(eventListener);
    }

    public void removeListener() {
        ref.removeEventListener(eventListener);
    }

    public void addUser(List<User> user) {
        ref.setValue(user);
    }

    public void updateScore(String userName, Long score) {
// assume temp is a one time listener for updating value in db
        ref.addListenerForSingleValueEvent(temp);
    }

}


public class SplashActivity extends AppCompatActivity {

    Intent intent;

    GameViewModel model;
    Boolean isDataReady = false;
    Boolean isAnimationDone = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);

        model = (new ViewModelProvider(this)).get(GameViewModel.class);

        model.getUsers().observe(this, u -> {
            isDataReady = true;
            if(isAnimationDone) {
                startActivity(intent);
            }
        });
    }

    private void initViewsAndVars() {
        intent = new Intent(this, LoginActivity.class);
    
        intent = new Intent(SplashActivity.this, GameActivity.class);
        
        if(isDataReady) {
              startActivity(intent);
        }
    }

}


public class GameActivity extends AppCompatActivity implements GameStatusInterface {

    GamePlay gamePlay;
    GameViewModel model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_game);

        gamePlay = new GamePlay(this, screenWidth, screenHeight);
        btnLeader = findViewById(R.id.btnLeaderGame);

        model = (new ViewModelProvider(this)).get(GameViewModel.class);

        btnLeader.setOnClickListener(view -> {
            Intent intent = new Intent(this, LeaderboardActivity.class);
            startActivity(intent);
            overridePendingTransition(0,0);
        });

    }

    @Override
    public void onGameEnded(Long score) {
        model.updateScore(userName, current + score);
    }

}


public class LeaderboardActivity extends AppCompatActivity {

    List<User> users = new ArrayList<>();
    RecyclerView recyclerView;
    LeaderboardRecyclerAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leaderboard);

        adapter = new LeaderboardRecyclerAdapter(users);

        recyclerView = findViewById(R.id.rv);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);

        GameViewModel model = (new ViewModelProvider(this)).get(GameViewModel.class);

        model.getUsers().observe(this, u -> {
            users.clear();
            users.addAll(u);
            adapter.notifyDataSetChanged();
        });

    }

}


public class LoginActivity extends AppCompatActivity {

    Intent intent;

    GameViewModel model;
    List<User> userList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        sharedPref = getSharedPreferences(
                SP_KEY, Context.MODE_PRIVATE);

        model = (new ViewModelProvider(this)).get(GameViewModel.class);

        model.getUsers().observe(this, u -> {
            userList = u;
        });

    }

    private void setListeners() {

        ivLeaderboard.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                intent = new Intent(LoginActivity.this, LeaderboardActivity.class);
                startActivity(intent);
                overridePendingTransition(0,0);
            }
        });

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                login();
            }
        });

    }

    private void login() {
// Login Checks Here
        intent = new Intent(this, GameActivity.class);
            startActivity(intent);        

    }

}

public void removeListener() {
    repository.removeListener();
}

@Override
protected void onCleared() {
    super.onCleared();
}
model.removeListener();
startActivity(intent);