【问题标题】:Android ViewModel and Firebase fetching empty listAndroid ViewModel 和 Firebase 获取空列表
【发布时间】:2021-04-27 23:05:11
【问题描述】:

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

应用说明: 有 Splash Activity、Login Activity、Game Activity 和 Leaderboard Activity。在启动活动中检索一次完整的用户数据,以检查用户在成功检索时是否有互联网连接、登录活动是否打开或被绕过到游戏活动。排行榜活动可以从 LoginActivity 和 GameAcivity 中打开。

问题: 每当我尝试从 GameActivity 打开排行榜时,总是会返回一个空列表。但是,当我从 LoginActivity 打开它时,这个问题永远不会出现。但是,如果我登录然后从 firebase 控制台更改数据库数据,我会在排行榜中获得一个非空列表。

我尝试了什么?: 调试和记录存储库代码,发现当我从 GameActivity 到达 LeaderboardActivity 时,onDataChange() 从未在内部调用,但是 ViewModel 调用按预期进行。

我也尝试使用 model.getUsers().getValue(); 检索列表,但这会返回 null。

代码:

GameViewModel.java


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

GameRepository.java


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

}

SplashActivity.java


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

}

GameActivity.java


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

}

LeaderboardActivity.java


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

    }

}

LoginActivity.java


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

    }

}

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

【问题讨论】:

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

标签: java android firebase firebase-realtime-database mvvm


【解决方案1】:

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

代码更改:

GameViewModel.java

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

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

XyzActivity.java

model.removeListener();
startActivity(intent);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    • 2014-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    相关资源
    最近更新 更多