Skip to content

Fix tons of bugs relating to null snapshot.values and improve perf for good citizens #871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.firebase.uidemo.database;

import android.os.Bundle;
import android.view.View;

import com.firebase.ui.database.FirebaseIndexRecyclerAdapter;
Expand All @@ -13,12 +12,6 @@
public class ChatIndexActivity extends ChatActivity {
private DatabaseReference mChatIndicesRef;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mChatIndicesRef = FirebaseDatabase.getInstance().getReference().child("chatIndices");
}

@Override
public void onClick(View v) {
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
Expand All @@ -34,6 +27,11 @@ public void onClick(View v) {

@Override
protected FirebaseRecyclerAdapter<Chat, ChatHolder> getAdapter() {
mChatIndicesRef = FirebaseDatabase.getInstance()
.getReference()
.child("chatIndices")
.child(FirebaseAuth.getInstance().getCurrentUser().getUid());
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a uid to the indices to make them more realistic.


return new FirebaseIndexRecyclerAdapter<Chat, ChatHolder>(
Chat.class,
R.layout.message,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,11 @@ public void onChildMoved(DataSnapshot snapshot, String previousChildKey) {
int oldIndex = getIndexForKey(snapshot.getKey());
mSnapshots.remove(oldIndex);

int newIndex = previousChildKey == null ? 0 : (getIndexForKey(previousChildKey) + 1);
int newIndex = previousChildKey == null ? 0 : getIndexForKey(previousChildKey) + 1;
mSnapshots.add(newIndex, snapshot);

notifyChangeEventListeners(ChangeEventListener.EventType.MOVED,
snapshot,
newIndex,
oldIndex);
notifyChangeEventListeners(
ChangeEventListener.EventType.MOVED, snapshot, newIndex, oldIndex);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,18 +140,32 @@ protected List<DataSnapshot> getSnapshots() {
return mDataSnapshots;
}

private int getIndexForKey(String key) {
int dataCount = size();
int index = 0;
for (int keyIndex = 0; index < dataCount; keyIndex++) {
String superKey = mKeySnapshots.getObject(keyIndex);
if (key.equals(superKey)) {
break;
} else if (mDataSnapshots.get(index).getKey().equals(superKey)) {
index++;
private int returnOrFindIndexForKey(int index, String key) {
int realIndex;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we initialize realIndex to 0 for clarity? If key.equals(superKey) is true on the first loop then we will break and return realIndex without ever having initialized it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, we could but intellij complains because we do initialize it to dataIndex while out of the loop. Do you still want to proceed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh oops I missed that, thanks!

if (isKeyAtIndex(key, index)) {
// To optimize this query, if the expected item position is accurate, we simply return
// it instead of searching for it in our keys all over again. This ensures developers
// correctly indexing their data (i.e. no null values) don't take a performance hit.
realIndex = index;
} else {
int dataCount = size();
int dataIndex = 0;
int keyIndex = 0;

while (dataIndex < dataCount && keyIndex < mKeySnapshots.size()) {
String superKey = mKeySnapshots.getObject(keyIndex);
if (key.equals(superKey)) {
break;
} else if (mDataSnapshots.get(dataIndex).getKey().equals(superKey)) {
// Only increment the data index if we aren't passing over a null value snapshot.
dataIndex++;
}
keyIndex++;
}

realIndex = dataIndex;
}
return index;
return realIndex;
}

/**
Expand All @@ -173,11 +187,15 @@ protected void onKeyAdded(DataSnapshot data) {
protected void onKeyMoved(DataSnapshot data, int index, int oldIndex) {
String key = data.getKey();

// We can't use `returnOrFindIndexForKey(...)` for `oldIndex` or it might find the updated
// index instead of the old one. Unfortunately, this does mean move events will be
// incorrectly ignored if our list is a subset of the key list e.g. a key has null data.
if (isKeyAtIndex(key, oldIndex)) {
DataSnapshot snapshot = removeData(oldIndex);
int realIndex = returnOrFindIndexForKey(index, key);
mHasPendingMoveOrDelete = true;
mDataSnapshots.add(index, snapshot);
notifyChangeEventListeners(EventType.MOVED, snapshot, index, oldIndex);
mDataSnapshots.add(realIndex, snapshot);
notifyChangeEventListeners(EventType.MOVED, snapshot, realIndex, oldIndex);
}
}

Expand All @@ -186,10 +204,11 @@ protected void onKeyRemoved(DataSnapshot data, int index) {
ValueEventListener listener = mRefs.remove(mDataRef.getRef().child(key));
if (listener != null) mDataRef.child(key).removeEventListener(listener);

if (isKeyAtIndex(key, index)) {
DataSnapshot snapshot = removeData(index);
int realIndex = returnOrFindIndexForKey(index, key);
if (isKeyAtIndex(key, realIndex)) {
DataSnapshot snapshot = removeData(realIndex);
mHasPendingMoveOrDelete = true;
notifyChangeEventListeners(EventType.REMOVED, snapshot, index);
notifyChangeEventListeners(EventType.REMOVED, snapshot, realIndex);
}
}

Expand Down Expand Up @@ -225,10 +244,13 @@ public String toString() {
* A ValueEventListener attached to the joined child data.
*/
protected class DataRefListener implements ValueEventListener {
/** Cached index to skip searching for the current index on each update */
private int currentIndex;

@Override
public void onDataChange(DataSnapshot snapshot) {
String key = snapshot.getKey();
int index = getIndexForKey(key);
int index = currentIndex = returnOrFindIndexForKey(currentIndex, key);

if (snapshot.getValue() != null) {
if (isKeyAtIndex(key, index)) {
Expand Down