Delete Realm object from RealmRecyclerViewAdapter - android

I'm getting a lot of IllegalStateExceptions, that are hard for me to debug.
They occur when I'm deleting a Realm object from a RealmAdapter.
For the RealmAdapter, I'm using the 'io.realm:android-adapters:1.3.0' version.
This is how I delete my object on long press in my adapter:
//Delete message object
new MaterialDialog.Builder(holder.cardView.getContext()).title("Delete")
.content("Delete message?")
.positiveText("YES")
.negativeText("Cancel")
.onPositive(new MaterialDialog.SingleButtonCallback() {
#Override
public void onClick(#NonNull MaterialDialog dialog, #NonNull DialogAction which) {
//Call delete
ApiManager.ApiManagerService apiManagerService = ApiManager.createService(ApiManager.ApiManagerService.class);
apiManagerService.deleteMessage(message.getId()).enqueue(new Callback<Void>() {
#Override
public void onResponse(Call<Void> call, Response<Void> response) {
Logger.d("delete message onResponse callback");
//Delete from realm
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
try {
message.deleteFromRealm();
} catch(Exception e) {
e.printStackTrace();
}
realm.commitTransaction();
//Notify adapter
notifyDataSetChanged();
}
#Override
public void onFailure(Call<Void> call, Throwable t) {
Logger.d("delete message onFailure callback");
}
});
}
})
.show();
return false;
}
Is the correct way to handle deleting an object from inside an adapter?

This
#Override
public void onResponse(Call<Void> call, Response<Void> response) {
Logger.d("delete message onResponse callback");
//Delete from realm
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
try {
message.deleteFromRealm();
} catch(Exception e) {
e.printStackTrace();
}
realm.commitTransaction();
//Notify adapter
notifyDataSetChanged();
}
should be
#Override
public void onResponse(Call<Void> call, Response<Void> response) {
Logger.d("delete message onResponse callback");
//Delete from realm
final long messageId = message.getId();
Realm realm = Realm.getDefaultInstance(); // TODO: use Realm bound to lifecycle
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.where(Message.class).equalTo("messageId", messageId).findAll().deleteAllFromRealm();
}
});
realm.close();
//Notify adapter
////notifyDataSetChanged();
}

Related

recycelrview adapter last item is not updating after deleting using retrofit , i am using interface because have to pass product id

Activity
#Override
protected void onResume() {
super.onResume();
getCartList();
}
private void getCartList() {
showProgressDialog();
Call<List<CartList>> call = api.getCartList(1);
call.enqueue(new Callback<List<CartList>>() {
#Override
public void onResponse(Call<List<CartList>> call, Response<List<CartList>> response) {
cartLists = response.body();
if (cartLists.size() == 0) {
tvCartEmpty.setVisibility(View.VISIBLE);
progressDialog.cancel();
} else {
addToCartAdapter.setData(cartLists);
productList.setAdapter(addToCartAdapter);
progressDialog.cancel();
}
}
#Override
public void onFailure(Call<List<CartList>> call, Throwable t) {
Toast.makeText(AddToCartActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void deleteItemID(int id) {
Call<CartList> deleteCall = api.deleteCartItem(id);
deleteCall.enqueue(new Callback<CartList>() {
#Override
public void onResponse(Call<CartList> call, Response<CartList> response) {
Log.d(TAG, "deleteItem by id" + response.code());
onResume();
}
#Override
public void onFailure(Call<CartList> call, Throwable t) {
Log.d(TAG, t.getMessage());
}
});
}
i mean last is deleting but not updating on the spot even though i called onresume in delete item method.
i have to open that activity again to see updted result
i hope i will get my query solved soon!
thanks
if you want i will upload inteface and adapter code. which i do not think necessary here.
Solution
just had to add this in response class
public void onResponse(Call<List<CartList>> call, Response<List<CartList>> response) {
cartLists = response.body();
if (cartLists.size() == 0) {
tvCartEmpty.setVisibility(View.VISIBLE);
tvCartItems.setText("0 items");
addToCartAdapter.setData(cartLists);
progressDialog.cancel();
} else {
addToCartAdapter.setData(cartLists);
productList.setAdapter(addToCartAdapter);
tvCartItems.setText(cartLists.size() + " Items");
progressDialog.cancel();
}
}
you don't need to call onResume() again.
instead of that you should use the following snippet:
cartLists.removeAt(id);
addToCartAdapter.notifyDataSetChanged();
your deleteItem function would look like below code:
#Override
public void deleteItemID(int id) {
Call<CartList> deleteCall = api.deleteCartItem(id);
deleteCall.enqueue(new Callback<CartList>() {
#Override
public void onResponse(Call<CartList> call, Response<CartList> response) {
Log.d(TAG, "deleteItem by id" + response.code());
cartLists.removeAt(id);
addToCartAdapter.notifyDataSetChanged();
}
#Override
public void onFailure(Call<CartList> call, Throwable t) {
Log.d(TAG, t.getMessage());
}
});

Data is not updated normally (room database)

I need when refresh page make request to API and insert getting data to my room database. But when I try to insert data I get io.reactivex.exceptions.OnErrorNotImplementedException: UNIQUE constraint failed: data.id (code 1555). So I decided to check is my table empty and if isn't make update request, but so my recyclerview doesn't work normally, and data doesn't update properly on db. Here is my code:
private void getData() {
progressBar.setVisibility(View.VISIBLE);
APIInterface service = RetrofitInstance.getRetrofitInstance().create(APIInterface.class);
Call<DataAll> call = service.getData();
call.enqueue(new Callback<DataAll>() {
#Override
public void onResponse(Call<DataAll> call, Response<DataAll> response) {
if(response.body() != null) {
List<Data> allData = response.body().getData();
Disposable disposable = db.dataDao().dataSize().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Integer>() {
#Override
public void accept(Integer dbSize) throws Exception {
if(dbSize > 0)
updateData(allData);
else
insertData(allData);
fillListFromDb();
}
});
}
}
#Override
public void onFailure(Call<DataAll> call, Throwable t) {
tvErrorMessage.setVisibility(View.VISIBLE);
recyclerDataList.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
swipeRefreshToUpdateList.setRefreshing(false);
Toast.makeText(getApplicationContext(), R.string.errorMessage, Toast.LENGTH_LONG).show();
t.printStackTrace();
}
});
}
private void fillListFromDb() {
Disposable disposable = db.dataDao().getAllData().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<List<Data>>() {
#Override
public void accept(List<Data> data) throws Exception {
listData.clear();
listData.addAll(data);
adapter = new MyAdapter(listData);
adapter.notifyDataSetChanged();
recyclerDataList.setAdapter(adapter);
progressBar.setVisibility(View.GONE);
swipeRefreshToUpdateList.setRefreshing(false);
}
});
}
private void updateData(List<Data> data) {
Completable.fromAction( () ->
db.dataDao().updateData(data))
.subscribeOn(Schedulers.io())
.subscribe();
}
private void insertData(List<Data> data) {
Completable.fromAction(() ->
db.dataDao().addData(data)).
subscribeOn(Schedulers.io())
.subscribe();
}
And onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
// ……
swipeRefreshToUpdateList.setOnRefreshListener(() -> {
tvErrorMessage.setVisibility(View.GONE);
recyclerDataList.setVisibility(View.VISIBLE);
getData();
});
}
Please help me
If you want an insert operation to overwrite existing object in DB, then you should use OnConflictStrategy.REPLACE.
See example:
#Dao
interface CatDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCats(cats: List<Cat>)
You have to check you List<Data> one by one in your updateData
For exemple :
#Override
public void onResponse(Call<DataAll> call, Response<DataAll> response) {
if(response.body() != null) {
List<Data> allData = response.body().getData();
for (Data data:allData){
if(isStored(data.getId())){
updateData(data);
else {
insertData(data);
}
}
}
}
}
}
you can create a new method in your DataDao findDataById(string id) and use it the boolean isStored(String id) method and update it directly
Good luck ! hope that will help you.

onComplete is not getting called when returning response as Flowable

I am trying to make webservie call using retrofit and rxjava 2. i was exploring two different approach to use RxJava2. problem is i am getting response whene i use Observable but it is not working with Flowable. Logs are not getting printed when using Flowable i tried to debug it but its not going inside onNext or onComplete or onError. only onSubscribe gets executed.
1) using observable as return type
new WebRequestManager().getContactObservable(userRequest)
.subscribe(new Observer<ResponseData>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(ResponseData responseData) {
Log.e(TAG , "data "+responseData.getStatus());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Log.e(TAG , "data complete");
}
}
);
2) Using flowable as return type
new WebRequestManager().getContactFlowable(userRequest)
.subscribe(new Subscriber<ResponseData>() {
#Override
public void onSubscribe(Subscription s) {
Log.e(TAG , "contact subscription ");
}
#Override
public void onNext(ResponseData responses) {
Log.e(TAG , "contact onNext ");
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
Log.e(TAG , "contact onComplete ");
}
});
Rest contact retrofit api
public interface ContactApi {
#POST(WebRequest.GET_CONTACTS)
Flowable<ResponseData> getContactFlowable(#Body UserRequest userRequest);
#POST(WebRequest.GET_CONTACTS)
Observable<ResponseData> getContactObservable(#Body UserRequest userRequest);
}
call to webservice
public Flowable<ResponseData> getContactsData(UserRequest userRequest){
return webRequest.getWebClient().create(ContactApi.class).getContacts(userRequest);
}
public Observable<ResponseData> getContact(UserRequest userRequest){
return webRequest.getWebClient().create(ContactApi.class).getContact(userRequest);
}
getting retrofit instance
public static Retrofit getWebClient(){
//if(okHttpClient == null)
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(120,TimeUnit.SECONDS)
.readTimeout(120,TimeUnit.SECONDS)
.writeTimeout(120,TimeUnit.SECONDS)
.addInterceptor(new WebRequestInterceptor("\"application/json\""))
.build();
// if(client == null)
client = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(LoganSquareConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return client;
}
With Subscribers, you have to call request to get items:
new WebRequestManager().getContactFlowable(userRequest)
.subscribe(new Subscriber<ResponseData>() {
#Override
public void onSubscribe(Subscription s) {
Log.e(TAG , "contact subscription ");
s.request(Long.MAX_VALUE); // <---------------------------------
}
#Override
public void onNext(ResponseData responses) {
Log.e(TAG , "contact onNext ");
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
Log.e(TAG , "contact onComplete ");
}
});
See also DisposableSubscriber with its example.

RxAndroid: onCompleted not being called

I'm using rx libraries im my app to call some REST api on my server and to show the results on screen.
I'm also following the MVP design pattern. So I have a Presenter and an Interactor classes.
In MainInteractor.java I have the following method:
public Observable<Card> fetchCard(final String clientId, final CardFetchedListener listener) {
Log.i(TAG, "FetchCard method");
// Manipulate the observer
return CARDS
.doOnCompleted(new Action0() {
#Override
public void call() {
Log.d(TAG, "CARDS Completed");
}
})
.flatMap(new Func1<Card, Observable<Card>>() {
#Override
public Observable<Card> call(final Card card) {
return ResourceClient.getInstance(card)
.getIDCard()
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.w(TAG, "interactor -> fetchCard 2", throwable);
}
}
})
.flatMap(new Func1<CardMeta, Observable<Card>>() {
#Override
public Observable<Card> call(CardMeta cardMeta) {
card.setCardMeta(cardMeta);
saveOrUpdateCardToTheDb(card);
return Observable.just(card);
}
})
.doOnCompleted(new Action0() {
#Override
public void call() {
Log.d(TAG, "Completed body");
}
});
}
});
}
In the logs I can see the "Completed Body" string.
The above method is being called by MainPresenter.java class as follows:
interactor.fetchCard(clientId, this)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Card>() {
#Override
public void onCompleted() {
Log.i(TAG, "fetchCard onCompleted");
view.hideProgressDialog();
view.updateCardsAdapter(cards);
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Fetch Card error ", e);
onFailure(parseThrowable(e));
}
#Override
public void onNext(Card card) {
if (card != null) {
Log.i(TAG, card.getTenant() + " was fetched and will be displayed");
}
}
});
The problem is that the onCompleted method in the Presenter class is never bein called. I have tried to call onCompleted myself and it worked, but the problem is I don't know actually when the observable has finished emitting cards.
What am I doing wrong here?
UPDATE
CARDS is also an observable that contains meta info. It is initialized using
Observable.from(tenants)
.filter(...).flatMap(// I'm using create operator here and it is calling its onCompleted method successflly);

Saving and retrieving data from realm database in android

Hi i used realm to save data into relam database but its not saving data to database
In my application class
Realm.init(this);
RealmConfiguration config = new RealmConfiguration.Builder().build();
Realm.deleteRealm(config);
Realm.setDefaultConfiguration(config);
In my saving activity
public void SaveUserData(final UserData userData, Realm realm)
{
final String userdata = new Gson().toJson(userData);
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// realm.createObjectFromJson(com.mediquick.databaseModel.UserData.class, userdata);
com.mediquick.databaseModel.UserData user = realm.createObject(com.mediquick.databaseModel.UserData.class);
user.setLname(userData.getLname());
user.setEmail(userData.getEmail());
user.setToken(userData.getToken());
user.setAddress(userData.getAddress());
user.setMobile_no(userData.getMobile_no());
user.setDob(userData.getDob());
user.setName(userData.getName());
user.setProfile_pic(userData.getProfile_pic());
user.setUser_type(userData.getUser_type());
}
});
And when i retrieve a value
public String getUserToken(Realm realm)
{
final com.mediquick.databaseModel.UserData user = realm.where(com.mediquick.databaseModel.UserData.class).findFirst();
return (user!=null)?user.getToken():null;
}
It gives null back when i retrive token from the database
Any ideas..
Declare Realm Object before onCreate function:
Realm realmDB;
Now write these line in onCreate Method:
Realm.init(this);
realmDB=Realm.getDefaultInstance();
On save button click method:
try{
realmDB.beginTransaction();
UserSchedule us = realmDB.createObject(UserSchedule.class);
us.setId(userData.get_ID());
us.setDate(userData.getDate());
realmDB.commitTransaction();
}
catch (Exception ex){
Log.d("RError",ex.toString());
Toast.makeText(this, "Error in realm", Toast.LENGTH_SHORT).show();
}
To get records:
RealmResults<UserSchedule>userSchedules=realmDB.where(UserSchedule.class).findAll();
for (UserSchedule userSchedule:userSchedules)
{
Toast.makeText(this,userSchedule.getId(), Toast.LENGTH_SHORT).show();
Toast.makeText(this, userSchedule.getId(), Toast.LENGTH_SHORT).show();
}
To delete record:
RealmResults<UserSchedule> result = realmDB.where(UserSchedule.class).findAll();
UserSchedule userSchedule = result.where().equalTo("Id", getItem(position).getS_ID()).equalTo("Date",getItem(position).getDate()).findFirst();
if(userSchedule!=null)
{
if (!realmDB.isInTransaction())
{
realmDB.beginTransaction();
}
userSchedule.deleteFromRealm();
realmDB.commitTransaction();
}
else
{
Toast.makeText(this,"No Record.", Toast.LENGTH_SHORT).show();
}
Could you try this save function:
// SAY YES TO THIS
Realm realm = null;
try { // I could use try-with-resources here
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.insertOrUpdate(dog);
}
});
} finally {
if(realm != null) {
realm.close();
}
}
In your code you are missing call
realm.insertOrUpdate(user)
inside executeTransaction function.
public void saveData(){
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
Students students = bgRealm.createObject(Students.class);
students.setName(name.getText().toString().trim());
students.setCity(city.getText().toString().trim());
students.setDegree(degree.getText().toString().trim());
students.setGender(gender.getSelectedItem().toString().trim());
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// Transaction was a success.
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
}
});
}

Categories

Resources