I explained the script a bit. I presented two solutions in your case. The second solution is the simplest.
The reason it updates the local mMessageRecyclerView when you are offline is because Firebase has offline capabilities. Firebase starts / shuts down through a separate workflow. This workflow starts synchronization after re-entering the network β you may need to consider saving the contents of the workflows. After restarting the application, all local recording is disabled. Note that you installed mFirebaseAdapter in the main thread, as shown below:
mFirebaseAdapter = new FirebaseRecyclerAdapter<FriendlyMessage, MessageViewHolder>(FriendlyMessage.class, R.layout.item_message, MessageViewHolder.class, mFirebaseDatabaseReference.child(MESSAGES_CHILD)) { }
This means that any below 4 FirebaseRecyclerAdapter parameters change:
FriendlyMessage.class, R.layout.item_message, MessageViewHolder.class, mFirebaseDatabaseReference.child(MESSAGES_CHILD)
the observer inside the mFirebaseAdapter immediately discovers that it immediately changes and updates your mMessageRecyclerView RecyclerView. Therefore, you must ensure that you do not change any of these parameters until you successfully update Firebase.
Also note that mFirebaseDatabaseReference.child(MESSAGES_CHILD).push().setValue(friendlyMessage); , here the actual operation using the push network is happening in a separate workflow, as you know, the network operation cannot happen in the main topic, but the next line is mMessageEditText.setText(""); in the main topic. Thus, the workflow is working (successfully or unsuccessfully), the main thread has already updated your GUI.
Thus, the following solutions are possible:
1) Github's complete solution : https://github.com/uddhavgautam/UddhavFriendlyChat (you must ensure that you do not change above 4 parameters of your mFirebaseAdapter until you successfully update the Firebase update, so it's quite difficult. but it works just fine): here you create FriendlyMessage2 , which is exactly the same as FriendlyMessage (only FriendlyMessage is used instead of FriendlyMessage2 ) and use only FriendlyMessage inside onCompletionListener , as shown below:
In this solution, instead of updating the Firebase database with setValue() we use the REST record from the OkHttp client. This is because mFirebaseDatabaseReference1.child(MESSAGES_CHILD).push() triggers that update your RecyclerView locally, so using setValue() doesn't work here. onCompletionListener() comes later, so you can implement the logic inside onSuccess() . Using REST write, your RecyclerViewAdapter updates are based on firebase data. In addition, we need to serialize FriendlyMessage2 before we write using the Okhttp client through a separate workflow. So you should change your build.gradle as
update build.gradle file
compile 'com.squareup.okhttp3:okhttp:3.5.0' compile 'com.google.code.gson:gson:2.8.0'
Implementation of the setOnClickListener Method
mSendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { /* new FirebaseRecyclerAdapter<FriendlyMessage, MessageViewHolder>( FriendlyMessage.class, R.layout.item_message, MessageViewHolder.class, mFirebaseDatabaseReference.child(MESSAGES_CHILD)) */ // if (OnLineTracker.isOnline(getApplicationContext())) { friendlyMessage2 = new FriendlyMessage2(mMessageEditText.getText().toString(), mUsername, mPhotoUrl, null); JSON = MediaType.parse("application/json; charset=utf-8"); Gson gson = new GsonBuilder().setPrettyPrinting().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); myJson = gson.toJson(friendlyMessage2); client = new OkHttpClient(); new Thread(new Runnable() { @Override public void run() { try { if(post(mFirebaseDatabaseReference.child(MESSAGES_CHILD).toString(), myJson, client, JSON)) { /* again for GUI update, we call main thread */ runOnUiThread(new Runnable() { @Override public void run() { mMessageEditText.setText(""); mFirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT, null); } }); } } catch (JSONException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } boolean post(String url, String json, OkHttpClient client, MediaType JSON) throws JSONException, IOException { RequestBody body = RequestBody.create(JSON, new JSONObject(json).toString()); Request request = new Request.Builder() .url(url+".json") .post(body) .build(); Response response = client.newCall(request).execute(); if(response.isSuccessful()) { return true; } else return false; } }).start(); // mFirebaseDatabaseReference1.child(MESSAGES_CHILD).push().setValue(friendlyMessage2).addOnCompleteListener(new OnCompleteListener<Void>() { // @Override // public void onComplete(@NonNull Task<Void> task) { // FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText().toString(), mUsername, mPhotoUrl, null); // // mMessageEditText.setText(""); // mFirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT, null); // } // }); // } } });
FriendlyMessage2.java - you need to create this because your RecyclerView adapter depends on FriendlyMessage. Using FriendlyMessage, observers inside the RecyclerView adapter understand that, and therefore, refresh your view.
package com.google.firebase.codelab.friendlychat; public class FriendlyMessage2 { private String id; private String text; private String name; private String photoUrl; private String imageUrl; public FriendlyMessage2() { } public FriendlyMessage2(String text, String name, String photoUrl, String imageUrl) { this.text = text; this.name = name; this.photoUrl = photoUrl; this.imageUrl = imageUrl; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhotoUrl() { return photoUrl; } public void setPhotoUrl(String photoUrl) { this.photoUrl = photoUrl; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } }
I hope you all understand.
A simple solution from here:
2) A simple solution: you simply use OnLineTracker immediately after clicking the button, as shown below. I prefer the second method.
Decides: 1) MessageViewHolder does not populate offline. 2) MessageViewHolder is populated only when writing a Firebase database.
mSendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (OnLineTracker.isOnline(getApplicationContext())) { FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText().toString(), mUsername, mPhotoUrl, null); mFirebaseDatabaseReference.child(MESSAGES_CHILD).push().setValue(friendlyMessage); mMessageEditText.setText(""); mFirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT, null); } } });
OnLineTracker.java
public class OnLineTracker { public static boolean isOnline(Context ctx) { ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); return netInfo != null && netInfo.isConnectedOrConnecting(); } }