I am getting error while i build android chat app using firebase. When i send message then it's ok but after send the message i get error. Error look like as below.
04-17 15:21:54.242 12961-12961/learning.firebase.app.learningfirebase E/AndroidRuntime: FATAL EXCEPTION: main
com.firebase.client.FirebaseException: Failed to bounce to type
at com.firebase.client.DataSnapshot.getValue(DataSnapshot.java:183)
at learning.firebase.app.learningfirebase.FirebaseListAdapter$1.onChildAdded(FirebaseListAdapter.java:63)
at com.firebase.client.core.ChildEventRegistration.fireEvent(ChildEventRegistration.java:48)
at com.firebase.client.core.view.DataEvent.fire(DataEvent.java:45)
at com.firebase.client.core.view.EventRaiser$1.run(EventRaiser.java:38)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5365)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
at dalvik.system.NativeStart.main(Native Method)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class learning.firebase.app.learningfirebase.Chat]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: java.io.StringReader#42285cd8; line: 1, column: 2]
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:984)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:276)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2034)
at com.firebase.client.DataSnapshot.getValue(DataSnapshot.java:181)
... 13 more
My MainActivity.java
public class MainActivity extends ListActivity {
private static final String FIREBASE_URL = "https://bohrachat.firebaseio.com/";
private String mUsername;
private Firebase mFirebaseRef;
private ValueEventListener mConnectedListener;
private ChatListAdapter mChatListAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupUsername();
setTitle("Chatting as " + mUsername);
// Setup our Firebase mFirebaseRef
mFirebaseRef = new Firebase(FIREBASE_URL).child("chat");
// Setup our input methods. Enter key on the keyboard or pushing the send button
EditText inputText = (EditText) findViewById(R.id.messageInput);
inputText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (actionId == EditorInfo.IME_NULL && keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
sendMessage();
}
return true;
}
});
findViewById(R.id.sendButton).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sendMessage();
}
});
}
#Override
public void onStart() {
super.onStart();
// Setup our view and list adapter. Ensure it scrolls to the bottom as data changes
final ListView listView = getListView();
// Tell our list adapter that we only want 50 messages at a time
mChatListAdapter = new ChatListAdapter(mFirebaseRef.limit(50), this, R.layout.chat_message, mUsername);
listView.setAdapter(mChatListAdapter);
mChatListAdapter.registerDataSetObserver(new DataSetObserver() {
#Override
public void onChanged() {
super.onChanged();
listView.setSelection(mChatListAdapter.getCount() - 1);
}
});
// Finally, a little indication of connection status
mConnectedListener = mFirebaseRef.getRoot().child(".info/connected").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
boolean connected = (Boolean) dataSnapshot.getValue();
if (connected) {
Toast.makeText(MainActivity.this, "Connected to Firebase", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Disconnected from Firebase", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
// No-op
}
});
}
#Override
public void onStop() {
super.onStop();
mFirebaseRef.getRoot().child(".info/connected").removeEventListener(mConnectedListener);
mChatListAdapter.cleanup();
}
private void setupUsername() {
SharedPreferences prefs = getApplication().getSharedPreferences("ChatPrefs", 0);
mUsername = prefs.getString("username", null);
if (mUsername == null) {
Random r = new Random();
// Assign a random user name if we don't have one saved.
mUsername = "JavaUser" + r.nextInt(100000);
prefs.edit().putString("username", mUsername).commit();
}
}
private void sendMessage() {
EditText inputText = (EditText) findViewById(R.id.messageInput);
String input = inputText.getText().toString();
if (!input.equals("")) {
// Create our 'model', a Chat object
Chat chat = new Chat(input, mUsername,"Today : ");
// Create a new, auto-generated child of that chat location, and save our chat data there
mFirebaseRef.push().setValue(chat);
inputText.setText("");
Bundle b=new Bundle();
b.putString("mUsername",mUsername);
b.putString("mMessage",input);
Intent intent = new Intent();
intent.putExtras(b);
intent.setAction("learning.firebase.app.learningfirebase.CUSTUM_INTENT");
sendBroadcast(intent);
}
}
}
Chat.java
public class Chat {
private String message;
private String author;
private String datetime;
// Required default constructor for Firebase object mapping
#SuppressWarnings("unused")
Chat(String message, String author,String datetime) {
this.message = message;
this.author = author;
this.datetime=datetime;
}
public String getMessage() {
return message;
}
public String getAuthor() {
return author;
}
public String getDatetime() {
return datetime;
}
}
ChatListAdapter.java
public class ChatListAdapter extends FirebaseListAdapter<Chat> {
// The mUsername for this client. We use this to indicate which messages originated from this user
private String mUsername;
public ChatListAdapter(Query ref, Activity activity, int layout, String mUsername) {
super(ref, Chat.class, layout, activity);
this.mUsername = mUsername;
}
/**
* Bind an instance of the <code>Chat</code> class to our view. This method is called by <code>FirebaseListAdapter</code>
* when there is a data change, and we are given an instance of a View that corresponds to the layout that we passed
* to the constructor, as well as a single <code>Chat</code> instance that represents the current data to bind.
*
* #param view A view instance corresponding to the layout we passed to the constructor.
* #param chat An instance representing the current state of a chat message
*/
#Override
protected void populateView(View view, Chat chat) {
// Map a Chat object to an entry in our listview
String author = chat.getAuthor();
TextView authorText = (TextView) view.findViewById(R.id.author);
authorText.setText(author + ": ");
// If the message was sent by this user, color it differently
if (author != null && author.equals(mUsername)) {
authorText.setTextColor(Color.RED);
} else {
authorText.setTextColor(Color.BLUE);
}
((TextView) view.findViewById(R.id.message)).setText(chat.getMessage());
((TextView) view.findViewById(R.id.datetime)).setText(chat.getDatetime());
}
}
My Firebase screenshot
Please help me about this. I follow firebase github chat project. After I just add one value, means date time, I get this error.
tl;dr: make the Chat class static and add a parameterless/default constructor to it.
Minimal code that works in a single file (Main.java):
public class Main {
public static class Chat {
private String message;
private String author;
private String datetime;
// Required default constructor for Firebase object mapping
public Chat() {}
public Chat(String message, String author, String datetime) {
this.message = message;
this.author = author;
this.datetime = datetime;
}
public String getMessage() { return message; }
public String getAuthor() { return author; }
public String getDatetime() { return datetime; }
}
public static void main(String[] args) throws Exception {
Firebase ref = new Firebase("https://stackoverflow.firebaseio.com/36675151/-KFaDuobfEA1FLslYZMM");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot.getValue(Chat.class));
}
public void onCancelled(FirebaseError error) {
}
});
Thread.sleep(10000);
}
}
The JSON data:
"-KFaDuobfEA1FLslYZMM": {
"author": "user73181",
"datetime": "today heyy",
"message": "xjcjcj"
}
The things that are different from your code:
I removed everything that has nothing to do with the error message. For example this is a Java program, not an Android program. That simple change removes thousands of potential problems. But since your problem remained, it means that the problem is not related to Android. This sort of isolating the problem makes it a lot easier to find the cause. It's typically known as a minimal, complete/compilable verifiable example.
I included everything into a single file. Not only does this makes it easier to copy/paste, it also means interaction between files cannot be the cause. In this case, it did mean I had to make the Chat class static, which is likely one cause of your problem.
I had to add a parameterless/default constructor, which is what Kato pointed you to on [your previous question]. (Failed to bounce to type, Chat example error when adding another value,) and what I told you to add. This is likely the second cause of your problem.
I included the JSON as text, instead of as an image. This means that you can now copy/paste my JSON to see if your problem stems from that (it doesn't btw). You can easily get your JSON by clicking the Export button in your Firebase dashboard. Doing so saves me from having to type it.
Related
The data is not going to the Database. I need to send snake name and scientific name to the cloud. I am using android studio and there is no compilation error in this code, but real time database is not updated.
This my code:-
MainActivity.java
public class MainActivity extends AppCompatActivity {
EditText Snakename;
Button upload;
Spinner SciName;
DatabaseReference databaseSnake;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Snakename=(EditText)findViewById(R.id.snakeName);
upload=(Button)findViewById(R.id.upload);
SciName=(Spinner)findViewById(R.id.scientificName);
databaseSnake= FirebaseDatabase.getInstance().getReference("snake");
upload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
addSnake();
}
});
}
private void addSnake(){
String snakename=Snakename.getText().toString().trim();
String SCINAME=SciName.getSelectedItem().toString();
if(!TextUtils.isEmpty(snakename)){
String id=databaseSnake.push().getKey();
Snake snake=new Snake(id,snakename,SCINAME);
databaseSnake.child(id).setValue(snake);
Toast.makeText(this,"Snake Added",Toast.LENGTH_LONG).show();;
}
else{
Toast.makeText(this,"You Should enter a name",Toast.LENGTH_LONG).show();
}
}
}
and my snake class:-
public class Snake {
String snakeID;
String snakeName;
String scientificName;
public Snake()
{
}
public Snake(String snakeID, String snakeName, String scientificName) {
this.snakeID = snakeID;
this.snakeName = snakeName;
this.scientificName = scientificName;
}
public String getSnakeID() {
return snakeID;
}
public String getSnakeName() {
return snakeName;
}
public String getScientificName() {
return scientificName;
}
}
How to check whether data is saved or not in database at runtime ??
try this,
if(!TextUtils.isEmpty(snakename)){
String id=databaseSnake.push().getKey();
Snake snake=new Snake(id,snakename,SCINAME);
Map userInfo = new HashMap();
userInfo.put(id, snake);
databaseSnake.updateChildren(userInfo);
Toast.makeText(this,"Snake Added",Toast.LENGTH_LONG).show();;
}
Hope it's help full.
as the offical documentation says :
If you'd like to know when your data has been committed, you can add a completion listener. Both setValue() and updateChildren() take an optional completion listener that is called when the write has been committed to the database. If the call was unsuccessful for some reason, the listener will be passed an error object indicating why the failure occurred:
databaseSnake.child(id).setValue(snake, new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
Toast.makeText(this,"Data could not be saved. " +
firebaseError.getMessage(),Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this,""Data saved successfully.",Toast.LENGTH_LONG).show();
}
}
});
I have an Android app in which I'm getting data from an API. Now I need to save this data from the API into an ActiveAndroid table.
So I've got stuck on that part.
Here is my table Partners(Active Android):
#Table(name = "Partners")
public class Partners extends Model {
#Column(name = "Name")
public String name;
public Partners() {}
public Partners(String name) {
this.name = name;
}
}
This is my POJO model:
public class Partner {
#Expose
#Column(name = "name")
private List<String> name;
public List<String> getName() {
return name;
}
public void setName(List<String> name) {
this.name = name;
}
}
And here is part of code where I'm getting data and trying to save it into ActiveAndroid table:
public class DownloadMain extends Fragment implements Callback<Partner> {
private static final String TAG = DownloadMain.class.getSimpleName();
private Button dloadPartners;
private Call callPartners;
public DownloadMain() {}
public DownloadMain newInstance() { return new DownloadMain(); }
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.download_main, container, false);
dloadPartners = (Button) view.findViewById(R.id.downloadPartners);
dloadPartners.setOnClickListener(btnListener);
callPartners = APIHelper.getApiService().getPartners();
return view;
}
Button.OnClickListener btnListener = (new Button.OnClickListener() {
#Override
public void onClick(View v) {
callPartners.clone().enqueue(DownloadMain.this);
insertPartners();
}
});
#Override
public void onResponse(Call call, Response response) {
if(response.body() == null) {
try {
response.errorBody().string();
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(getActivity(), "No Partners!", Toast.LENGTH_SHORT).show();
} else {
List<String> partners = (List<String>) response.body();
Log.d(TAG, "Number of partners received: " + partners.size());
Toast.makeText(getActivity(), "Partners downloaded!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call call, Throwable t) {
}
private void insertPartners() {
ActiveAndroid.beginTransaction();
try {
for (int i = 0; i < 100; i++) {
Partner partner = new Partner();
partner.getName();
//partner.save();
}
ActiveAndroid.setTransactionSuccessful();
} finally {
ActiveAndroid.endTransaction();
}
}
}
The problem is how to save data I get from the API into table, so I need few tips and advice how to properly do that.
Question: I need some tips how to save data I get from web with retrofit into ActiveAndroid table?
please check my old weather app project, where I'f already used ActiveAndroid.
Here's one of my saving to database methods:
public void saveCityDataToDatabase(Forecast forecast) {
ActiveAndroid.beginTransaction();
try {
if (WeatherApplication.getCityList().size() > 0) {
new Delete().from(City.class).execute();
}
forecast.getCity().getCoord().save();
forecast.getCity().save();
ActiveAndroid.setTransactionSuccessful();
} finally {
ActiveAndroid.endTransaction();
}
Log.d("DATABASE", "WeatherApplication: " + WeatherApplication.getCityList());
} }
From: https://github.com/piotrek1543/LocalWeather/blob/master/app/src/main/java/com/piotr/localweather/repositories/WeatherDatabaseRepository.java
With ActiveAndroid is really easy to save Objects like Weather or Wind, but for some purposes you would need to use TypeSerializer. In your example you would use it to deserialize and save List<String>.
As you may notice from its Github page: https://github.com/pardom/ActiveAndroid
this libary is not maintained more than two years, so I can say clearly that is deprecated and not good to use to fresh projects. It may have many bugs. For me, implementation of this lib was a pretty painful.
Instead of ActiveAndroid, I would recommend you to use Realm, greenDao or ORMLite library to store your data.
Hope it would help
First of all create a parameterless constructor and call super in it.
public Partners() {
super();
}
then a partern.save() should suffice.
To retreive all the parterns you could do something like
List<Partner> partners = new Select().from(Partner.class).execute());
if you get the Partner object fro web via retrofit, probably you are using Gson to put the json in partner object. so you can make that object extending model, put a paramterless constructor like above and put a #Table annotation.
I'm simply just trying to populate data from Firebase Database into my listview. The logs are showing the data is being retrieved, but the adapter won't set the values to the text in a single list item in the list? All it says is "No setter/field for INSERT VALUE". Which makes me think that I didn't have my setters made correctly but there were auto generated by Android Studio. I don't know what I am missing here. Any help is appreciated.
NODE OBJECT
package com.megliosolutions.ipd.Objects;
import android.graphics.Bitmap;
/**
* Created by Meglio on 6/13/16.
*/
public class NodeObject {
public String mStaticAddress;
public String mLat;
public String mLong;
public NodeObject(){
//needed for firebase
}
public NodeObject(String address, String lat, String Long){
this.mStaticAddress = address;
this.mLat = lat;
this.mLong = Long;
}
public String getmStaticAddress() {
return mStaticAddress;
}
public void setmStaticAddress(String mStaticAddress) {
this.mStaticAddress = mStaticAddress;
}
public String getmLat() {
return mLat;
}
public void setmLat(String mLat) {
this.mLat = mLat;
}
public String getmLong() {
return mLong;
}
public void setmLong(String mLong) {
this.mLong = mLong;
}
}
STATIC LISTADAPTER
/**
* Created by Meglio on 6/14/16.
*/
public class StaticListAdapter extends ArrayAdapter<NodeObject> {
public static String TAG = StaticListAdapter.class.getSimpleName();
public Context mContext;
public List<NodeObject> mNodes;
public class ViewHolder {
TextView mStaticAddress;
TextView mLAT;
TextView mLONG;
}
#Override
public int getCount() {
return mNodes.size();
}
public StaticListAdapter(Context context, List<NodeObject> objects) {
super(context, R.layout.activity_main, objects);
this.mContext = context;
this.mNodes = objects;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
NodeObject node = mNodes.get(position);
if (convertView == null) {
convertView = LayoutInflater.from(this.mContext).inflate(R.layout.node_item, null);
holder.mLONG = (TextView) convertView.findViewById(R.id.node_item_LONG);
holder.mStaticAddress = (TextView) convertView.findViewById(R.id.node_item_IP);
holder.mLAT = (TextView) convertView.findViewById(R.id.node_item_LAT);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mStaticAddress.setText(node.getStaticAddress());
holder.mLONG.setText(node.getLongitude());
holder.mLAT.setText(node.getLatitude());
return convertView;
}
}
MAINACTIVITY
public class MainActivity extends AppCompatActivity {
public static String TAG = MainActivity.class.getSimpleName();
public ListView main_ListView;
public FirebaseAuth mAuth;
public FirebaseUser mUser;
public DatabaseReference mDatabase;
//Strings
public String static_ip;
public String lat = "5.0";
public String mLong = "4.0";
public String currentUser;
//Adapters
public StaticListAdapter listAdapter;
//Node Object
NodeObject node;
public List<NodeObject> nodesList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Instances
mAuth = FirebaseAuth.getInstance();
mUser = mAuth.getCurrentUser();
mDatabase = FirebaseDatabase.getInstance().getReference();
currentUser = mUser.getUid();
main_ListView = (ListView)findViewById(R.id.Main_listview);
//Toolbar
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//[End of Toolbar]
nodesList = new ArrayList<>();
retrieveData();
listAdapter = new StaticListAdapter(getApplicationContext(),nodesList);
main_ListView.setAdapter(listAdapter);
Log.i(TAG, "USER: " + currentUser);
}
private void retrieveData() {
mDatabase.child("nodes").child(mUser.getUid())
.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
NodeObject nodeObject = dataSnapshot.getValue(NodeObject.class);
listAdapter.add(nodeObject);
listAdapter.setNotifyOnChange(true);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.getGPS:
//nothing
return true;
case R.id.addNode:
addNode();
return true;
case R.id.logout:
signOut();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void signOut() {
mAuth.signOut();
Intent intent = new Intent(MainActivity.this, Login.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Toast.makeText(getApplicationContext(), "Logging Out.", Toast.LENGTH_SHORT).show();
startActivity(intent);
}
private void addNode() {
//AlertDialog
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
dialogBuilder.setTitle("Dude, assign something...");
LayoutInflater inflater = this.getLayoutInflater();
View dialogView = inflater.inflate(R.layout.main_add_node_dialog, null);
dialogBuilder.setView(dialogView);
final EditText editText = (EditText)
dialogView.findViewById(R.id.static_et);
dialogBuilder.setPositiveButton("Assign", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
static_ip = editText.getText().toString();
String ip = static_ip;
node = new NodeObject(ip, lat, mLong);
mDatabase.child("nodes").child(currentUser).push().setValue(node);
Toast.makeText(getApplicationContext(), "Static IP: " + static_ip + " assigned!"
, Toast.LENGTH_SHORT).show();
}
}).
setNegativeButton("Or Not...", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "Fine, nvm then..."
, Toast.LENGTH_SHORT).show();
}
});
dialogBuilder.create().show();
}
}
Debugging shows that I am able to get everything client side. Logs show this as well.
06-18 18:25:42.981 12962-12962/com.megliosolutions.ipd D/MainActivity: zHF4TGnRvkeXEbKiLegiUNLGHX12:{-KKLeBAe9pV1Umm3qQMo={mStaticAddress=26161910494949, mLong=3.0, mLat=2.0}, -KKG_ACFvdX7aJOR98-o={mStaticAddress=10.223.22.250, mLong=3.0, mLat=2.0}, -KKWKMZS7WkE_xWbL3rC={mStaticAddress=, mLong=4, mLat=5}, -KKQQLITf9-7iMFlqEWR={mStaticAddress=123123123123, mLong=3.0, mLat=2.0}, -KKG_J6PKwogjBFdk52Z={mStaticAddress=10.333.555.888, mLong=3.0, mLat=2.0}}
UPDATE
The part that didn't make sense to me, but I'm sure makes sense to those who know it very well. Is comprehending what I'm reading the firebase documentation. I read over it a few time, but I guess it just wasn't clicking. I figured out that structuring the data in firebase is KEY. Without that you can't code properly because everything relies on that. Firebase makes it easy to use now that I see it working. I will be making a blog post on this to explain my troubles and how to surpass them so no one else runs into this mess. The below code will build a functional listview with firebase backend!
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
NodeObject nodeObject = dataSnapshot.getValue(NodeObject.class);
listAdapter.add(nodeObject);
listAdapter.setNotifyOnChange(true);
}
In retrieveData(), you should be using a ChildEventListener, not a ValueEventListener. You are adding Node objects as children of node/$uid with the keys generated by push(). The ValueEventListener is returning a Map<String,Object> containing all of the Nodes. That is shown in the logcat output you posted. You can use the onChildAdded() callback of ChildEventListener to get each Node as it is created and then add it to your adapter.
The warning is because the casing mismatches between your field and you setter.
Following the examples in the Firebase documentation, this seems like the better way to model the Java class:
public class NodeObject {
public String staticAddress;
public String lat;
public String lon;
public NodeObject(){
//needed for firebase
}
public NodeObject(String address, String lat, String lon){
this.staticAddress = address;
this.lat = lat;
this.lon = lon;
}
}
As an added bonus this will lead to more sensible JSON property names too.
I had the same issue just now & the answer was pretty frustrating.
I think that the issue is with your naming convention. For example, it looks like you named a variable mLat, and then your accessors/mutators are getmLat() and setmLat(). I think when Firebase is doing the deserialization, they rely on certain naming. For example, if you have a member variable named lat, you will need to have getLat() and setLat().
For your case, you might just be able to change your methods to be getMLat() and setMLat(). Although I'd suggest changing up your conventions a bit in general.
As a side note, know that your naming is typically against convention. Generally, variables prefixed by m are private member variables, accessed & mutated by public methods (like yours).
I was getting an error because I was accessing wrong variable(parent variable)
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot dataSnapshot : snapshot.getChildren()){
list.add(snapshot.getValue(CategoryModel.class));
// Here in list I should have added dataSnapshot.getValue(CategoryModel.class)
}
}
It may help.
I am having trouble retrieving a List from the Firebase. I have no trouble storing it, but as soon as I try to cast dataSnapshot.getValue() to ArrayList my app crashes, giving an exception:
HashMap cannot be casted to ArrayList
But when I tried to cast it to a HashMap, it also crashes, giving exception:
ArrayList can't be casted to hashmap
Need help please! Here is the code that is creating the problem:
Fire.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<TaskDes> td = (ArrayList<TaskDes>) dataSnapshot.getValue()
notifyDataSetChanged();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
I want to retrieve all the data in the Firebase as one List. The class TaskDes contains three fields:
class TaskDes { // definition
boolean done
String taskDescription
String taskTitle
}
You need to create a GenericTypeIndicator object to pass as DataSnapshot.getValue() parameter.
Code:
GenericTypeIndicator<List<String>> t = new GenericTypeIndicator<List<String>>() {};
List<String> yourStringArray = dataSnapshot.getValue(t);
Your Model
public class TaskDes {
private boolean done;
private String taskDescription;
private String taskTitle;
public TaskDes() {
}
public boolean isDone() {
return done;
}
public void setDone(boolean done) {
this.done = done;
}
public String getTaskDescription() {
return taskDescription;
}
public void setTaskDescription(String taskDescription) {
this.taskDescription = taskDescription;
}
public String getTaskTitle() {
return taskTitle;
}
public void setTaskTitle(String taskTitle) {
this.taskTitle = taskTitle;
}
}
You need to create a GenericTypeIndicator object to pass as DataSnapshot.getValue() parameter.
In Activity
private static final String TAG=MainActivity.class.getSimpleName();
private FirebaseDatabase database;
private DatabaseReference myRef=null;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
database=FirebaseDatabase.getInstance();
myRef=database.getReference("ADD_YOUR_REFERECE");
myRef.addValueEventListener(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot){
/* This method is called once with the initial value and again whenever data at this location is updated.*/
long value=dataSnapshot.getChildrenCount();
Log.d(TAG,"no of children: "+value);
GenericTypeIndicator<List<TaskDes>> genericTypeIndicator =new GenericTypeIndicator<List<TaskDes>>(){};
List<TaskDes> taskDesList=dataSnapshot.getValue(genericTypeIndicator);
for(int i=0;i<taskDesList.size();i++){
Toast.makeText(MainActivity.this,"TaskTitle = "+taskDesList.get(i).getTaskTitle(),Toast.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError error){
// Failed to read value
Log.w(TAG,"Failed to read value.",error.toException());
}
});
}
Make another item that contains a list for your item:
This is your item:
class TaskDes { // definition
boolean done
String taskDescription
String taskTitle
}
This is the list item
class TaskDesList { // definition
private ArreyList<TaskDes> yourlist
}
public TaskDesList(){
}
public ArrayList<TaskDes> getYourlist() {
return yourlist;
}
and when calling an EventListener
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
yourlist.clear();
taskDesList=dataSnapshot.getValue(TaskDesList.class);
if (taskDesList!=null) {
yourlist= taskDesList.getYourlist();
}
}
and now "yourlist" is a list that contains all of your "TaskDes" items
A bit late, but in case any one else needs this.
IF the list is inside another object.
The object
public class Question {
public Date date;
public String questionNumber;
public String questionText;
public QuestionType questionType;
public String multipleSelection1;
public String multipleSelection2;
public String multipleSelection3;
public Question() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
}
Then to get your array of question objects
GenericTypeIndicator<List<Question>> t = new GenericTypeIndicator<List<Question>>() {};
List<Question> questionList = dataSnapshot.getValue(t);
Apparently, the GenericTypeIndicator doesn't work for all List objects particularly when the object contains none primitive types like maps. So, if it didn't work for your use case as it didn't for me, try this alternate solution:
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<TaskDes> tDlist = new ArrayList<>();
for (DataSnapshot d: dataSnapshot.getChildren()){
TaskDes tD = d.getValue(TaskDes.class);
tDlist.add(tD);
}
notifyDataSetChanged();
}
As mentioned in the previous answers make sure your class( like TaskDes in this case) has a public constructor which is empty so the getValue method can deserialize correctly to your java class.
I don't have much experience with building well-designed object oriented systems, and this time I improvised, which lead to the system not working and not giving me any errors.
Basically in my android app, I have a user profile activity that calls a class that queries the remote database using the user ID, and returns values for user avatar and user name.
Until the class was nested inside the profile activity class it was alright, but I decided to move it out of there and change some other stuff and now when I go to My profile I do not see my avatar and I do not see my user name.
Here is the GetUserData class:
public class GetUserData extends Activity {
private String currentlyLoggedInUserString;
SharedPreferences sharedPrefs;
Editor editor;
int currentlyLoggedInUser;
private JSONParser jsonParser = new JSONParser();
private Configurationz configurationz = new Configurationz();
private ToastMaker toastMaker = new ToastMaker();
private static final String TAG_SUCCESS = "success";
private static final String TAG_USER_AVATAR = "user_avatar";
private static final String TAG_USER_NAME = "user_name";
private static final String TAG_USER_EMAIL = "user_email";
private static final String TAG_USER_SEX = "user_sex";
private static final String TAG_USER_DATE_REGISTERED = "user_date_registered";
private static final String TAG_USER_LAST_SEEN = "user_last_seen";
private static final String TAG_USER_PASSWORD = "user_password";
private static final String APP_SHARED_PREFS = "asdasd_preferences";
private String userName;
private String userEmail;
private String userSex;
private String userPassword;
private String userAvatar;
private String userDateRegistered;
private String userLastSeen;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserAvatar() {
return userAvatar;
}
public void setUserAvatar(String userAvatar) {
this.userAvatar = userAvatar;
}
public String getUserDateRegistered() {
return userDateRegistered;
}
public void setUserDateRegistered(String userDateRegistered) {
this.userDateRegistered = userDateRegistered;
}
public String getUserLastSeen() {
return userLastSeen;
}
public void setUserLastSeen(String userLastSeen) {
this.userLastSeen = userLastSeen;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedPrefs = getApplicationContext().getSharedPreferences(APP_SHARED_PREFS, Context.MODE_PRIVATE);
new GetUserDataGetter().execute();
}
public class GetUserDataGetter extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
int success;
try {
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
// fix these shitty variables.
currentlyLoggedInUser = sharedPrefs.getInt("currentLoggedInUserId", 0);
currentlyLoggedInUserString = Integer.toString(currentlyLoggedInUser);
parameters.add(new BasicNameValuePair("user_id", currentlyLoggedInUserString));
final JSONObject json = jsonParser.makeHttpRequest(configurationz.URL_PHP_GET_USER_DATA, "POST", parameters);
success = json.getInt(TAG_SUCCESS);
if (success == 1) {
// user data found
setUserLastSeen(json.getString(TAG_USER_LAST_SEEN));
setUserDateRegistered(json.getString(TAG_USER_DATE_REGISTERED));
setUserAvatar(json.getString(TAG_USER_AVATAR));
setUserSex(json.getString(TAG_USER_SEX));
setUserPassword(json.getString(TAG_USER_PASSWORD));
setUserEmail(json.getString(TAG_USER_EMAIL));
setUserName(json.getString(TAG_USER_NAME));
//return json.getString(TAG_USER_AVATAR);
return null;
} else if (success == 2) {
//toast about not being able to connect to db;
runOnUiThread(new Runnable() {
public void run() {
//this might cause some SHIT!!!!!!!!!!!! TEST IT!!!
toastMaker.toast(getBaseContext(), configurationz.ERROR_MESSAGES_SIGNUP_DEVICE_UNABLE_TO_TAKE_PHOTOS, configurationz, Toast.LENGTH_LONG);
}
});
setUserLastSeen("");
setUserDateRegistered("");
setUserAvatar("");
setUserSex("");
setUserPassword("");
setUserEmail("");
setUserName("");
return null;
} else {
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
}
and here is the MyProfile class:
public class MyProfile extends ActionBarAndSlidingMenu {
private TableRow myProfileActionButtonsHolder;
private TextView tvUserName;
private ImageButton iUserAvatar;
private Bitmap iUserAvatarBitmap;
private String avatarPath;
private String userName;
private static final String APP_SHARED_PREFS = "asdasd_preferences";
SharedPreferences sharedPrefs;
Editor editor;
int currentlyLoggedInUser;
boolean userLoggedInState = false;
private GetUserData getUserData = new GetUserData();
public MyProfile() {
super(R.string.app_name);
}
// do a check here whether this is the user themselves or some other user
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedPrefs = getApplicationContext().getSharedPreferences(APP_SHARED_PREFS, Context.MODE_PRIVATE);
setContentView(R.layout.user_profile);
// check whether user is logged in, otherwise redirect them to
// login/signup page
userLoggedInState = sharedPrefs.getBoolean("userLoggedInState", false);
if (!userLoggedInState) {
// start intent to get them out of here.
// Research whether this step is necessary at all
}
// define the view components
myProfileActionButtonsHolder = (TableRow) findViewById(R.id.userProfileActionButtonsHolder);
// set avatar image
iUserAvatar = (ImageButton) findViewById(R.id.iUserAvatar);
avatarPath = getUserData.getUserAvatar();
if (avatarPath != "") {
iUserAvatarBitmap = BitmapFactory.decodeFile(avatarPath);
iUserAvatar.setImageBitmap(iUserAvatarBitmap);
} else {
iUserAvatar.setImageResource(R.drawable.avatar_default_male);
}
//set user display name
userName = getUserData.getUserName();
tvUserName = (TextView) findViewById(R.id.tvUserName);
tvUserName.setText(userName);
// create action buttons fragment with "edit" and "settings" buttons
getSupportFragmentManager().beginTransaction().replace(R.id.userProfileActionButtonsHolder, new MyProfileActionButtonsFragment()).commit();
}
}
First, you need to read up on programming in general and proper coding guidelines in particular, as this is a bit of a chaos. As soon as your project becomes more complex, this gets unreadable and undebuggable. Second, you should read up on how Android works.
Here's your problem in a nutshell:
An Activity is not just Android's own version of a class and you can't use it as such. An Activity represents a screen that is displayed to the user. No screen to display? No Activity.
Thus, your getUserData Activity should be a regular class and not extend activity.
Now, in MyProfile you just declare a member variable with
private GetUserData getUserData = new GetUserData();
This does nothing and it certainly never runs that class' onCreate. Thus, your task is never executed and all your fields return null.
Here's what to do in a nutshell:
Create a class UserDetails that has a constructor that takes the username, etc. plus the getters necessary to get these details. Add nothing else. This is what we call Java's version of a value object.
public class UserDetails {
private final String mUsername;
public UserDetails(String username) {
mUsername = username;
}
public String getUsername() {
return mUsername;
}
}
Create an interface called IOnUserDetailsReceivedListener with the method onUserDetailsReceived(UserDetails userDetails). The reason for this is that your download task will take some time. You need to get informed when it's done and that's what we use this interface for. This is called a listener pattern.
public interface IOnUserDetailsReceivedListener {
public void onUserDetailsReceived(UserDetails userDetails);
public void onUserDetailsError();
}
Create a class Downloader that contains your AsyncTask and that has a method retrieveUserDetails(); or something. In that method, run the async task to download. When you get the data from the server, fill it into a new UserDetails(...) object and then call listener.onUserDetailsReceived(userDetails).
public class UserDetailsDownloader {
private IOnUserDetailsReceivedListener mListener;
public UserDetailsDownloader(IOnUserDetailsReceivedListener listener) {
mListener = listener;
}
public void downloadUserDetails() {
//Execute the async task here. In it's onPostExecute, do mListener.onUserDetailsReceived(userDetails).
}
private class DownloaderTask extends AsyncTask<String, Integer, UserDetails> {
#Override
protected UserDetails doInBackground(String... params) {
//Download code
//In downloading there might go stuff wrong. If so, return null as an easy method without any error handling.
UserDetails userDetails = new UserDetails("downloadedUsername");
return userDetails;
}
#Override
protected void onPostExecute(UserDetails userDetails) {
if(userDetails == null) {
if(mListener != null) {
//Something went wrong. Tell the listener.
mListener.onUserDetailsError();
}
} else {
if(mListener != null) {
//Cool! Lets pass the userDetails to the activity.
mListener.onUserDetailsReceiver(userDetails);
}
}
}
}
}
Let your activity implements IOnUserDetailsReceivedListener.
public void UserActivity extends Activity implements IOnUserDetailsReceivedListener {
private UserDetailsDownloader mUserDetailsDownloader;
public void onCreate(...) {
mUserDetailsDownloader = new UserDetailsDownloader(this);
mUserDetailsDownloader.downloadUserDetails();
}
public void onUserDetailsReceived(UserDetails userDetails) {
//Yeeh we received user data.
}
public void onUserDetailsError() {
//Something went wrong. Tell the user?
}
}
When your task is done, it'll call your Activities onUserDetailsReceived method and pass you the UserDetails value object with which you can then do what you want.
I don't know if this is your only problem or not but too much for a comment. You shouldn't use runOnUiThread() in doInBackground()
runOnUiThread(new Runnable() {
public void run() {
//this might cause some SHIT!!!!!!!!!!!! TEST IT!!!
toastMaker.toast(getBaseContext(), configurationz.ERROR_MESSAGES_SIGNUP_DEVICE_UNABLE_TO_TAKE_PHOTOS, configurationz, Toast.LENGTH_LONG);
}
});
this is why AsyncTask has onPostExecute() and its other methods...they all run on the UI Thread except for doInBackground()
Instead of return null, returnsuccessand depending on that value, do what you need to inonPostExecute()`.
Edit
onPostExecute() gets its parameter from what doInBackground() returns which is the third param in your declaration public class GetUserDataGetter extends AsyncTask<String, String, String>. So you can change that param or return a String to onPostExecute() from doInBackground().
AsyncTask Docs