I used FirebaseRecyclerAdapter to get all the childs of "Pro" using a Model class named " Spacecraft" and now I want to retrieve all the candidates into a child of Pro like "1"
I created a public static "candidat" into "Spacecraft" and I used the setters and getters but still the same error
This is my database:
this is the Model Class
public class Spacecraft{
private String name;
private String desc;
private String last;
private candidat candidat;
public Spacecraft.candidat getCandidat() {
return candidat;
}
public void setCandidat(Spacecraft.candidat candidat) {
this.candidat = candidat;
}
public Spacecraft() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
public static class candidat{
private String info;
private String namecandid;
public candidat(){}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String getNamecandid() {
return namecandid;
}
public void setNamecandid(String namecandid) {
this.namecandid = namecandid;
}
}
}
This is my code for FirebaseRecyclerAdapter
#Override
protected void onStart() {
super.onStart();
FirebaseRecyclerAdapter<Spacecraft, candidatviewholder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Spacecraft, candidatviewholder>(
Spacecraft.class,
R.layout.candidat,
candidatviewholder.class,
query){
#Override
protected void populateViewHolder(candidatviewholder viewHolder, Spacecraft model, int position) {
viewHolder.setName1(model.getCandidat().getNamecandid());
viewHolder.setInfo1(model.getCandidat().getInfo());
}
};
rv.setAdapter(firebaseRecyclerAdapter);
}
The error:
No setter/field for key1 found on class com.example.ilyas.evotingapplication.Spacecraft$candidat
I had this error but the above solutions didn't fix it. Hopefully, this alternate solution will help others. If you have that error occur for almost every variable, chances are that you have Proguard enabled and it is removing the un-used getter and setter methods. To fix this, add a line similar to this to your proguard-rules.pro file:
-keep class com.example.yourapp.ObjectClass
where ObjectClass is the name of your java object class that is stored to Firebase.
I think it's just that your data models on Firebase and in Java differ.
In your java class, the Spacecraft class has a candidat field of type Candidat. But, in the database, the candidat field is really a nested object (map), containing one key Key1, which value is a Candidat structure.
So, depending on what did you want to achieve:
if you wanted each spacecraft to have exactly one candidat: save the database object properly, so {info: "info 1", namecandid: "name 1"} is saved directly under candidat field, not one level deeper, so the field has type Candidat in the code.
if you wanted each spacecraft to have a few candidats: instead of private Candidat candidat field, it should be typed Map<String, Candidat>, because that's the type it has in your database screenshot.
Work for me:
-keepclassmembers class com.myPackageName.MyClassName { *; }
Related
There is no real documentation about how to properly store the auto-generated ID of a Firestore document in a custom Java object. Retrieving the ID is easy, but how to properly store it to avoid redundancy.
This is my approach:
Model class:
public class Note {
private String documentId;
private String title;
private String description;
public Note() {
//public no arg constructor necessary
}
public Note(String title, String description) {
this.title = title;
this.description = description;
}
#Exclude
public String getDocumentId() {
return documentId;
}
public void setDocumentId(String documentId) {
this.documentId = documentId;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
}
Load data:
public void loadNotes(View v) {
notebookRef.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
List<Note> noteList = new ArrayList<>();
for (QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
Note note = documentSnapshot.toObject(Note.class);
note.setDocumentId(documentSnapshot.getId());
noteList.add(note);
}
}
});
}
My questions:
1) Is the #Exclude on the getter method enough? Should I also add it to the setter? Or to the field declaration?
2) Am I missing a more convenient way to handle the document ID in a model class?
There's finally a proper way to do this now. You just need to annotate the field with #DocumentId. More details
In your case
public class Note {
#DocumentId
private String documentId;
...
}
As noted by #fuadj, this is available starting on Cloud Firestore version 20.2.0
#Exclude on the getter is sufficient.
There is not really a "proper" way to do what you're doing. It looks like you're handling this the way you need, and that's fine.
If you would like to see a more formalized and automated way of mapping the document ID into a javabean, that sounds like a feature request you could file. Maybe another annotation could be added that indicates which field you would like to use to store the ID.
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;
}
}
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;
}
}
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:
How to Convert Firebase data to Java Object...? for an overview of the options when reading/writing the database from Java.
I would like to observe a #Bindable via Java, is it possible?
I read that I can observe a ObservableField on this answer:
https://stackoverflow.com/a/31885802/858257
But sometimes you need the primitive field and the best approach is using a #Bindable.
Sure you can. If you have a field marked with #Bindable and implement Observable, you can listen for changes to that field. Any bindable field must notify when changed. For example:
public class Item extends BaseObservable {
private String name;
private int stockCount;
#Bindable
public String getName() { return name; }
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
#Bindable
public int getStockCount() { return stockCount; }
public void setStockCount(int stockCount) {
this.stockCount = stockCount;
notifyPropertyChanged(BR.stockCount);
}
}
You can then listen for changes on this object. I used BaseObservable as the base class for this data class because it implements the observability for me.
public void listenForStockChange(Item item) {
item.addOnPropertyChangedCallback(new OnPropertyChangedCallback() {
#Override
public void onPropertyChanged(Observable sender, int propertyId) {
if (propertyId == BR.stockCount) {
Item item = (Item) sender;
// Do whatever you want when the stock changes
}
}
});
}
I have data in my firebase DB, everything works fine until I try to De-serialize the data.
Error: argument 1 has type io.realm.RealmList, got java.util.ArrayList
Here's my code:
DatabaseReference root = FirebaseDatabase.getInstance().
getReferenceFromUrl("https://swing-8792d.firebaseio.com/playlist");
Query playlistQuery = root.orderByKey().equalTo(key);
playlistQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
Log.d("Child", child + "");
Playlist receivedPlaylist = child.getValue(Playlist.class);
Playlist playlist = new Playlist();
playlist.setCreatedBy(receivedPlaylist.getCreatedBy());
playlist.setName(receivedPlaylist.getName());
playlist.setMyMap(receivedPlaylist.getMyMap());
playlist.setQrKey(receivedPlaylist.getQrKey());
playlist.setCount(receivedPlaylist.getCount());
playlist.setId(receivedPlaylist.getId());
playlist.setTracks(receivedPlaylist.getTracks());
mPlaylist.add(playlist);
}
This is my POJO class:
#RealmClass
public class Playlist extends RealmObject {
String name;
Long id;
RealmList<Track> tracks;
Integer count;
String createdBy;
RealmList<UserMap> myMap;
String qrKey;
public RealmList<UserMap> getMyMap() {
return myMap;
}
public void setMyMap(RealmList<UserMap> myMap) {
this.myMap = myMap;
}
public Playlist(){}
public String getQrKey() {
return qrKey;
}
public void setQrKey(String qrKey) {
this.qrKey = qrKey;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public RealmList<Track> getTracks() {
return tracks;
}
public void setTracks(RealmList<Track> tracks) {
this.tracks = tracks;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
If I try to de-serialize with Normal POJO class (i.e Removing Realm) it works fine.
Firebase won't work with classes that do not have default constructor or private variables i.e no public getter/setter.
A easier solution in your case would be to make a middleware class that is the same pojo just not extending RealmObject. Next initialise your RealmObject subclass using the values of the pojo.
Pseudo code
class SimplePojoPlaylist {
public String variable;
}
class Playlist extends RealmObject {
public String variable;
}
Then first cast into SimplePojoPlaylist
for (DataSnapshot child : dataSnapshot.getChildren()) {
SimplePojoPlaylist receivedPlaylist = child.getValue(SimplePojoPlaylist.class);
Playlist playList = new Playlist();
playList.variable = receivedPlaylist.variable;
}
RealmList is not a supported type for deserialization. Your database checks its structure and deduces that tracks should be an ArrayList. Then, when it tries to convert it, it finds that the types do not match.
Check this link from the docs:
Also, it is a good practice to make your objects immutable to avoid unwanted access and/or modifications.
Creating an empty object from scratch and then calling setter methods to define its state is not a very good pattern, because it can create a situation where an object is accessed before when its state is "broken".
If you need to create an object that is flexible, has a few mandatory fields and some optional, consider using the Builder pattern, although to do it you'd have to redesign your model.
wikipedia - Builder
If you don't need/want to use a builder, my advice is:
1) Make the empty constructor private and create another public one that requires all the fields.
2) Change your tracks field to be of type "List". Then, if you need the object to return a RealmList create another getter method such as tracksAsRealmList() that makes a RealmList out of the member list and returns it.
3) Make sure that the "Track" model has an empty private constructor, a public one with all of its parameters and that all of its fields are supported by firebase deserialization.
4) Unless strictly necessary, make your object fields private and set its value through a setter method.
I hope this helps you.
I have data in my firebase DB, everything works fine until I try to De-serialize the data.
Error: argument 1 has type io.realm.RealmList, got java.util.ArrayList
Here's my code:
DatabaseReference root = FirebaseDatabase.getInstance().
getReferenceFromUrl("https://swing-8792d.firebaseio.com/playlist");
Query playlistQuery = root.orderByKey().equalTo(key);
playlistQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
Log.d("Child", child + "");
Playlist receivedPlaylist = child.getValue(Playlist.class);
Playlist playlist = new Playlist();
playlist.setCreatedBy(receivedPlaylist.getCreatedBy());
playlist.setName(receivedPlaylist.getName());
playlist.setMyMap(receivedPlaylist.getMyMap());
playlist.setQrKey(receivedPlaylist.getQrKey());
playlist.setCount(receivedPlaylist.getCount());
playlist.setId(receivedPlaylist.getId());
playlist.setTracks(receivedPlaylist.getTracks());
mPlaylist.add(playlist);
}
This is my POJO class:
#RealmClass
public class Playlist extends RealmObject {
String name;
Long id;
RealmList<Track> tracks;
Integer count;
String createdBy;
RealmList<UserMap> myMap;
String qrKey;
public RealmList<UserMap> getMyMap() {
return myMap;
}
public void setMyMap(RealmList<UserMap> myMap) {
this.myMap = myMap;
}
public Playlist(){}
public String getQrKey() {
return qrKey;
}
public void setQrKey(String qrKey) {
this.qrKey = qrKey;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public RealmList<Track> getTracks() {
return tracks;
}
public void setTracks(RealmList<Track> tracks) {
this.tracks = tracks;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
If I try to de-serialize with Normal POJO class (i.e Removing Realm) it works fine.
Firebase won't work with classes that do not have default constructor or private variables i.e no public getter/setter.
A easier solution in your case would be to make a middleware class that is the same pojo just not extending RealmObject. Next initialise your RealmObject subclass using the values of the pojo.
Pseudo code
class SimplePojoPlaylist {
public String variable;
}
class Playlist extends RealmObject {
public String variable;
}
Then first cast into SimplePojoPlaylist
for (DataSnapshot child : dataSnapshot.getChildren()) {
SimplePojoPlaylist receivedPlaylist = child.getValue(SimplePojoPlaylist.class);
Playlist playList = new Playlist();
playList.variable = receivedPlaylist.variable;
}
RealmList is not a supported type for deserialization. Your database checks its structure and deduces that tracks should be an ArrayList. Then, when it tries to convert it, it finds that the types do not match.
Check this link from the docs:
Also, it is a good practice to make your objects immutable to avoid unwanted access and/or modifications.
Creating an empty object from scratch and then calling setter methods to define its state is not a very good pattern, because it can create a situation where an object is accessed before when its state is "broken".
If you need to create an object that is flexible, has a few mandatory fields and some optional, consider using the Builder pattern, although to do it you'd have to redesign your model.
wikipedia - Builder
If you don't need/want to use a builder, my advice is:
1) Make the empty constructor private and create another public one that requires all the fields.
2) Change your tracks field to be of type "List". Then, if you need the object to return a RealmList create another getter method such as tracksAsRealmList() that makes a RealmList out of the member list and returns it.
3) Make sure that the "Track" model has an empty private constructor, a public one with all of its parameters and that all of its fields are supported by firebase deserialization.
4) Unless strictly necessary, make your object fields private and set its value through a setter method.
I hope this helps you.