How to create private RealmObjects in a shared Realm - android

My goal is pretty simple: I need to create private RealmObjects in a shared Realm.
I already created objects and stored them in a shared realm. I would like to give users the ability to store their objects privately though. As far as I know I have two ways to accomplish this, but none of them is working.
SOLUTION 1: using permissions to let Users share Realm objects to each other like in the following sample:
public static void setActiveUser(final SyncUser user) {
if (user.isValid()) {
SyncConfiguration defaultConfig = new SyncConfiguration.Builder(user, Colombo.SERVER_URL).build();
Realm.setDefaultConfiguration(defaultConfig);
Realm realm = user.getManagementRealm();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
String includeAll = "*";
change = new PermissionChange(includeAll, includeAll, true, true, false);
realm.insert(change);
}
});
}
}
Unfortunately this doesn't work, the synchronized Realms are visibile just to the current SyncUser.
SOLUTION 2: Create a "common" SyncUser to save/show public items and a private SyncUser for each registered user to collect his private items, switching from common to private SyncUser depending on the needs. Anyway it seems quite inefficient to me. How does it sound to you? Do you think there's another way I can do it?

You cannot use Realm's authentication mechanism to provide different login permissions to different accounts for a single Realm.
As Nabil said, you can programmatically control access by assigning UUIDs to users, and then querying only for them per user.

Related

Comparing current values with last values in architecture component

I am getting a complex json from an api and I am saving the values with Architect component(Room).
Question:
Can I compare current values with last values that I saved in SQlite and if in compare I find a difference, update the RecyclerView?
Is this method logical?
Do you have a better way to offer?
If you have a better way to offer get me a sample(url sample)
Yes, you can do that and it is actually the recommended way. In order to do so, I think you should leverage the use of two other Architecture Components that were introduced with Android Jetpack, not only Room database: ViewModel and LiveData, but it is not mandatory.
The important thing is to add an extra layer to your app called Repository:
Repository modules handle data operations. They provide a clean API so
that the rest of the app can retrieve this data easily. They know
where to get the data from and what API calls to make when data is
updated. You can consider repositories to be mediators between
different data sources, such as persistent models, web services, and
caches.
So basically, the suggested architecture to handle this will look something like this:
With that in mind, an example of a Repository that retrieves User data from a web service and save it to a local Room Database will look something like this:
public class UserRepository {
private final Webservice webservice;
private final UserDao userDao;
private final Executor executor;
public UserRepository(Webservice webservice, UserDao userDao, Executor executor) {
this.webservice = webservice;
this.userDao = userDao;
this.executor = executor;
}
public LiveData<User> getUser(String userId) {
refreshUser(userId);
// Returns a LiveData object directly from the database.
return userDao.load(userId);
}
private void refreshUser(final String userId) {
// Runs in a background thread.
executor.execute(() -> {
// Check if user data was fetched recently.
boolean userExists = userDao.hasUser(FRESH_TIMEOUT);
if (!userExists) {
// Refreshes the data.
Response<User> response = webservice.getUser(userId).execute();
// Check for errors here.
// Updates the database. The LiveData object automatically
// refreshes, so we don't need to do anything else here.
userDao.save(response.body());
}
});
}
}
Then, your ViewModel will get the User Live Data doing something like this:
...
user = userRepo.getUser(userId);
...
And it will provide that LiveData to the UI layer with a public method:
...
public LiveData<User> getUser() {
return this.user;
}
...
Finally, from your UI layer (an Activity or Fragment) you can observe the LiveData in the ViewModel and adapt the UI accordingly.
viewModel.getUser().observe(this, user -> {
// Update UI.
});
For a more complete explanation I suggest that you take a look to:
Guide to app architecture in Android`s Developers website.
This Github project with a basic example.
This other Github project with a more complex example.
You can merge multiple live data source from server and sqlite with MediatorLiveData which is a subclass of LiveData.
For example, if you have a LiveData object in your UI that can be updated from a local database or a network, then you can add the following sources to the MediatorLiveData object:
A LiveData object associated with the data stored in the database.
A LiveData object associated with the data accessed from the network.
Documentation

RealmResult as RealmObject field

I'm trying to figure out the best way to set up a RealmObject with a RealmResult as one of its fields.
For example, let's say I have two RealmObjects, Goal and Achievement. The Goal object contains fields that define a query of Achievement's the user wants to track (e.g. date range the achievement was created, type of achievement, etc) and has custom methods to extract statistics from those Achievements.
What is the best way for Goal to contain this RealmResult of Achievements? Here are some ways I've thought of doing this:
Have a persisted RealmList field in Goal and update it anytime a field is changed that would change the resulting query. But how would this RealmList get updated if a new Achievement gets added to the realm?
Use #Ignore annotation on a RealmResult<Achievement> field within Goal. Anywhere in Goal where mResult is used, first check if null and requery if needed. This seems like I will be doing a lot of unneccessary querying if I'm using something like a RecyclerView that refetches the object in getItem().
Have a wrapper class that contains a Goal object and the RealmResult<Achievement> as fields. Add a listener to Goal so that anytime a relevant field changes the RealmResult can be requeried.
I'm leaning towards the last one as the cleanest way to keep a valid RealmResult. Am I missing an easier way to accomplish this?
Okay so I'm trying to implement a wrapper class (which I think is similar to the DAO abstraction #EpicPandaForce was mentioning, but I'm not super familiar with that)
public class GoalWrapper {
private RealmResults<Achievements> mResults;
private Goal mGoal;
private Realm mRealm;
public GoalWrapper(Realm realm, Goal goal) {
mRealm = realm;
mGoal = goal;
// TODO: does this need to be removed somewhere? What happens when GoalWrapper gets GC'd?
goal.addChangeListener(new RealmChangeListener<RealmModel>() {
#Override
public void onChange(RealmModel element) {
// rerun the query
findResultForGoal();
}
});
findResultForGoal();
}
/**
* Run a query for the given goal and calculate the result
*/
private void findResultForGoal() {
mResults = mRealm.where(Achievement.class)
.greaterThanOrEqualTo("date", mGoal.getStartDate())
.lessThanOrEqualTo("date", mGoal.getEndDate())
.equalTo("type", mGoal.getAchievementType())
.findAll();
calculateStats();
}
private void calculateStats() {
// Get relevant stats from mResult...
}
}
I haven't tested this code yet but I plan to have a RecyclerView.Adapter with an ArrayList of GoalWrapper objects.
My one concern is that I never remove the listener on mGoal. Do I even need to remove it? What happens in the case that the ArrayList gets GC'ed? I would think that the Goal field and resulting listeners attached to it all get GC'ed as well.

Flattening data in Firebase to reflect model in Android app

I'm trying out Firebase as my backend for a prototype app im creating.
As a mockup, im creating a fake game.
The data is pretty simple:
There are 3 lists of 'levels' common to all users, organized by difficulty ( easy, medium hard ). ( These are in a Fragment inside a ViewPager
Each level has mini-games inside of them than the users complete.
On the Android app, the user sees that list but also sees a counter of how many mini-games of that level he/she has completed.
If the user clicks on a level, he/she sees the list of mini-games and sees which of those are completed.
Im currently structuring my data as follows:
levels:{
easy:{
easy-level-1-id:{
total-mini-games:10,
easy-level-1-id:String
}
},
medium:{....},
hard:{.....}
}
user-progress:{
user-id:{
levels:{
easy-level-1-id:{
user-completed:int
}
},
mini-games:{
mini-game-1:true
mini-game-2:true
}
}
}
I have to access many places in order to check if a game has been completed, or how many games have been completed per level.
All of this is in order to avoid nesting data, as the docs recommend.
Is it better to do it like this or is it better to store every available level under every user id in order to do less calls, but having much more repeated data ?
I know about the FirebaseRecyclerViewAdapter class provided by the Firebase team, but because it takes a DatabaseReference object as an argument for knowing where the data, it doesn't really apply here, because i fetch data from different structures when building the model for the lists.
mRef1.child(some_child).addValueEventListener(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot){
final Model model = dataSnapshot.getValue(Model.class);
mRef2.child(some_other_child).addValueEventListener(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot){
//add more data into the model class
.....
}
#Override
public void onCancelled(DatabaseError databaseError){}
});
#Override
public void onCancelled(DatabaseError databaseError){}
});
Any pointers as to how to work with more structured data ?
FirebaseUI includes support for working with indexed data. It can load linked data from an index, such as in your mini-games node.
In your example that could work as:
keyRef = ref.child("user-progress").child(currentUser.getUid()).child("mini-games");
dataRef = ref.child("mini-games");
adapter = new FirebaseIndexRecyclerAdapter<Chat, MiniGameHolder>(
MiniGame.class,
android.R.layout.two_line_list_item,
MiniGameHolder.class,
keyRef, // The Firebase location containing the list of keys to be found in dataRef.
dataRef) //The Firebase location to watch for data changes. Each key key found at keyRef's location represents a list item in the RecyclerView.

Using a Singleton for Current logged in user of mobile app

Is creating a Singleton to keep track of the current logged in user of a mobile app a bad idea? The class below is something along the lines of what I've used before and it works fine, but I always feel it's not the best way of doing things.
public class LoggedInUser {
private static LoggedInUser ourInstance = null;
User user;
public static LoggedInUser getInstance() {
return ourInstance != null ? ourInstance : new LoggedInUser();
}
private LoggedInUser () {
user = new User();
}
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return this.user;
}
}
I use the info of the user pretty often throughout the app, and sometimes the User object isn't trivially small so passing the object between each view doesn't sound like the best option either. What is normal practice for this?
The user has to log in every time they use the app, so I also don't want to write the information to the phone necessarily.
I have found a similar question (Is Singleton the best way to keep data in memory on an Android application?) and the answer posted by charlag is actually pretty mature:
I would use the following: Interface CurrentUser. CurrentUser has methods to retrieve and update user. User may be stored in DB, preferences or mixed. I would use Dagger to inject CurrentUser when needed. CurrentUser implementation should itself be a singleton, because Dagger doesn't guarantee qualities of singleton. Bonus points if your CurrentUser has something like RxJava stream or LiveData to keep observers up-to-date.
As an alternative to Singleton you may want to implement Fragment
Holder pattern with retained fragments but in your situation Singleton
seems better. Just make sure that you use interfaces and injection to
not compromise testability and to keep your components separated.
Do not fall into keeping your objects within the App class, unless it is crucial to instantiate your app's dependencies correctly - it sounds nice and easy, but can also get out of control pretty quick.
Singleton is normal.
But you can always use Application class and its variables (both static and object) to store the data.
public class MyApp extends Application {
public static LoggedInUser user;
#Override
public void onCreate() {
super.onCreate();
user = LoggedInUser.getInstance();
//...
}
//or even
public LoggedInUser user_obj;
}
AndroidManifest.xml:
<application
android:allowBackup="true"
android:largeHeap="true"
android:name=".MyApp"
android:icon="#drawable/iconlite"
android:label="#string/app_alias"
android:theme="#style/AppTheme" >
somewhere in the code use MyApp.user or ((MyApp)getApplication()).user_obj

Realm, RxJava, asObservable() and doOnUnsubscribe()

In my Android projects, I use realm as my data storage engine. I love it!
I also use RxJava because it makes "threading" so much easier, and I really like the whole "reactive mindset". I love it!
I use an MVP pattern + some "Clean architecture" ideas to build my apps.
My Interactors are the only ones who know about Realm. I expose data with the help of Observable, like this:
#Override
public Observable<City> getHomeTown() {
final Realm realm = Realm.getDefaultInstance();
return realm.where(City.class).equalTo("name", "Cluj-Napoca").findAllAsync().asObservable()
.doOnUnsubscribe(new Action0() {
#Override
public void call() {
realm.close();
}
})
.compose(new NullIfNoRealmObject<City>());
}
The problem is my doOnUnsubscribe side-effect gets called before Realm can do its thing, handling the exposed observable:
Caused by: java.lang.IllegalStateException: This Realm instance has already been closed, making it unusable.
at io.realm.BaseRealm.checkIfValid(BaseRealm.java:344)
at io.realm.RealmResults.removeChangeListener(RealmResults.java:818)
at io.realm.rx.RealmObservableFactory$3$2.call(RealmObservableFactory.java:137)
at rx.subscriptions.BooleanSubscription.unsubscribe(BooleanSubscription.java:71)
at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:124)
at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:113)
at rx.Subscriber.unsubscribe(Subscriber.java:98)
at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:124)
at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:113)
at rx.Subscriber.unsubscribe(Subscriber.java:98)
at rx.subscriptions.CompositeSubscription.unsubscribeFromAll(CompositeSubscription.java:150)
at rx.subscriptions.CompositeSubscription.unsubscribe(CompositeSubscription.java:139)
at ro.tudorluca.realm.sandbox.city.CityPresenter.onDestroy(CityPresenter.java:62)
at ro.tudorluca.realm.sandbox.city.CityActivity.onDestroy(CityActivity.java:35)
I created a sandbox project for this use case.
I really like using Realm+RxJava, but I can't seem to find a clean solution to close the Realm instance when I unsubscribe (I usually unsubscribe when the activity gets destroyed). Any ideas?
Edit 1: https://github.com/realm/realm-java/issues/2357
Edit 2: thanks to the very active realm team, there is already a pull request to fix this issue.
21 hours later and this is what I came up with:
#Override
public Observable<City> getHomeTown() {
return getManagedRealm()
.concatMap(new Func1<Realm, Observable<City>>() {
#Override
public Observable<City> call(Realm realm) {
return realm.where(City.class).equalTo("name", "Cluj-Napoca").findAllAsync().asObservable()
.compose(new NullIfNoRealmObject<City>());
}
});
}
private static Observable<Realm> getManagedRealm() {
return Observable.create(new Observable.OnSubscribe<Realm>() {
#Override
public void call(final Subscriber<? super Realm> subscriber) {
final Realm realm = Realm.getDefaultInstance();
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
realm.close();
}
}));
subscriber.onNext(realm);
}
});
}
I tried something like this before posting the question on stackoverflow, but my mistake was using flatMap(), instead of concatMap().
Unlike flatMap(),concatMap() will keep the order of the emissions which, in my case, means that my Action0 -> realm.close() will be the last action being called after unsubscribing from the stream, after Realm's Action0 -> results.removeChangeListener(listener) which was causing the problem.
A full example can be found on github.
Edit: thanks to the very active realm team, there is already a pull request to fix this issue.
Since you said that only the Interactor "knows" about the Realm framework I would say not to even return a managed Realm object, instead return an unmanaged copy of the results using copyFromRealm. That way you don't have to care about the Realm instance being open or closed in the Presenter.
At the same time I would let the Presenter chose wether the call should be done asynchronous or not since RxJava does that pretty cool and easy and you won't have issues calling the Interactor load method within another thread(which can be avoided using Loopers but why overcomplicate the situation if you can make it simpler :P ).
So I would go for:
Override
public Observable<City> getHomeTown() {
final Realm realm = Realm.getDefaultInstance();
City city = realm.where(City.class).equalTo("name", "Cluj-Napoca").findFirst();
// make sure we don't send back Realm stuff, this is a deep copy that will copy all referenced objects (as the method doc says)
City cityUnmanaged = realm.copyFromRealm(city);
// safe to close the realm instance now
realm.close();
return Observable.just(cityUnmanaged);
}
I am curious to see more options :).
As per me, one of the major thing to look after in a good architecture is modularity. All the major modules(or libraries) should be isolated from rest of the code. Since Realm, RealmObject or RealmResult can not be passed across threads, it is even more important to make Realm & Realm related operations isolated from rest of the code.
Keeping this philosophy in mind, I came up with the following approach.
For every jsonModel class, we create a realmModel class and a DAO (Data Access Object) class. Idea here is that other than DAO class none of the class must know or access realmModel or Realm entities. DAO class takes jsonModel, converts it to realmModel, performs read/write/edit/remove operations & for read operations DAO converts resulted realmModel to jsonModel and returns them.
This way it is easy to maintain Realm, avoid all thread related issues, easy to test and debug.
Here is an article about Realm best practices with a good architechture https://medium.com/#Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f
Also a sample project demonstrating Integration of Realm on Android with MVP(Model View Presenter), RxJava, Retrofit, Dagger, Annotations & Testing. https://github.com/viraj49/Realm_android-injection-rx-test

Categories

Resources