Я создал приложение Android с использованием облачных сервисов Java и Firebase. Цель моего приложения - позволить пользователям просматривать самые популярные или самые популярные фильмы и отмечать их как избранные.
Вот моя структура базы данных , как только пользователь нажимает кнопку «Избранное», детали фильма сохраняются в БД в реальном времени.
При первом запуске приложения фильмСписок выглядит великолепно Основное задание
, но при выходе из задания и возврате полученные фильмы будут дубликаты, подобные этому: дубликаты фильмов
Вот класс Query, который будет содержать OnActive (), где вставлен ChildEventListener, и OnInactive, где он будет удален
public class FirebaseQueryLiveData extends LiveData<DataSnapshot> {
private final Query query;
private final MyValueEventListener listener = new MyValueEventListener();
public FirebaseQueryLiveData(Query query) {
this.query = query;
}
public FirebaseQueryLiveData(DatabaseReference ref) {
this.query = ref;
}
@Override
protected void onActive() {
query.addChildEventListener(listener);
}
@Override
protected void onInactive() {
if (query != null && listener != null) {
query.removeEventListener(listener);
}
}
private class MyValueEventListener implements ChildEventListener {
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
setValue(dataSnapshot);
}
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
}
}
, и это мой MovieViewModel
public class MovieViewModel extends ViewModel {
MutableLiveData<List<Movie>> movieLiveData = new MutableLiveData<>();
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference movieRef;
private FirebaseQueryLiveData liveData;
public MovieViewModel(String userName) {
this.movieRef = database.getReference().child(userName);
this.liveData = new FirebaseQueryLiveData(movieRef);
}
@NonNull
public LiveData<DataSnapshot> getLiveData() {
return liveData;
}
}
этоосновной Activity, и, как вы можете видеть ниже, метод generateMovies (String mUserName) отвечает за вставку извлеченных фильмов из класса viewModel в список, и он генерируется в OnCreate, если username! = null, и OnRestart.
public class MainActivity extends AppCompatActivity{
private List<Movie> movies;
private MovieAdapter adapter;
public static final String TAG = MainActivity.class.getName();
public static final String LIFE_CYCLE = "LIFE CYCLE";
private FirebaseAuth mFirebaseAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
public static final int RC_SIGN_IN = 1;
public static final String ANONYMOUS = "anonymous";
private String mUserName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(LIFE_CYCLE,"onCreate");
mFirebaseAuth = FirebaseAuth.getInstance();
mAuthStateListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null){
//user is signed in
Log.d(TAG, "onAuthStateChanged: " +user.getDisplayName());
mUserName = user.getDisplayName();
//if user is signed in generate Movies and pass in the username
generateMovies(mUserName);
}else {
onSignedOutCleanUp();
//user is signed out
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setIsSmartLockEnabled(false)
.setAvailableProviders(Arrays.asList(
new AuthUI.IdpConfig.GoogleBuilder().build(),
new AuthUI.IdpConfig.EmailBuilder().build()))
.build(), RC_SIGN_IN);
}
}
};
//when the device is rotated the adapter will be null so we have to init in onCreate
adapter = new MovieAdapter(getApplicationContext(),movies,mUserName);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN){
if (resultCode == RESULT_OK){
Toast.makeText(MainActivity.this,"Signed In!",Toast.LENGTH_SHORT).show();
} else if (resultCode == RESULT_CANCELED){
Toast.makeText(MainActivity.this,"Signed In canceled!!",Toast.LENGTH_SHORT).show();
finish(); }
}
}
@Override
protected void onResume() {
super.onResume();
mFirebaseAuth.addAuthStateListener(mAuthStateListener);
if (movies != null){
movies.clear();
Log.d(LIFE_CYCLE, "Clear ");
}
Log.d(LIFE_CYCLE,"onResume");
}
@Override
protected void onPause() {
super.onPause();
mFirebaseAuth.removeAuthStateListener(mAuthStateListener);
Log.d(LIFE_CYCLE, "onPause");
if (movies != null){
movies.clear();
Log.d(LIFE_CYCLE, "Clear");
}
}
@Override
protected void onStart() {
super.onStart();
Log.d(LIFE_CYCLE, "onStart");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.menu_sorting, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.settings:
Intent intent = new Intent(this, Settings.class);
startActivity(intent);
return true;
case R.id.sign_out_menu:
AuthUI.getInstance().signOut(this);
}
return super.onOptionsItemSelected(item);
}
private String getSortValue() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
Log.d("MainActivity","the preference is "+sharedPreferences.getString(getString(R.string.sort_by_key), getString(R.string.sort_popularity)));
return sharedPreferences.getString(getString(R.string.sort_by_key), getString(R.string.sort_popularity));
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(LIFE_CYCLE, "onRestart ");
Toast.makeText(this, "Sorting by " + getSortValue(),Toast.LENGTH_LONG).show();
movies.clear();
generateMovies(mUserName);
}
private void onSignedOutCleanUp(){
mUserName = ANONYMOUS;
if (movies != null){
movies.clear();
adapter.notifyDataSetChanged();
}
}
private void generateMovies(String mUserName){
Log.d(LIFE_CYCLE,"GENERATE ");
movies = new ArrayList<>();
ViewModelFactory modelFactory = new ViewModelFactory(mUserName);
MovieViewModel viewModel = ViewModelProviders.of(this,modelFactory).get(MovieViewModel.class);
if (getSortValue().equals(getString(R.string.sort_by_favorites))) {
movies.clear();
adapter.notifyDataSetChanged();
viewModel.getLiveData().observe(this, new Observer<DataSnapshot>() {
@Override
public void onChanged(DataSnapshot dataSnapshot) {
if (dataSnapshot != null) {
for (DataSnapshot itemSnapShot : dataSnapshot.getChildren()) {
Movie movie = itemSnapShot.getValue(Movie.class);
Log.d(TAG, "itemSnapShot = " + itemSnapShot.getValue().toString());
movies.add(movie);
adapter.notifyDataSetChanged();
}
Log.d(TAG, "movies list: "+movies.size());
}
}
});
} else if (getSortValue().equals(getString(R.string.sort_popularity))){
viewModel.getMovies( getString(R.string.sort_popularity) ).observe(this, new Observer<List<Movie>>() {
@Override
public void onChanged(@Nullable List<Movie> moviesModels) {
movies.clear();
if (moviesModels != null) {
movies.addAll(moviesModels);
adapter.notifyDataSetChanged();
}
}
}); }else if (getSortValue().equals(getString(R.string.sort_top_rated))){
viewModel.getMovies(getString(R.string.sort_top_rated)).observe(this, new Observer<List<Movie>>() {
@Override
public void onChanged(@Nullable List<Movie> moviesModels) {
movies.clear();
if (moviesModels != null) {
movies.addAll(moviesModels);
adapter.notifyDataSetChanged();
}
}
});
}
Toast.makeText(this, "Sorting by " + getSortValue(),Toast.LENGTH_LONG).show();
RecyclerView recyclerView = findViewById(R.id.recyclerView);
// Calling the Adapter object and setting it to the recycler view.
adapter = new MovieAdapter(this, movies,mUserName);
recyclerView.setAdapter(adapter);
GridLayoutManager layoutManager = new GridLayoutManager(this, 3);
recyclerView.setLayoutManager(layoutManager);
}
}