0

I am trying my hands on Firebase for the first time and I ran into kind of a problem. Getting data out of my Firebase storage/database only works if the getter method fits the variable name or the member variables are public. But my naming convention for member variables is mVariableName and i leave that "m" out of my getter methods name. Now I have multiple questions:

Is making the model member variables public a viable option or is that bad practice? What is the best approach here for naming? Should i name the getter methods getmName or should i leave the "m" out of the member variable names? Should I then change it for the whole project or just for this class? I just want to know what the best practices are here.

This is the class that reads the entries:

public class ImagesActivity extends AppCompatActivity {

private RecyclerView mRecyclerView;
private ImageAdapter mAdapter;

private FirebaseStorage mFirebaseStorage;
private DatabaseReference mDatabase;
private List<Upload> mUploads;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_show_images);

    mRecyclerView = findViewById(R.id.recycler_view);
    mRecyclerView.setHasFixedSize(true);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

    mUploads = new ArrayList<>();

    mFirebaseStorage = FirebaseStorage.getInstance();
    mDatabase = FirebaseDatabase.getInstance().getReference(Constants.DATABASE_PATH_UPLOADS);

    mDatabase.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
                Upload upload = postSnapshot.getValue(Upload.class);
                Log.i("UPLOAD", "Upload : " + upload.getName());
                mUploads.add(upload);
            }

            mAdapter = new ImageAdapter(getApplicationContext(), mUploads);

            mRecyclerView.setAdapter(mAdapter);

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
        }
    });
}
}

And these are the rules:

Database:

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Storage:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if true;
    }
  }
}

And the Upload.class (only works if either fields are public or getter method names fit m-convention, which is ugly):

public class Upload {

    public String mName;
    public String mImageUrl;

    public Upload() {
    }

    public Upload(String name, String imageUrl) {
        if (name.trim().equals("")) {
            name = "No Name";
        }

        mName = name;
        mImageUrl = imageUrl;
    }


    public String getName() {
        return mName;
    }

    public String getImageUrl() {
        return mImageUrl;
    }
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Florian Walther
  • 6,237
  • 5
  • 46
  • 104

2 Answers2

4

The best practice is to use a standard POJO, or Plain Old Java Object. If you do that, Firebase will get out of your way:

public final class User {
    // These names don't matter since they are private, you could call it `glubustufo` 
    // They should always be private
    private String mName;
    private String mEmail;
    // ...

    // Constructors

    // Methods should be public and use the get/set convention where the following
    // words are in CamelCase and will be translated to lowerCamelCase in the db.
    public String getName() { return mName; }

    public void setName(String name) { mName = name; }

    public String getEmail() { return mEmail; }

    public void setEmail(String email) { mEmail = email; }

    // equals, hashCode, toString
}

Edit, use this class:

public final class Upload {
    private String mName;
    private String mImageUrl;

    public Upload() {
        // Needed for Firebase reflection
    }

    public Upload(String name, String imageUrl) {
        if (name.trim().equals("")) {
            name = "No Name";
        }

        mName = name;
        mImageUrl = imageUrl;
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }

    public String getImageUrl() {
        return mImageUrl;
    }

    public void setImageUrl(String url) {
        mImageUrl = url;
    }
}
SUPERCILEX
  • 3,929
  • 4
  • 32
  • 61
  • I try that, but this way i cant read the data out of Firebase. It only works if either the getter method names fit the variable names, or the variables are public :/ – Florian Walther Dec 30 '17 at 19:14
  • It will work, do you have a public empty constructor? In the user case, it would be `public User() {}`. – SUPERCILEX Dec 30 '17 at 19:15
  • Yea, I have an empty public constructor – Florian Walther Dec 30 '17 at 19:16
  • Then it probably has something to do with your security rules or your client code. Could you post that? – SUPERCILEX Dec 30 '17 at 19:17
  • I have the rules set so everyone can access it. But it only works if the names of the getter methods match – Florian Walther Dec 30 '17 at 19:19
  • Please edit your answer with the rules and a snippet of client code. – SUPERCILEX Dec 30 '17 at 19:19
  • Hmmm, ok. So your `Upload` class has a `setName(String)` and the database field is called `name`? A bit of JSON would be nice too since I don't see something wrong at first glance. – SUPERCILEX Dec 30 '17 at 19:27
  • the database entries actually have both name and mName with same value (the some for the other fields). Very confusing – Florian Walther Dec 30 '17 at 19:28
  • And you're sure it's not working when you try again? `name` should translate to `setName`. – SUPERCILEX Dec 30 '17 at 19:31
  • 100% sure. The RecyclerView is empty unless i change the getter methods or make the variables private – Florian Walther Dec 30 '17 at 19:32
  • Well wait, the member variables (fields) need to be private. Firebase prioritizes fields over methods. – SUPERCILEX Dec 30 '17 at 19:33
  • Actually, could you post your `Upload` class too? – SUPERCILEX Dec 30 '17 at 19:33
  • This explains why it works when they are public and not when they are public. I really think the getter names have to fit and I have no clue how that fits into my usual naming convention. I put the Upload class into the question. – Florian Walther Dec 30 '17 at 19:34
  • Nope, you weren't using a POJO, use my class in the updated answer. – SUPERCILEX Dec 30 '17 at 19:39
  • Ohh, now it works. First of all, thank you. So it is because of the setter methods? And why make the class final? – Florian Walther Dec 30 '17 at 19:42
  • The class doesn't really have to be final, that's just good practice. But yeah, you need to either _only_ use fields, or _only_ use getters/setters. You can't mix and match. – SUPERCILEX Dec 30 '17 at 19:44
  • Can I ask one more question to clarify that. When I get my results from the Firebase database, Firebase uses these setter methods to create my Java object? I guess it happens here: `Upload upload = postSnapshot.getValue(Upload.class);` – Florian Walther Dec 30 '17 at 20:14
  • Yep, essentially that code gets translated to `Upload upload = new Upload(); upload.setName(...); upload.setImageUrl(...);` using [reflection](https://stackoverflow.com/a/37632/4548500). – SUPERCILEX Dec 30 '17 at 20:17
2

As an alternative to Supercilex' excellent answer, you can also use a class with only public fields (and not getters/setters):

public final class Upload {
    public String name;
    public String imageUrl;
}

In this situation Firebase will look for (or create) a JSON property that exactly matches the field name, so make sure you capitalize it correctly.

The Firebase client creates an instance of this class by looking for a parameterless constructor. In this trivial class there is no constructor, so the Java/Android compiler will generate a default, parameterless constructor for you. If you add you own constructor, be sure to also add a parameterless one (as in Supercilex' answer).

See also:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807