Я использую библиотеку подкачки с Firebase и устанавливаю ValueEventListener в хранилище, чтобы сделать данные недействительными при каждом изменении.Проблема в том, что как только я запускаю invalidate()
, триггер наблюдателя onChanged
и Adapter.submitList(items)
получает элементы с размером 0, прежде чем новая дата загружается из Firebase с помощью метода loadInitial
в ItemKeyedDataSource
.
В результате DIFF_CALLBACK
никогда не вызывается на PagedListAdapter
, потому что нет сравнения между старым списком и списком новых элементов (размер 0), и когда полученные данные обновляют список, как будтоЯ использовал notifyDataSetChanged()
.
Пока единственное решение - задержать Adapter.submitList(items)
на 5 секунд, пока все данные не будут получены из Firebase, но это не практичное решение.
public class UsersRepository {
private final static String TAG = UsersRepository.class.getSimpleName();
// [START declare_database_ref]
private DatabaseReference mDatabaseRef;
private DatabaseReference mUsersRef;
private Boolean isFirstLoaded = true;
public ValueEventListener usersChangesListener;
public UsersRepository() {
mDatabaseRef = FirebaseDatabase.getInstance().getReference();
mUsersRef = mDatabaseRef.child("users");
isFirstLoaded = true;
Log.d(TAG, "UsersRepository init. isFirstLoaded= " + isFirstLoaded);
}
public void getUsers(Long initialKey, final int size, @NonNull final ItemKeyedDataSource.LoadInitialCallback < User > callback) {
if (initialKey == null) {
Log.d(TAG, "getUsers initialKey= " + initialKey);
mUsersRef.orderByChild("created").limitToFirst(size).addListenerForSingleValueEvent(new ValueEventListener() {@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
// [START_EXCLUDE]
if (dataSnapshot.exists()) {
// loop throw users value
List < User > usersList = new ArrayList < >();
for (DataSnapshot userSnapshot: dataSnapshot.getChildren()) {
usersList.add(userSnapshot.getValue(User.class));
Log.d(TAG, "getUsers dataSnapshot. getSnapshotKey= " + userSnapshot.getKey());
}
if (usersList.size() == 0) {
return;
}
Log.d(TAG, "getUsers usersList.size= " + usersList.size() + " lastkey= " + usersList.get(usersList.size() - 1).getCreatedLong());
if (callback instanceof ItemKeyedDataSource.LoadInitialCallback) {
//initial load
/*((ItemKeyedDataSource.LoadInitialCallback)callback)
.onResult(usersList, 0, 14);*/
callback.onResult(usersList);
}
} else {
Log.w(TAG, "getUsers no users exist");
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
} else {
Log.d(TAG, "getUsers initialKey= " + initialKey);
mUsersRef.orderByChild("created").startAt(initialKey).limitToFirst(size).addListenerForSingleValueEvent(new ValueEventListener() {@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
// [START_EXCLUDE]
if (dataSnapshot.exists()) {
// loop throw users value
List < User > usersList = new ArrayList < >();
for (DataSnapshot userSnapshot: dataSnapshot.getChildren()) {
usersList.add(userSnapshot.getValue(User.class));
Log.d(TAG, "getUsers dataSnapshot. getSnapshotKey= " + userSnapshot.getKey());
}
if (usersList.size() == 0) {
return;
}
Log.d(TAG, "getUsers usersList.size= " + usersList.size() + " lastkey= " + usersList.get(usersList.size() - 1).getCreatedLong());
if (callback instanceof ItemKeyedDataSource.LoadInitialCallback) {
//initial load
/*((ItemKeyedDataSource.LoadInitialCallback)callback)
.onResult(usersList, 0, 14);*/
callback.onResult(usersList);
}
} else {
Log.w(TAG, "getUsers no users exist");
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
}
}
public void getUsersAfter(final Long key, final int size, @NonNull final ItemKeyedDataSource.LoadCallback < User > callback) {
/*if(key == entireUsersList.get(entireUsersList.size()-1).getCreatedLong()){
Log.d(TAG, "getUsersAfter init. afterKey= " + key+ "entireUsersList= "+entireUsersList.get(entireUsersList.size()-1).getCreatedLong());
return;
}*/
Log.d(TAG, "getUsersAfter. AfterKey= " + key);
mUsersRef.orderByChild("created").startAt(key).limitToFirst(size).addListenerForSingleValueEvent(new ValueEventListener() {@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
// [START_EXCLUDE]
if (dataSnapshot.exists()) {
// loop throw users value
List < User > usersList = new ArrayList < >();
for (DataSnapshot userSnapshot: dataSnapshot.getChildren()) {
usersList.add(userSnapshot.getValue(User.class));
Log.d(TAG, "getUsersAfter dataSnapshot. getSnapshotKey= " + userSnapshot.getKey());
}
if (usersList.size() == 0) {
return;
}
Log.d(TAG, "getUsersAfter usersList.size= " + usersList.size() + "lastkey= " + usersList.get(usersList.size() - 1).getCreatedLong());
if (callback instanceof ItemKeyedDataSource.LoadCallback) {
//initial After
callback.onResult(usersList);
/*((ItemKeyedDataSource.LoadCallback)callback)
.onResult(usersList);*/
}
} else {
Log.w(TAG, "getUsersAfter no users exist");
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
//mUsersRef.addValueEventListener(usersListener);
}
public void getUsersBefore(final Long key, final int size, @NonNull final ItemKeyedDataSource.LoadCallback < User > callback) {
Log.d(TAG, "getUsersBefore. BeforeKey= " + key);
/*if(key == entireUsersList.get(0).getCreatedLong()){
return;
}*/
mUsersRef.orderByChild("created").endAt(key).limitToLast(size).addListenerForSingleValueEvent(new ValueEventListener() {@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
// [START_EXCLUDE]
if (dataSnapshot.exists()) {
// loop throw users value
List < User > usersList = new ArrayList < >();
for (DataSnapshot userSnapshot: dataSnapshot.getChildren()) {
usersList.add(userSnapshot.getValue(User.class));
Log.d(TAG, "getUsersBefore dataSnapshot. getSnapshotKeys= " + userSnapshot.getKey());
}
if (usersList.size() == 0) {
return;
}
Log.d(TAG, "getUsersBefore usersList.size= " + usersList.size() + "lastkey= " + usersList.get(usersList.size() - 1).getCreatedLong());
if (callback instanceof ItemKeyedDataSource.LoadCallback) {
//initial before
callback.onResult(usersList);
/*((ItemKeyedDataSource.LoadCallback)callback)
.onResult(usersList);*/
}
//initial load
/* ((ItemKeyedDataSource.LoadCallback)callback)
.onResult(usersList, 0, usersList.size());*/
} else {
Log.w(TAG, "getUsersBefore no users exist");
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
//mUsersRef.addValueEventListener(usersListener);
}
public void usersChanged(final DataSource.InvalidatedCallback InvalidatedCallback) {
final Query query = mUsersRef.orderByChild("created");
usersChangesListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!isFirstLoaded) {
isFirstLoaded = true;
Log.d(TAG, "entireUsersList Invalidated:");
// Remove post value event listener
if (usersChangesListener != null) {
query.removeEventListener(usersChangesListener);
Log.d(TAG, "usersChanged Invalidated removeEventListener");
}
((ItemKeyedDataSource.InvalidatedCallback)InvalidatedCallback).onInvalidated();
}
isFirstLoaded = false;
/*if(entireUsersList.size() > 0){
entireUsersList.clear();
((ItemKeyedDataSource.InvalidatedCallback)onInvalidatedCallback).onInvalidated();
Log.d(TAG, "entireUsersList Invalidated:");
return;
}
if (dataSnapshot.exists()) {
// loop throw users value
for (DataSnapshot userSnapshot: dataSnapshot.getChildren()){
entireUsersList.add(userSnapshot.getValue(User.class));
}
Log.d(TAG, "entireUsersList size= "+entireUsersList.size()+"dataSnapshot count= "+dataSnapshot.getChildrenCount());
} else {
Log.w(TAG, "usersChanged no users exist");
}*/
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
query.addValueEventListener(usersChangesListener);
//mUsersRef.addValueEventListener(eventListener);
}
}
public class UsersDataSource extends ItemKeyedDataSource < Long,
User >
public class UsersDataSource extends ItemKeyedDataSource < Long,
User > {
private final static String TAG = UsersDataSource.class.getSimpleName();
private UsersRepository usersRepository;
public UsersDataSource() {
usersRepository = new UsersRepository();
}
@Override
public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
//super.addInvalidatedCallback(onInvalidatedCallback);
Log.d(TAG, "Callback Invalidated ");
usersRepository.usersChanged(onInvalidatedCallback);
}
@Override
public void loadInitial(@NonNull LoadInitialParams < Long > params, @NonNull LoadInitialCallback < User > callback) {
/*List<User> items = usersRepository.getUsers(params.requestedInitialKey, params.requestedLoadSize);
callback.onResult(items);*/
Log.d(TAG, "loadInitial params key" + params.requestedInitialKey + " " + params.requestedLoadSize);
usersRepository.getUsers(params.requestedInitialKey, params.requestedLoadSize, callback);
//usersRepository.getUsers( 0L, params.requestedLoadSize, callback);
}
@Override
public void loadAfter(@NonNull LoadParams < Long > params, @NonNull LoadCallback < User > callback) {
/*List<User> items = usersRepository.getUsers(params.key, params.requestedLoadSize);
callback.onResult(items);*/
Log.d(TAG, "loadAfter params key " + (params.key + 1));
usersRepository.getUsersAfter(params.key + 1, params.requestedLoadSize, callback);
}
@Override
public void loadBefore(@NonNull LoadParams < Long > params, @NonNull LoadCallback < User > callback) {
/*List<User> items = fetchItemsBefore(params.key, params.requestedLoadSize);
callback.onResult(items);*/
Log.d(TAG, "loadBefore params " + (params.key - 1));
usersRepository.getUsersBefore(params.key - 1, params.requestedLoadSize, callback);
}
@NonNull@Override
public Long getKey(@NonNull User user) {
return user.getCreatedLong();
}
}
public MainFragment()
public MainFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
*/
public static MainFragment newInstance(String param1, String param2) {
MainFragment fragment = new MainFragment();
Bundle args = new Bundle();
//fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
/*if(activity != null){
activity.setTitle(R.string.main_frag_title);
}*/
View fragView = inflater.inflate(R.layout.fragment_main, container, false);
// prepare the Adapter
mUserArrayList = new ArrayList < >();
mProfileAdapter = new UsersAdapter();
// Initiate the viewModel
viewModel = ViewModelProviders.of(this).get(UsersViewModel.class);
// Initiate the RecyclerView
mProfileRecycler = (RecyclerView) fragView.findViewById(R.id.users_recycler);
mProfileRecycler.setHasFixedSize(true);
mProfileRecycler.setLayoutManager(new LinearLayoutManager(mActivityContext));
//viewModel.usersList.observe(this, mProfileAdapter::submitList);
//viewModel.getPagedListObservable().observe(this, new Observer<PagedList<User>>() {
viewModel.usersList.observe(this, new Observer < PagedList < User >> () {@Override
public void onChanged(@Nullable PagedList < User > items) {
System.out.println("onChanged");
if (items != null) {
new java.util.Timer().schedule(
new java.util.TimerTask() {@Override
public void run() {
// your code here
Log.d(TAG, "submitList size" + items.size());
mProfileAdapter.submitList(items);
}
},
5000);
}
}
});
mProfileRecycler.setAdapter(mProfileAdapter);
return fragView;
}
Обновление:
Что ж, я нашел лучшее решение, чем задержка submitList на 5 секунд.Я решил использовать цикл while до тех пор, пока данные не будут получены из базы данных firebase, когда items.size () больше 0, я прекращаю цикл и отправляю список адаптеру.
public class MainFragment extends Fragment {
private final static String TAG = MainFragment.class.getSimpleName();
private RecyclerView mUsersRecycler;
private ArrayList<User> mUserArrayList;
private UsersAdapter mUsersAdapter;
private Context mActivityContext;
private Activity activity;
private UsersViewModel viewModel;
public MainFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
*/
public static MainFragment newInstance(String param1, String param2) {
MainFragment fragment = new MainFragment();
Bundle args = new Bundle();
//fragment.setArguments(args);
return fragment;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Initiate viewModel for this fragment instance
viewModel = ViewModelProviders.of(this).get(UsersViewModel.class);
//viewModel.getPagedListObservable().observe(this, new Observer<PagedList<User>>() {
viewModel.usersList.observe(this, new Observer<PagedList<User>>() {
@Override
public void onChanged(@Nullable final PagedList<User> items) {
System.out.println("mama onChanged");
if (items != null ){
// Create new Thread to loop until items.size() is greater than 0
new Thread(new Runnable() {
int sleepCounter = 0;
@Override
public void run() {
try {
while(items.size()==0) {
//Keep looping as long as items size is 0
Thread.sleep(20);
Log.d(TAG, "sleep 1000. size= "+items.size()+" sleepCounter="+sleepCounter++);
if(sleepCounter == 1000){
break;
}
//handler.post(this);
}
//Now items size is greater than 0, let's submit the List
Log.d(TAG, "after sleep finished. size= "+items.size());
if(items.size() == 0 && sleepCounter == 1000){
// If we submit List after loop is finish with 0 results
// we may erase another results submitted via newer thread
Log.d(TAG, "Loop finished with 0 items. Don't submitList");
}else{
Log.d(TAG, "submitList");
mUsersAdapter.submitList(items);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
});
}
}