when moving in another activity by taking topicId from firebase through recView BpTransactionCompletedListener: Failed to transact (-32)? - android

here is the stucture of the database
when we fetch the data of the allTopic then these topics are showing proper on recyView but when i get the id of topic and send it to the SeeTopContent Activity then it is not fetching the data of topic in that(SeeTopContent) activity and gives the error as :
2023-02-16 23:41:04.204 1102-1102/? E/BpTransactionCompletedListener: Failed to transact (-32)
2023-02-16 23:41:03.905 1102-1102/? E/BpTransactionCompletedListener: Failed to transact (-32)

Related

How to send and receive an Array List from fragment to service

I'm trying to send an Array List using broadcast and service intent here is code for sending Intent:
intent.putExtra(AUDIO_LIST, audioList)
and this is how I'm receiving
intent.getSerializableExtra(AUDIO_LIST) as ArrayList<AudioModel>
but at the end I get a runtime exception of this type
java.lang.RuntimeException: Parcel: unable to marshal value AudioModel(uri=content://media/external/audio/media, songTitle=Over the Horizon, songArtist=Samsung, songPath=/storage/emulated/0/Samsung/Music/Over_the_Horizon.mp3)
what's possible error here...

In which part of the code in the MVVM architecture should the Room DB room be updated?

I use MVVM architecture with Room DB and LiveData in my project.
When the application works offline, the data is received from roomdb, and when the user is connected to the Internet, when entering the application, the data is received as Livedata from roomdb and a request is sent to the server to update the Room DB and update the observed list with Livedata.
I did the update as follows in the repository constructor:
public MainRepository(Application application) {
DatabaseManager databaseManager = DatabaseManager.getInstance(application);
dataDao = databaseManager.dataDao();
context = application.getApplicationContext();
getDataFromWebService();
}
Inside the getDataFromWebService() function, I retrofit the data from the server and place it asynctask on the server with OnConflictStrategy.REPLACE.
Now when the app opens, I receive and display the data with the observer.
My problem is that in this case, the data is fetched twice, but when I remove the getDataFromWebService() function from the code above, it fetches only once correctly.
Twice fetching the list has exactly the same data but the list is updated again and the animation corrupts the display of data.
Please help me how to update the database without fetching the list twice.
Is updating the database in the repository constructor correct? If not, then where should the update be done?

manually set Firebase Firestore cache

I am looking for a solution for my problem, i have a chat app which displays all the users correspondence as a Recyclerview a listener, in-order to improve user experience when a user send a message i manually update the Recyclerview with the current list plus the new message with an "uploading indication" then when the message actually uploads to Firebase the listener overrides the local list i used and updates the last message to "uploaded", problem is if i rebuild the activity the listener automatically update the Recyclerview's list to the one cached therefore the user cannot see his "uploading" message any more until it hits the server.
is was thinking if there is a manually way i can set the listener's cache to the local list without waiting for it to upload? if not maybe there is a better solution i hadn't thought about? (i have many different chats with many different users so i need to be able to fetch every chat channels own unique list)
There is an option in which you can force a query to retrieve data only from the cache. If this is what you need, you can achieve this with the help of the DocumentReference.get(Source source) and Query.get(Source source) methods.
By default, get() attempts to provide up-to-date data when possible by waiting for data from the server, but it may return cached data or fail if you are offline and the server cannot be reached. This behavior can be altered via the Source parameter.
So you can pass as an argument to the DocumentReference or to the Query the source so we can force the retrieval of data from the server only, chache only or attempt server and fall back to the cache.
So something like this will do the trick:
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference docIdRef = db.collection("tests").document("fOpCiqmUjAzjnZimjd5c");
docIdRef.get(Source.CACHE).addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
//Get data from the documentSnapshot object
}
});
In this case, we force the data to be retrieved from the cache only. If you want to force the data to be retrieved from the server only, you should pass as an argument to the get() method, Source.SERVER. More informations here.

How to implement robust offline support with rx-kotlin?

TL;DR Looking for recommendations on robust offline support using rx-kotlin.
I've followed a nice guide on offline-support in Android apps.
It works like this:
Load data from the Internet, go to step 3 if error
Store the data in the local database
Load the data from the local database
Display the data in the UI
The code is:
Observable.mergeDelayError(
loadRemoteData()
.doOnNext { writeDataToLocalDatabase(it) }
.subscribeOn(Schedulers.io()),
loadDataFromLocalDatabase()
.subscribeOn(Schedulers.io())
)
Unfortunately, this approach relies on the database code always working. If the database operations for some reason fail, everything fails, even when the data is loaded successfully from the remote server.
Is there a way I can achieve the following using rx-kotlin/rx-java?:
Load data from the Internet, go to step 3 if error
Store the data in the local database
Load the data from the local database
(if steps 2 or 3 failed) Use the data from step 1
Display the data in the UI
I'd like to avoid loading the data from the Internet twice. I'm using room + retrofit, if that matters.
EDIT:
Thanks to #MinseongPark, I ended up with the code below.
The mergeDelayError reports an error only if both the remote and the local source fails, and if the writeDataToLocalDatabase method fails (throws an Exception), then that does not keep the remote data from being reported to the UI. The information about errors in writeDataToLocalDatabase is saved by reporting it remotely.
Now, the code is robust to one of the two sources failing, and to writing new entries to the database failing.
return Observable.mergeDelayError(
loadRemoteData().doOnNext {
try {
writeDataToLocalDatabase(it)
} catch (error: Throwable) {
Timber.d(error)
Crashlytics.logException(error)
}
},
loadDataFromLocalDatabase()
)
.subscribeOn(Schedulers.io())
Try this.
Observable.mergeDelayError(
loadRemoteData()
.doOnNext { runCatching { writeDataToLocalDatabase(it) } }
.subscribeOn(Schedulers.io()),
loadDataFromLocalDatabase()
.onErrorResumeNext(Observable.empty())
.subscribeOn(Schedulers.io())
)

Using RxJava to chain request on a single thread

I'm saving the user's location in the app local database and then send it to the server. Once the server return a success, I delete the location that was sent.
Each time a point has been saved in the database I call this method:
public void sendPoint(){
amazonRetrofit.postAmazonPoints(databaseHelper.getPoints())
.map(listIdsSent -> deleteDatabasePoints(listIdsSent))
.doOnCompleted(() -> emitStoreChange(finalEvent))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(AndroidSchedulers.from(backgroundLooper))
.subscribe();
}
I query the database for the point to be send to the server
I received from the server the list of point successfully sent
Using .map(), I gather the point successfully sent and delete them from the local database
Sometimes, It happens that I call this method repeatedly without having wait for the previous request to be completed and deleted the point sent. So, when I call that method again, it will post the same point as the previous request because that previous request isn't completed yet thus haven't deleted the point using the .map() yet. Causing the server to receive duplicates...
Timeline
1st Call to postPoint()
Retrive point A,B,C from the database
Post point A,B,C to the server
2nd call to postPoint()
Retrive point A,B,C,D from the database
Post point A,B,C,D to the server
Receive success from the 1st request
Deleting A,B,C from the local database
Receive success from the 2nd request
Deleting A,B,C,D from the local database
Result:
The server database now have received : A,B,C,A,B,C,D
Each request occurs sequentially but somehow the same location points are sent to the server when I call sendPoint() too quickly. How can I fix this?
First to all you are not using observerOn operator properly, observeOn operator is applied over the steps in your pipeline, once is defined.
So if you define at the end of the pipeline just before subscribeOn, then none of your previous steps will be executed in that thread.
Also, since you need to wait until the response of your server call, you can use the callbacks handlers that Subscriber already provide (onNext(), onComplete())
public void sendPoint(){
Observable.from(databaseHelper.getPoints())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(poins-> amazonRetrofit.postAmazonPoints(points))
.subscribeOn(AndroidSchedulers.from(backgroundLooper))
.subscribe(listIdsSent-> deleteDatabasePoints(listIdsSent), () -> emitStoreChange(finalEvent));
}
if you want to see more examples of ObserverOn and SubscribeOn you can take a look here. https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/scheduler/ObservableAsynchronous.java
You should have some kind of validation on the client side or/and on the backend side.
Client side:
The simplest solution is to add two columns to the table with locations like "processing" and "uploaded".
When you select locations from database and clausure where processing=false and uploaded=false.
Then when you have rows ready to sent set processing=true and when the server returns success set done=true.
Backend side (optional, depends on requirements):
You should send location with timestamp to the server (probably one more additional column in your client side table). If the server gets a location with timestamp older than the last one in a database it shouldn't store it.
RxJava solution:
You can implement a similar solution with memory cache which is kept around all sendPoint as List.
Pseudocode:
public void sendPoint(){
databaseHelper.getPoints()
.filter(points -> pointsNotInCache())
.map(points -> amazonRetrofit.postAmazonPoints())
.map(points -> addToCache())
.map(listIdsSent -> deleteDatabasePoints(listIdsSent))
.map(listIdsSent -> removeSentPointsFromCache()) //if you would like save memory
.doOnCompleted(() -> emitStoreChange(finalEvent))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(AndroidSchedulers.from(backgroundLooper))
.subscribe();
}
It looks like, as everyone else is saying, you need an intermediate cache.
i.e.
HashSet<Point> mHashSet = new HashSet<>();
public void sendPoint() {
Observable.from(databaseHelper.getPoints())
.filter(point -> !mHashSet.contains(point))
.doOnNext(mHashSet::put)
.toList()
.flatMap(amazonRetrofit::postAmazonPoints)
.map(this::deleteDatabasePoints)
.doOnCompleted(() -> emitStoreChange(finalEvent))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(AndroidSchedulers.from(backgroundLooper))
.subscribe();
}

Categories

Resources