I'm using leak canary for detecting memory leaks. From my activity, I'm calling a method from other class for updating data in firebase realtime-database, on addOnCompleteListener() I'm showing a success toast, and on addOnFailureListener() showing error toast. As toast requires context I've extended Application to get Application context, I haven't passed context form my activity, because reading some article I came to know that passing context can cause a memory leak. Here is my class for updating in the database.
public class FirebaseUpdaterr extends Application {
private Context context;
private DatabaseReference ref=FirebaseDatabase.getInstance().getReference();
private FirebaseUser user= FirebaseAuth.getInstance().getCurrentUser();
public FirebaseUpdaterr(){
this.context=getApplicationContext();
}
public void retriveBook(String bookId, final BookInfo bookInfo){
final Book[] book = new Book[1];
ref.child("Books").child(bookId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
book[0] =dataSnapshot.getValue(Book.class);
bookInfo.onCallback(book[0]);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public void accept(final BookRequestData bookRequestData){
retriveBook(bookRequestData.getReqBookId(), new BookInfo() {
#Override
public void onCallback(Book book) {
Map<String, Object> childUpdates = new HashMap<>();
childUpdates.put("/Books/"+bookRequestData.getReqBookId(),book);
childUpdates.put("/Requests/"+bookRequestData.getReqId()+"/status",StaticValues.REQUESTE_ACCEPTED);
ref.updateChildren(childUpdates)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if(task.isSuccessful()){
Toasty.success( context, bookRequestData.getReqUserName()+"'s request accepted", Toast.LENGTH_SHORT, true).show();
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toasty.error(context, bookRequestData.getReqUserName()+"'s request is not accepted", Toast.LENGTH_SHORT, true).show();
}
});
}
});
}
}
My BookInfoActivity is large. I've added only possible reason for Memory leaks.
public class BookInfoActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
BookViewModelFactory modelFactory=new BookViewModelFactory(bookLight.getBookId());
BookViewModel viewModel = ViewModelProviders.of(this,modelFactory).get(BookViewModel.class);
LiveData<Book> liveData = viewModel.getBookLiveData();
liveData.observe(this, new Observer<Book>() {
#Override
public void onChanged(Book bookLive) {
//other stuffs
}
checkSameReq(new FirebaseCallBack() {
#Override
public void onCallback(final BookRequestData reqData) {
requestBtn.setOnClickListener(new View.OnClickListener()
{
if(requested){
FirebaseUpdaterr fireUpdate=new FirebaseUpdaterr();
fireUpdater.accept(bookRequest);
}
}
});
}
});
}
private void checkSameReq( final FirebaseCallBack firebaseCallBack) {
ref = mdatabase.getReference();
sameReqCheck=ref.child("Users").child(book.getOwnerID()).child("pendingRequest").orderByChild("reqUserId").equalTo(user.getUid());
sameReqCheckValEventListener=new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
boolean sameReqCheck=false;
final BookRequestData[] requestData = {null};
Log.e("shanto2", String.valueOf(dataSnapshot.getChildrenCount()));
for (DataSnapshot reqSnapshot: dataSnapshot.getChildren()) {
BookRequestData bookRequestData=reqSnapshot.getValue(BookRequestData.class);
if(bookRequestData.getReqBookId().equals(book.getBookId())){
sameReqCheck=true;
requestData[0] =bookRequestData;
break;
}
}
if(!sameReqCheck){
requestBooks.setText(REQUEST_BOOK);
}else{
requestBooks.setText(CANCEL_REQUEST);
}
bookStatusSetter(book);
if(book.getAcceptedReqId().length()>0){
requestRef=ref.child("Requests").child(book.getAcceptedReqId());
reqEventListener=new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
BookRequestData bookRequestData=dataSnapshot.getValue(BookRequestData.class);
if(book.getCurrentOwnerId().equals(user.getUid()) && bookRequestData.getStatus()==StaticValues.REQUESTE_ACCEPTED){
requestBooks.setText(GOT_IT);
requestData[0] =bookRequestData;
}
firebaseCallBack.onCallback(requestData[0]);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
requestRef.addValueEventListener(reqEventListener);
}else {
firebaseCallBack.onCallback(requestData[0]);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
sameReqCheck.addListenerForSingleValueEvent(sameReqCheckValEventListener);
}
}
After inspecting with leakcanary I've found following logs, Where is the error?
The provided leak trace shows no code of yours, this is most likely an Android leak. It could be a bug in AOSP, or a bug in a manufacturer implementation. You can provide this information to LeakCanary (https://github.com/square/leakcanary/issues/new/choose => Leak in Android SDK / support library) and provide the Android API version and manufacturer so that it gets tagged as a "library leak" in future releases.
Store app context It is not a good practice but It should not cause a memory leak because you only have 1 app instance . It is difficult to see what is happening but inline listeners and callbacks should be avoided. I suggest store listeners and callbacks in prívate attributes. Init them when activity is started and then put them to null when activity is stopped.
I had the same problem.
But the problem is not the Toast, but the context, so I created an extended function, where I leave the context as global, and use the local context to show the toast. take a look
Extension.kt
import android.widget.Toast.LENGTH_SHORT
import android.widget.Toast
import android.view.View
fun Context.toast(text: String, duration: Int = LENGTH_SHORT) {
Toast.makeText(
applicationContext,
text,
duration
).show()
}
fun Context.toastCustom(text: String, durationCustom: Int = LENGTH_SHORT) {
Toast(applicationContext).apply {
duration = durationCustom
view = View.inflate(applicationContext, R.layout.custom_toast, null)
val textV = view?.findViewById(R.id.toastCustomTv) as TextView
textV.text = text
show()
}
}
from Activity call
toast("message to toast", LENGTH_SHORT)
from Fragment call
requireContext().toast("message to toast", LENGTH_SHORT)
from Composable call
val localContext = LocalContext.current
localContext.toastCustom("message to toast", Toast.LENGTH_SHORT)
Toasts is leaking not anymore
Related
I am new to ReactiveX and I have a case where I want my observable to emit data to a late subscriber(whenever the observer subscribes, observable should emit the same data that it emitted previously). I made this Observable class that provide ReplaySubject's same instance to all observers (it is singleton class).
public class AccountsObservable {
private static ConnectableObservable<String> hotObservable;
private static AccountsObservable accountsObservable;
public static AccountsObservable getObject() {
if (accountsObservable == null) {
accountsObservable = new AccountsObservable();
}
return accountsObservable;
}
public ConnectableObservable<String> getObservable() {
if (hotObservable == null) {
Observable<String> observable = ReplaySubject.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("XYZ");
emitter.onComplete();
}
});
hotObservable = observable.replay();//publish
}
return hotObservable;
}
}
Similarly, this is the observer class that creates new observer instance.
public class AccountsObserver {
AccountsFetchListener listener;
public AccountsObserver(AccountsFetchListener listener) {
this.listener = listener;
}
public Observer<String> getObserver() {
return new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String accounts) {
listener.onSuccess(accounts);
}
#Override
public void onError(Throwable e) {
listener.onFailure();
}
#Override
public void onComplete() {
}
};
}
public interface AccountsFetchListener {
void onSuccess(String accounts);
void onFailure();
}
}
Here is the function where I test these observables
private void testObs() {
ConnectableObservable<String> observable = AccountsObservable.getObject().getObservable();
Observer<String> observer = new AccountsObserver(new AccountsObserver.AccountsFetchListener() {
#Override
public void onSuccess(String accounts) {
Log.e("DATA -> ", accounts);
}
#Override
public void onFailure() {
}
}).getObserver();
observable.subscribe(observer);
observable.connect();
}
I called this function "testObs()" 5 times but it emitted data only 2 times. The problem seems to be in AccountsObservable class where I provide ReplaySUbject's instance. Thanks
Your code runs fine as it is, your logs are being suppressed in logcat as per this:
We declared an application as too chatty once it logs more than 5 lines a second. Please file a bug against the application's owner that is producing this developer-verbose-debug-level class logging spam. The logs are 256KB, that means the application is creating a DOS attack and shortening the logs timepan to 6 seconds(!) making it useless for all others.
You can avoid this behaviour by whitelisting your app for logcat:
adb logcat -P '<pid or uid of your app>'
Firebase provides awesome Realtime Database service, but it is a bit outstanding from my, and I suggest that not only my, experience when this engine is used as main local storage (offline mode).
What do I expect when app is requesting some data is to see some indication of the process. But I have not found any direct functionality to get it working.
So, I created custom solution and would like to get reviews from community.
The idea is simple: bind a ValueEventListener to some version tag which creates a request with SingleValueEvent callback updating data and changing its state.
Suppose, i have a User object and for versioning I add ver property with timestamp content:
users : {
id : {
name : 'Mr. Anderson',
ver : '<timestamp>'
}
}
To see data state I introduce enum State, interface StateIndicatable, interface OnStateChangeListener:
enum State{ INIT, LOADING, LOADED, FAILED }
interface OnStateChangeListener{
void stateChanged(State newState);
}
interface StateIndicatable{
State getState();
void setOnStateChangeListener(OnStateChangeListener listener);
void clearOnStateChangeListener();
}
I wouldn't like to change the User model and I will use a wrapper which will exist on the level of view/presentation/view_model:
I'd like to create an abstract Wrapper around any versionable model:
abstract class FirebaseVersionedModelWrapper<M> implements StateIndicatable{
int version;
protected WeakReference<M> modelRef;
State state;
OnStateChangeListener onStateChangeListener;
DatabaseReference verRef;
FirebaseVersionedModelWrapper(M model, String firebasePath, OnStateChangeListener onStateChangeListener){
this.modelRef = new WeakReference(model);
this.state = State.INIT;
this.onStateChangeListener = onStateChangeListener;
verRef = FirebaseDatabase.getInstance().getReference(firebasePath + "/ver");
verRef.keepSynced(true);
verRef.addValueEventListener(
new OnValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(modelRef.get()==null){
verRef.removeEventListener(this);
return;
}
if(dataSnapshot.hasChild("ver"){
if(dataSnapshot.child("ver").getValue(Integer.class) != version){
requestUpdate();
}
}
else
setState(State.FAILED);
}
#Override
public void onCancelled(DatabaseError databaseError) {
if(modelRef.get()==null){
verRef.removeEventListener(this);
return;
}
setState(State.FAILED);
}
}
);
}
private void requestUpdate(){
setState(State.LOADING);
DatabaseReference ref = FirebaseDatabase
.getInstance
.getReference(firebasePath);
ref.keepSynced(true);
ref.addListenerForSingleValueEvent(
new OnValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
M model = modelRef.get();
if (model==null) return;
if(dataSnapshot.hasChild("ver")){
version = dataSnapshot.child("ver").getValue(Integer.class);
setFields(model, dataSnapshot);
setState(State.LOADED);
}
else{
setState(State.FAILED);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
setState(State.FAILED);
}
}
);
}
private void setState(State newState){
if (this.state == newState) return;
this.state = newState;
if (onStateChangeListener != null)
onStateChangeListener.stateChanged(newState);
}
#Override
public State getState(){
return this.state;
}
#Override
public void setOnStateChangeListener(OnStateChangeListener listener){
this.onStateChangeListener = listener;
}
#Override
public void clearOnStateChangeListener(){
this.onStateChangeListener = null;
}
protected abstract void setFields(M model, DataSnapshot dataSnapshot);
}
Here DatabaseReference#keepSynced(true) is called to be sure that the request on data path will return fresh data. This is discussable approach and not completely confirmed in my practice now.
Then I wrap the User into UserWrapper:
class UserWrapper extends FirebaseVersionedModelWrapper<User>{
FirebasedUser(User userModel, String userPath, OnStateChangeListener listener){
super(userModel, userPath, listener);
}
#Override
void setFields(User model, DataSnapshot dataSnapshot){
// setting values for model
}
}
And eventually I can instantiate the wrapper and bind indication routines to OnStateChangeListener callback:
User user = new User();
UserWrapper wrapper = new UserWrapper(
user,
new OnStateChangeListener(){
#Override
void stateChanged(State newState){
if(newState == State.LOADED){
showUserDetails();
}
else if(newState == State.FAILD){
showErrorState();
}
else{
showLoadingIndicator();
}
}
}
);
Well, as I said, any opinions and alternative solutions posted here are much appreciated!
the code is typed from memory and may have mistakes
In one of my Android activities I need to perform multiple queries to Firebase to finally show something to the user.
In summary, I need to check in a Users reference to check which course step he is currently in, then I need to read the contents of the Course to load it.
What I´m currently doing is that I have two nested listeners like this:
ref1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
ref2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//do the work
}
});
}
});
Is there a better way to do this queries when you need them sequentially?
TL;DR
Just like Parse did with Bolts, Google also provided a task framework that implement JavaScript promises. So, instead of nesting listeners, you can create a sequence of tasks.
The result will be sent to addOnSuccessListener if all tasks execute successfully.
If any of them fail during the use case execution, the sequence will be aborted and the exception is passed to addOnFailureListener.
public Task<Course> execute() {
return Tasks.<Void>forResult(null)
.then(new GetUser())
.then(new GetCourse());
}
public void updateInBackground() {
Tasks.<Void>forResult(null)
.then(new GetUser())
.then(new GetCourse())
.addOnSuccessListener(this)
.addOnFailureListener(this);
}
#Override
public void onFailure(#NonNull Exception error) {
Log.e(TAG, error.getMessage());
}
#Override
public void onSuccess(Customer customer) {
// Do something with the result
}
Description
Suppose you wish to download two object of type User and Course from Firebase.
You need to create the first task of your sequence using the Tasks API. Your options are:
Create a successful task using Tasks.forResult
Create a TaskCompletionSource, set the result or exception values, then return a task.
Create a task from a callable.
I prefer the first option mostly due code legibility. If you you need run the tasks in your own executor, you should use the first or second option.
Now let's create two Continuation tasks two download each one:
class GetUser implements Continuation<Void, Task<User>> {
#Override
public Task<User> then(Task<Void> task) {
final TaskCompletionSource<User> tcs = new TaskCompletionSource();
ref1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onCancelled(DatabaseError error) {
tcs.setException(error.toException());
}
#Override
public void onDataChange(DataSnapshot snapshot) {
tcs.setResult(snapshot.getValue(User.class));
}
});
return tcs.getTask();
}
}
and
class GetCourse implements Continuation<User, Task<Course>> {
#Override
public Task<Course> then(Task<User> task) {
final User result = task.getResult();
final TaskCompletionSource<Course> tcs = new TaskCompletionSource();
ref2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onCancelled(DatabaseError error) {
tcs.setException(error.toException());
}
#Override
public void onDataChange(DataSnapshot snapshot) {
tcs.setResult(snapshot.getValue(Course.class));
}
});
return tcs.getTask();
}
}
According the documentation, call getResult() and allow the RuntimeExecutionException to propagate to propagate failure from the completed Task.
The RuntimeExecutionException will be unwrapped such that the Task returned by continueWith(Continuation) or continueWithTask(Continuation) fails with the original exception.
According to the Firebase blog: https://firebase.googleblog.com/2016/09/become-a-firebase-taskmaster-part-3_29.html
We could implement a chain of asynchronous task as the following:
public Task<ClassReturnedByTask3> wrapAllTask() {
return Tasks.call(new Task1())
.continueWithTask(new Task2())
.continueWithTask(new Task3());
}
Where Task1 through Task3 are defined as:
static class Task1 implements Callable<ClassReturnedByTask1> {
#Override
public ClassReturnedByTask1 call() throws Exception {
ClassReturnedByTask1 result = new ClassReturnedByTask1();
return result;
}
}
static class Task2 implements Continuation<ClassReturnedByTask1, Task<ClassReturnedByTask2>> {
#Override
public Task<ClassReturnedByTask2> then(Task<ClassReturnedByTask1> task) {
final TaskCompletionSource<ClassReturnedByTask2> tcs = new TaskCompletionSource();
ClassReturnedByTask1 resultFromTask1 = task.getResult();
ClassReturnedByTask2 result = new ClassReturnedByTask2();
tcs.setResult(result);
return tcs.getTask();
}
}
static class Task3 implements Continuation<ClassReturnedByTask2, Task<ClassReturnedByTask3>> {
#Override
public Task<ClassReturnedByTask3> then(Task<ClassReturnedByTask2> task) {
final TaskCompletionSource<ClassReturnedByTask3> tcs = new TaskCompletionSource();
ClassReturnedByTask2 resultFromTask2 = task.getResult();
ClassReturnedByTask3 result = new ClassReturnedByTask3();
tcs.setResult(result);
return tcs.getTask();
}
}
To execute the wrapAllTask() function, you can run:
Task<ClassReturnedByTask3> tasks = wrapAllTask();
tasks.addOnSuccessListener(new OnSuccessListener<ClassReturnedByTask3>() {
#Override
public void onSuccess(ClassReturnedByTask3 resultFromTask3) {
// do something
}
});
I am using Firebase in my app, along with RxJava.
Firebase is capable of notify your app whenever something changed in the backend data (addition, removals, changes, ...).
I am trying to combine the feature of Firebase with RxJava.
The data I am listening for is called Leisure, and the Observable emits LeisureUpdate which contains a Leisure and the type of update (add, remove, moved, changed).
Here is my method which allows to subscribe to this events.
private Observable<LeisureUpdate> leisureUpdatesObservable;
private ChildEventListener leisureUpdatesListener;
private int leisureUpdatesSubscriptionsCount;
#NonNull
public Observable<LeisureUpdate> subscribeToLeisuresUpdates() {
if (leisureUpdatesObservable == null) {
leisureUpdatesObservable = Observable.create(new Observable.OnSubscribe<LeisureUpdate>() {
#Override
public void call(final Subscriber<? super LeisureUpdate> subscriber) {
leisureUpdatesListener = firebase.child(FirebaseStructure.LEISURES).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.ADDED));
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.CHANGED));
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.REMOVED));
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.MOVED));
}
#Override
public void onCancelled(FirebaseError firebaseError) {
subscriber.onError(new Error(firebaseError.getMessage()));
}
});
}
});
}
leisureUpdatesSubscriptionsCount++;
return leisureUpdatesObservable;
}
First off, I would like to use Observable.fromCallable() method in order to create the Observable, but I guess it is impossible, since Firebase uses callbacks, right?
I keep a single instance of the Observable in order to always have one Observable where multiple Subscriber can subscribe.
The problem comes when everyone unsubscribe and I need to stop listening for the events in Firebase.
I didn't find anyway to make the Observable understand if there is any subscription still. So I keep counting how many calls I got to subscribeToLeisuresUpdates(), with leisureUpdatesSubscriptionsCount.
Then every time someone wants to unsubscribe it has to call
#Override
public void unsubscribeFromLeisuresUpdates() {
if (leisureUpdatesObservable == null) {
return;
}
leisureUpdatesSubscriptionsCount--;
if (leisureUpdatesSubscriptionsCount == 0) {
firebase.child(FirebaseStructure.LEISURES).removeEventListener(leisureUpdatesListener);
leisureUpdatesObservable = null;
}
}
This is the only way I found to make the Observable emits items when there is a subscriber, but I feel like there must be an easier way, specially understanding when there is no more subscribers listening to the observable.
Anyone who encountered a similar problem or have a different approach?
You can use Observable.fromEmitter, something along these lines
return Observable.fromEmitter(new Action1<Emitter<LeisureUpdate>>() {
#Override
public void call(final Emitter<LeisureUpdate> leisureUpdateEmitter) {
final ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// process update
LeisureUpdate leisureUpdate = ...
leisureUpdateEmitter.onNext(leisureUpdate);
}
#Override
public void onCancelled(DatabaseError databaseError) {
leisureUpdateEmitter.onError(new Throwable(databaseError.getMessage()));
mDatabaseReference.removeEventListener(this);
}
};
mDatabaseReference.addValueEventListener(listener);
leisureUpdateEmitter.setCancellation(new Cancellable() {
#Override
public void cancel() throws Exception {
mDatabaseReference.removeEventListener(listener);
}
});
}
}, Emitter.BackpressureMode.BUFFER);
Put this in your Observable.create() at the end.
subscriber.add(Subscriptions.create(new Action0() {
#Override public void call() {
ref.removeEventListener(leisureUpdatesListener);
}
}));
I suggest you to check as reference(or just use it) one of the next libraries:
RxJava : https://github.com/nmoskalenko/RxFirebase
RxJava 2.0: https://github.com/FrangSierra/Rx2Firebase
One of them works with RxJava and the other one with the new RC of RxJava 2.0. If you are interested of it, you can see the differences between both here.
Here is a sample code for using RxJava2 with Firebase's CompletionListener:
Completable.create(new CompletableOnSubscribe() {
#Override
public void subscribe(final CompletableEmitter e) throws Exception {
String orderKey = FirebaseDatabase.getInstance().getReference().child("orders").push().getKey();
FirebaseDatabase.getInstance().getReference().child("orders").child(orderKey).setValue(order,
new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (e.isDisposed()) {
return;
}
if (databaseError == null) {
e.onComplete();
} else {
e.onError(new Throwable(databaseError.getMessage()));
}
}
});
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
Another amazing library which will help you to wrap all firebase realtime database logic under rx patterns.
https://github.com/Link184/Respiration
Here you can create your firebase repository and extend it from GeneralRepository for example:
#RespirationRepository(dataSnapshotType = Leisure.class)
public class LeisureRepository extends GeneralRepository<Leisure>{
protected LeisureRepository(Configuration<Leisure> repositoryConfig) {
super(repositoryConfig);
}
// observable will never emit any events if there are no more subscribers
public void killRepository() {
if (!behaviorSubject.hasObservers()) {
//protected fields
behaviorSubject.onComplete();
databaseReference.removeEventListener(valueListener);
}
}
}
You can "kill" your repository in that way:
// LeisureRepositoryBuilder is a generated class by annotation processor, will appear after a successful gradle build
LeisureRepositoryBuilder.getInstance().killRepository();
But I think for your situation will be better to extend com.link184.respiration.repository.ListRepository to avoid data mapping from java.util.Map to Leisure model through LeisureUpdate
I'm trying to read from Firebase (offline), but the method onDataChange is never called,
private class MyAuthStateListener implements Firebase.AuthStateListener {
#Override
public void onAuthStateChanged(AuthData authData) {
if (authData != null) {
// user is logged in
userId = authData.getUid();
mFirebase = mFirebase.child(authData.getUid()).child("xxx");
Query mQuery = mFirebase.orderByChild("externalId").equalTo(externalId);
mQuery.addListenerForSingleValueEvent(new MyValueEventListener());
}
}
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot mDataSnapshot) {
if (mDataSnapshot.hasChildren()) {
Iterable<DataSnapshot> i = mDataSnapshot.getChildren();
Iterator<DataSnapshot> mIterator = i.iterator();
if (mIterator.hasNext()) {
mArrayList.clear();
}
while (mIterator.hasNext()) {
DataSnapshot c = mIterator.next();
mArrayList.add(c.getValue(mObject.class));
}
}
onTaskComplete(); // call the notifyDataSetChange() on the adapter.
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
}
Have I done something wrong?
Online it works well, but offline it won't works.
The new objects are created with a FAB that open a new activity (to modify the object) and than I return to that activity.
Thank you all for the help.