Basically I'm trying to check if an object exists before trying to use it, if not, I'm adding it beforehand then trying to use it.
If it doesn't exist, it gets added correctly but it's still not found by the application afterwards unless I reopen the app
Everything works as intended, however, if the object is there to begin with
private class ExerciseViewPageAdapter extends FragmentPagerAdapter {
Realm realm;
ExerciseViewPageAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int pos) {
realm = Realm.getDefaultInstance();
String exercise_name = getIntent().getExtras().getString("exercise_name");
boolean found_exercise = realm.where(Exercise.class).equalTo("name", exercise_name).findAll().size() > 0;
if (!found_exercise) {
CompositeDisposable disposable = new CompositeDisposable();
ExerciseClient client = new ExerciseClient(getApplicationContext());
disposable.add(client.getExerciseByName(exercise_name)
.subscribeOn(Schedulers.io())
.subscribe(
this::getExercisesSuccess,
this::getExercisesError
)
);
}
final ArrayList<Exercise> exercise = new ArrayList<>();
exercise.add(realm.where(Exercise.class).equalTo("name", exercise_name).findFirst());
if (exercise.get(0) != null) {
switch (pos) {
//returning new fragments depending on position
}
}
//if exercise.get(0) doesn't exist at first, even tho I added it afterwards it's still null here unless I reopen the app or let it crash
finish();
realm.close();
return null;
}
private void getExercisesError(Throwable throwable) {
}
private void getExercisesSuccess(Exercise exercise) {
try (Realm r = Realm.getDefaultInstance()) {
r.executeTransaction(realm -> {
realm.insertOrUpdate(exercise);
});
}
}
#Override
public int getCount() {
return 3;
}
}
Edit: TL;DR: the problem is that the object is being seen by the realm.where()... after inserting an object prior to it only after reopening the app
You called api, it's will run on background so the code next to wrote on api call will execute. So your activity will close automatically.
call your validation on your api success getting failed
private void getExercisesSuccess(Exercise exercise) {
try (Realm r = Realm.getDefaultInstance()) {
r.beginTransaction();
realm.insertOrUpdate(exercise);
r.commitTransaction();
//checking records are in realm
final ArrayList<Exercise> exercise = new ArrayList<>();
exercise.add(realm.where(Exercise.class).equalTo("name", exercise_name).findFirst());
if (exercise.get(0) != null) {
switch (pos) {
//returning new fragments depending on position
}
}
//if exercise.get(0) doesn't exist at first, even tho I added it afterwards it's still null here unless I reopen the app or let it crash
finish();
realm.close();
}catch(Exception e){}
}
And remove below lines from your getItem()
final ArrayList<Exercise> exercise = new ArrayList<>();
exercise.add(realm.where(Exercise.class).equalTo("name", exercise_name).findFirst());
if (exercise.get(0) != null) {
switch (pos) {
//returning new fragments depending on position
}
}
//if exercise.get(0) doesn't exist at first, even tho I added it afterwards it's still null here unless I reopen the app or let it crash
finish();
realm.close();
Related
i have a fragment in my app that i show two list of saparate data in it.i'm using from android architecture components to load my data.
Once the data is fetched from the network, I store it locally using Room DB and then display it on the UI using ViewModel that observes on the LiveData object (this works fine). However, I want to be able to have a refreshLayout which When Refreshing Occurs a refresh action and perform a network request to get new data from the API if and only if there is a network connection.The issue is when Refreshing Occurs data load from locate database and network together .
my question is :How do I manage to get data only from Network when refreshing data?
How do I manage to get data only from Network when refreshing data?
I've seen this question and it didn't help me...
my codes:
repository:
public NetworkResult<LiveData<HomeHealthModel>> getHomeHealth(String query) {
MutableLiveData<String> _liveError = new MutableLiveData<>();
MutableLiveData<HomeHealthModel> data = new MutableLiveData<>();
LiveData<List<GeneralItemModel>> liveClinics = App.getDatabase().getGeneralItemDAO().getTops(GeneralItemType.Clinics, GeneralItemType.TOP);
LiveData<List<GeneralItemModel>> liveDoctors = App.getDatabase().getGeneralItemDAO().getTops(GeneralItemType.Doctors, GeneralItemType.TOP);
setupService(_liveError); //request data from network
data.postValue(new HomeHealthModel(liveClinics, liveDoctors));
_liveError.postValue(String.valueOf(NetworkResponseType.LocaleData));
return new NetworkResult<>(_liveError, data);
}
my viewModel
public class HomeHealthVM extends ViewModel {
private MutableLiveData<String> queryLiveData;
private LiveData<String> networkErrors;
private LiveData<List<GeneralItemModel>> Clinics;
private LiveData<List<GeneralItemModel>> Doctors;
public HomeHealthVM(HealthRepository repository) {
queryLiveData = new MutableLiveData<>();
LiveData<NetworkResult<LiveData<HomeHealthModel>>> repoResult;
repoResult = Transformations.map(queryLiveData, repository::getHomeHealth);
LiveData<HomeHealthModel> model = Transformations.switchMap(repoResult, input -> input.data);
Doctors = Transformations.switchMap(model, HomeHealthModel::getDoctors);
Clinics = Transformations.switchMap(model, HomeHealthModel::getClinics);
networkErrors = Transformations.switchMap(repoResult, input -> input.error);
}
public void search(String queryString) {
queryLiveData.postValue(queryString);
}
public String lastQueryValue() {
return queryLiveData.getValue();
}
public LiveData<String> getNetworkErrors() {
return networkErrors;
}
public LiveData<List<GeneralItemModel>> getClinics() {
return Clinics;
}
public LiveData<List<GeneralItemModel>> getDoctors() {
return Doctors;
}
}
my fragment code:
private void setupViewModel() {
ViewModelFactory<HealthRepository> factory = new ViewModelFactory<>(new HealthRepository());
healthVM = ViewModelProviders.of(this, factory).get(HomeHealthVM.class);
healthVM.getNetworkErrors().observe(this, states -> {
try {
if (Integer.parseInt(states) != WarningDialogType.Success &&
Integer.parseInt(states) != WarningDialogType.Locale) {
stopLoading();
linerNoInternet.setVisibility(View.VISIBLE);
linerContent.setVisibility(View.GONE);
}
} catch (Exception e) {
stopLoading();
linerNoInternet.setVisibility(View.VISIBLE);
linerContent.setVisibility(View.GONE);
}
});
healthVM.getDoctors().observe(this, doctors -> {
if (doctors.size() > 0) {
doctorsAdapter.submitList(doctors);
stopLoading();
} else {
}
});
healthVM.getClinics().observe(this, clinics -> {
if (clinics.size() > 0) {
clinicsAdapter.submitList(clinics);
stopLoading();
} else {
conesClinics.setVisibility(View.GONE);
}
});
healthVM.search("");
}
I am creating very simple android app using mvvm and repository pattern. It fetch data from network (using retrofit2/RxJava2) if app is online and saves to DB (using room) and post to observe. If app is offline, app gets the data from DB and post to observe. From activity app updates the textviews after getting observed from viewmodel class.
Everything is working very fine when app has active internet connection. When internet is not available it does not load data from DB. And that's the problem am facing with no clue.
Activity class
viewModel.loadHomeData();
viewModel.homeDataEntityResult().observe(this, this::updateTextViews);
private void updateTextViews(HomeDataEntity data) {
if (data != null) {
tv1.setText(data.todayDate);
tv2.setText(data.bnDate);
tv3.setText(data.location);
}
}
Viewmodel class
private RamadanRepository repository;
private DisposableObserver<HomeDataEntity> disposableObserver;
private MutableLiveData<HomeDataEntity> homeDataEntityResult = new MutableLiveData<>();
public LiveData<HomeDataEntity> homeDataEntityResult() {
return homeDataEntityResult;
}
public void loadHomeData() {
disposableObserver = new DisposableObserver<HomeDataEntity>() {
#Override
public void onNext(HomeDataEntity homeDataEntity) {
homeDataEntityResult.postValue(homeDataEntity);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
repository.getHomeData()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.debounce(400, MILLISECONDS)
.subscribe(disposableObserver);
}
Repository class
public Observable<HomeDataEntity> getHomeData() {
boolean hasConnection = appUtils.isOnline();
Observable<HomeDataEntity> observableFromApi = null;
if (hasConnection) {
observableFromApi = getHomeDataFromApi();
}
Observable<HomeDataEntity> observableFromDb = getHomeDataFromDb();
if (hasConnection)
return Observable.concatArrayEager(observableFromApi, observableFromDb);
else return observableFromDb;
}
private Observable<HomeDataEntity> getHomeDataFromApi() {
return apiService.getDemoHomeData()
.map(HomeDataEntity::copyFromResponse)
.doOnNext(homeDataDao::saveData);
}
private Observable<HomeDataEntity> getHomeDataFromDb() {
return homeDataDao.getHomeData()
.toObservable()
.doOnNext(homeDataEntity -> {
Timber.d("db data %s", homeDataEntity.toString());
});
}
When app is online it also prints the roomDB inserted data after fetching. What actually am missing when app is offline?
Hi I have gone through the best practices in Realm and according to it the best way to handle Realm instances is to open a realm instance in onCreate() method of an Activity using relam = Realm.getDefaultInstance() and close it in onDestroy() using realm.close().
But currently I have following singleton structure in my code. I need to know the pros and cons of the following singleton structure over the best practice suggested in realm docs.
Here is my code: Approach 1
public class RealmManager {
private static final String TAG = "RealmManager";
private RealmAsyncTask transactionManager;
private static RealmManager mInstance = null;
public static RealmManager getInstance() {
if (mInstance == null)
mInstance = new RealmManager();
return mInstance;
}
private Realm mRealm;
protected RealmManager() {
mRealm = Realm.getDefaultInstance();
}
public void saveOrUpdateChatChannel(ChatChannel channel) {
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm bgRealm) {
bgRealm.copyToRealmOrUpdate(channel);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
Log.e(TAG,"Failed to update Channel");
}
});
}
public void deleteChatChannel(String channelID, OnRealmDatabaseListener mRealmListener) {
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
RealmResults<ChatChannel> result = realm.where(ChatChannel.class).equalTo("channelId", channelID).findAll();
result.deleteAllFromRealm();
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(channelID, true);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(#NonNull Throwable error) {
}
});
}
public void closeRealm() {
if (mRealm != null) {
mRealm.close();
}
if (transactionManager != null) {
transactionManager.cancel();
}
}
}
So in the Approach 1, I will be creating realm instances in my activities, services, intent services using RealmManager.getInstance() and then continue to do transactions. And in all my Activity onDestroy() methods i am closing the realm using RealmManager.closeRealm(). So my question is, if the RealmManager.closeRealm() which is called in Activity onDestroy(), will affect any transactions which are executing in Service?
Here is my code: Approach 2
public class RealmManager {
private static RealmManager mInstance = null;
public static RealmManager getInstance() {
if (mInstance == null)
mInstance = new RealmManager();
return mInstance;
}
private Realm mRealm;
protected RealmManager(){
mRealm = Realm.getDefaultInstance();
}
public void addClockModel(ClockRLM clockRLM,OnRealmDatabaseListener mRealmListener){
RealmAsyncTask transactionManager = mRealm.executeTransactionAsync(realm -> realm.copyToRealm(clockRLM), new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d("Clocke ", "Inserted TimeStamp " + clockRLM.getTimeStamp());
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(clockRLM,true);
if (transactionManager != null)
transactionManager.cancel();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
if (transactionManager != null)
transactionManager.cancel();
}
});
}
}
So in the Approach 2, I will be creating realm instances in my activities, services, intent services using RealmManager.getInstance() and then continue to do transactions. I am not sure where to close realm if i use Approach 2. What if i do not close it anywhere and only when the app closes, the RealmManager gets destroyed and realm instance will be destroyed. OR i need to close the realm instance in the application level (I am not sure whether we can close instance in the application level).
Which one is better among Approach 1 and Approach 2. Or is it better to open a realm instance in onCreate() method of an Activity using relam = Realm.getDefaultInstance() and close it in onDestroy() using realm.close().
Realm is hard to use under a "singleton manager" because Realm.getDefaultInstance() might seem like you're getting something that's a "singleton", but it's really not. Instances of Realm are thread-local and reference-counted, each call to getInstance() increments a ref count while close() decrements it.
I've said a few times that open() would have been a better name, but I came to this conclusion far too late :)
First, the reason why your Singleton approach is not good is because:
Calls to the methods can only be done from the thread that first calls RealmManager.getInstance(), which is expected to the UI thread (but not guaranteed)
You hard-code that you want to execute each 1 operation in 1 async transaction, so you can't use this thing on a background thread too much
In order to make a realm manager that can be invoked on any threads, and UI thread uses async transaction while background thread uses sync transaction, you'd need to use a method like this.
And also, you'd need to track the open Realm instance for that given thread, so that you can access it wherever you want, without incrementing the ref count.
public class RealmManager {
private final ThreadLocal<Realm> localRealm = new ThreadLocal<>();
public Realm openLocalInstance() {
Realm realm = Realm.getDefaultInstance();
if(localRealm.get() == null) {
localRealm.set(realm);
}
return realm;
}
public Realm getLocalInstance() {
Realm realm = localRealm.get();
if(realm == null) {
throw new IllegalStateException("No open Realms were found on this thread.");
}
return realm;
}
public void closeLocalInstance() {
Realm realm = localRealm.get();
if(realm == null) {
throw new IllegalStateException(
"Cannot close a Realm that is not open.");
}
realm.close();
if(Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) {
localRealm.set(null);
}
}
With a class like this, you could do:
try {
realmManager.openLocalInstance();
// Realm is open and can be accessed on this thread with `realmManager.getLocalInstance()`
// also all your methods in the RealmManager can use `getLocalInstance()`
} finally {
realmManager.closeLocalInstance();
}
I also created a library a while ago that wraps Realm in such a way that it eliminates the need for manual reference counting, but it never really caught on. Check out the source if curious.
I'm trying to write data on one service and accessing it from another service.
The data I'm getting from another service is not updated, it's old copy.
Restarting the app sometimes gets updated data.
The services are both normal Service extended, so UI thread only.
And I'm not keeping any realm instances open anywhere in the app.
How do I ensure it's always new and updated one?
Writing -
data is detached using realm.copyFromRealm(...)
try (Realm realm = Realm.getDefaultInstance()) {
if (realm != null) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
data.setValue("New value ...");
realm.insertOrUpdate(data);
}
});
}
}
Reading -
Data data = null;
try (Realm realm = Realm.getDefaultInstance()) {
if (realm != null) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
data = realm.copyFromRealm(realm.where(Data.class).equalTo("Id", id).findFirst());
}
});
}
}
Data -
public class Data {
...
private String Value;
public String getValue() { return Value; }
public void setValue(String v) { Value = v; }
...
}
Edit -
I ended up merging both services into one, which works for now. But I'll look forward if anyone can provide some tips or has a similar problem.
How do I ensure it's always new and updated one?
Don't use copyFromRealm(). and use RealmResults field variable + RealmChangeListener
RealmResults<T> results;
RealmChangeListener<RealmResults<T>> listener = new RealmChangeListener<...>() {
#Override public void onChange(RealmResults<T> results) {
...
}
}
public void something() {
results = realm.where(...).findAll*();
results.addChangeListener(
Changes in the database from a background thread is displayed only after restart the application. Why? How to fix?
public class UILApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
RealmConfiguration config = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(config);
}
In the background is next code inserting to DB:
public void setFollows(List<JSONObject> follows) {
Realm realm = Realm.getDefaultInstance();
List<Follow> finalFollows = new RealmList<>();
try {
for(JSONObject follow:follows){
finalFollows.add(new Follow(follow.getLong("id"),follow.getString("username"), follow.getString("profile_picture"), follow.getString("full_name")));
}
List<Follow> goneFollows = getGoneFollows(finalFollows);
List<Follow> newFollows = getNewFollows(finalFollows);
realm.beginTransaction();
if(goneFollows != null && !goneFollows.isEmpty()){
goneFollows = realm.copyToRealmOrUpdate(goneFollows);
L.d("getHistoryForUpdate goneFollows");
History history = getHistoryForUpdate();
history.getRemoveFollows().clear();
history.getRemoveFollows().addAll(goneFollows);
}
if(newFollows != null && !newFollows.isEmpty()){
newFollows = realm.copyToRealmOrUpdate(newFollows);
L.d("getHistoryForUpdate newFollows");
History history = getHistoryForUpdate();
history.getNewFollows().clear();
history.getNewFollows().addAll(newFollows);
}
finalFollows = realm.copyToRealmOrUpdate(finalFollows);
getFollows().clear();
getFollows().addAll(finalFollows);
realm.commitTransaction();
realm.close();
} catch (JSONException e) {
e.printStackTrace();
}
}
public History getHistoryForUpdate(){
Realm realm = Realm.getDefaultInstance();
String today = DateHandler.getOnlyDate();
History history = realm.where(History.class).equalTo("createDate", today).findFirst();
if(history == null){
L.d("getHistoryForUpdate new");
realm.beginTransaction();
history = new History();
history = realm.copyToRealm(history);
history.setCreateDate(today);
getHistoryList().add(history);
realm.commitTransaction();
}
L.d("getHistoryForUpdate");
realm.close();
return history;
}
In Fragment trying to enter new data but I get only after you restart the application
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
realm = Realm.getDefaultInstance();
favorite = realm.where(Favorite.class).equalTo("id", Long.parseLong(userId))
.findFirst();
RealmList<History> historyList = favorite.getHistoryList();
...
}
Inside background thread are all well written and have access to the data at once, but to get the data from the application, it must be restarted
<receiver
android:name=".core.receivers.Alarm"
android:process=":remote" />
Success came after removal of the second attribute