我使用ViewModel架构和Firebase实时数据库来管理我的应用程序的数据。
应用程序描述:有飞溅活动、登录活动、游戏活动和排行榜活动。在启动活动中检索一次完整的用户数据,以检查用户在成功检索时是否有互联网连接,登录活动打开或绕过游戏活动。排行榜活动可以从LoginActivity和GameAcivity打开。
问题:每当我试图从GameActivity打开排行榜时,总是会返回一个空列表。但是,当我从LoginActivity打开它时,这个问题永远不会出现。然而,如果我登录并从firebase控制台更改数据库数据,我确实会在Leaderboard中得到一个非空列表。
我尝试了什么?:调试并记录Repository代码,发现当我从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();
});
}
}
登录活动.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);
}
}
假设代码适用于其他功能,已经删除了这个问题不需要的部分
在活动被销毁之前手动删除附加到FirebaseReference的侦听器可以解决问题。早期的实现遵循ViewModel生命周期,因此从技术上讲,侦听器被附加并随后被删除,因此列表为空。
代码更改:
GameViewModel.java
public void removeListener() {
repository.removeListener();
}
@Override
protected void onCleared() {
super.onCleared();
}
XyzActivity.java
model.removeListener();
startActivity(intent);