I am using rxJava to fetch data from the database and show it in a recyclerview. The relevant code is shown below
function updateUI(){
ContactsLab contactsLab = ContactsLab.get(getActivity());
Subscription sub = contactsLab.getContactList().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toList()
.subscribe(onContactsReceived());
mCompositeSubscription.add(sub);
}
ContactsLab is a singleton that returns an Observable of Contact objects.
onContactsReceived function is shown below
private Observer<List<Contact>> onContactsReceived(){
return new Observer<List<Contact>>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(List<Contact> contacts) {
if(mContactsAdapter == null) {
mContactsAdapter = new ContactsAdapter(contacts);
mRecyclerView.setAdapter(mContactsAdapter);
} else{
mContactsAdapter.setContactList(contacts);
mContactsAdapter.notifyDataSetChanged();
}
}
};
}
The updateUI function is called in my fragment onResume but the view is updated only the first time. If I come back to this fragment from any other fragment (having added more items to db), onResume is called, updateUI runs and onContactsReceived also runs but returns immediately without calling onNext or onComplete.
I think this has something to do with the way rxJava handles observables but no idea how to fix it (read about defer but couldn't understand much). Can somebody please help?
Edit:
The getContactList function look like this :
public rx.Observable<Contact> getContactList() {
List<Contact> contacts = new ArrayList<>();
ContactCursorWrapper cursorWrapper = queryContacts(null, null);
try{
cursorWrapper.moveToFirst();
while (!cursorWrapper.isAfterLast()){
contacts.add(cursorWrapper.getContact());
cursorWrapper.moveToNext();
}
} finally {
cursorWrapper.close();
}
return rx.Observable.from(contacts);
}
Basically it queries the database and maps the returned Cursor into my Contact class(which is a POJO). I added the rx.Observable.from to get an observable that was later collated using toList and updated into the adapter.
I used this approach avoid having to call notifyDataSetChanged after getting each item (and call it only once after getting all that).
What's the right approach to minimize the number of notifyDataSetChanged calls and also, refresh each time onResume is called?
Your observable contactsLab.getContactList().toList() has terminated.toList() collects all emissions from a source observable to a list and emits the entire list once the source Observable terminates (see the documentation). You aren't going to observe any more emissions from it.
Related
I'm using firebase to store my data and it is structured this way:
users {
userid {information here}
}
friends {
userid {
friendid1:true
friendid2:true
}
}
If a friendid is added or removed, then an appropriate event should be called.
If the user information is changed, then an event update should be triggered.
So far I have:
RxFirebaseDatabase.observeFriendList(/*Path to friends id list*/)
.flatMap(new Func1<RxFirebaseChildEvent, Observable<User>>() {
#Override
public Observable<User> call(RxFirebaseChildEvent stringBooleanPair) {
//This is a childeventlistener callback
//The key of RxFirebaseChildEvent is the friendid
switch (stringBooleanPair.getEventType()) {
case ADDED:
return RxFirebaseDatabase.observeUserInformation(/*path to user list*/.child(stringBooleanPair.getKey()), User.class);
case REMOVED:
return null; //What do I do here to unregister the listener?
}
}
}).subscribe(user -> {
//This is a ValueEventListener callback that returns the user
//Add, remove or update here, how?
});
How do I remove the specific user listener when a friendId is removed and also call add/remove/update on that user to update the view?
I came up with a somewhat hacked-together version, would love to see a better implementation though.
//refUsers is path to users
List<String> userKeys = new ArrayList<>();
RxFirebaseDatabase.observeFriendList(/*Path to friends id list*/)
.flatMap(new Func1<RxFirebaseChildEvent, Observable<User>>() {
#Override
public Observable<User> call(RxFirebaseChildEvent stringBooleanPair) {
//This is a childeventlistener callback
//The key of RxFirebaseChildEvent is the friendid
switch (stringBooleanPair.getEventType()) {
case ADDED:
userKeys.add(stringBooleanPair.getKey());//User Key
return RxFirebaseDatabase
.observeUserInformation(refUsers.child(stringBooleanPair.getKey()),
User.class,
Eventype.ADDED)
.takeWhile((user) -> userKeys.contains(user.getKey()));
case REMOVED:
userKeys.remove(userid);
return RxFirebaseDatabase
.observeUserInformation(refUsers.child(userid),
User.class,
Const.EventType.REMOVED)
.take(1);
}
}
}).subscribe(userEvent -> {
//Returns an object that contains the user and the eventype (added, updated or removed)
});
Basically, I'm keeping track of the users that are in the friends list and only returning an event if the userid is still watched.
You don't need to do that RxFirebase library already remove your listener when you unsubscribe your Observable, you can see it if you check the implementation:
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
query.removeEventListener(childEventListener);
}
I suggest you to check as reference(or just use it) my RxJava2 library if you want to update your app from RxJava to RxJava2:
https://github.com/FrangSierra/Rx2Firebase
If you are interested of it, you can see the differences between both RxJava here.
I create an Observable from a long running operation + callback like this:
public Observable<API> login(){
return Observable.create(new Observable.OnSubscribe<API>() {
#Override
public void call(final Subscriber<? super API> subscriber) {
API.login(new SimpleLoginListener() {
#Override
public void onLoginSuccess(String token) {
subscriber.onNext(API.from(token));
subscriber.onCompleted();
}
#Override
public void onLoginFailed(String reason) {
subscriber.onNext(API.error());
subscriber.onCompleted();
}
});
}
})
}
A successfully logged-in api is the pre-condition for multiple other operations like api.getX(), api.getY() so I thought I could chain these operation with RxJava and flatMap like this (simplified): login().getX() or login().getY().
My biggest problem is now, that I don't have control over when login(callback) is executed. However I want to be able to reuse the login result for all calls.
This means: the wrapped login(callback) call should be executed only once. The result should then be used for all following calls.
It seems the result would be similar to a queue that aggregates subscribers and then shares the result of the first execution.
What is the best way to achieve this? Am I missing a simpler alternative?
I tried code from this question and experiemented with cache(), share(), publish(), refCount() etc. but the wrapped function is called 3x when I do this for all of the mentioned operators:
apiWrapper.getX();
apiWrapper.getX();
apiWrapper.getY();
Is there something like autoConnect(time window) that aggregates multiple successive subscribers?
Applying cache() should make sure login is only called once.
public Observable<API> login() {
return Observable.create(s -> {
API.login(new SimpleLoginListener() {
#Override
public void onLoginSuccess(String token) {
s.setProducer(new SingleProducer<>(s, API.from(token)));
}
#Override
public void onLoginFailed(String reason) {
s.setProducer(new SingleProducer<>(s, API.error()));
}
});
}).cache();
}
If, for some reason you want to "clear" the cache, you can do the following trick:
AtomicReference<Observable<API>> loginCache = new AtomicReference<>(login());
public Observable<API> cachedLogin() {
return Observable.defer(() -> loginCache.get());
}
public void clearLoginCache() {
loginCache.set(login());
}
Ok I think I found one major problem in my approach:
Observable.create() is a factory method so even if every single observable was working as intented, I created many of them. One way to avoid this mistake is to create a single instance:
if(instance==null){ instance = Observable.create(...) }
return instance
I have created a TcmExtension named WorkflowEventSystem that has an event handler subscribed to the FinishProcess event. The purpose of this event is to schedule for publish all dependencies (i.e. pages) of the associated workflow subject.
The problem I am having is that even though the event triggers at the right time (a workflow process is completed), and all the items that are supposed to be scheduled for publish are, the PublishScheduler object created by the event never seems to go out of scope, and as such the WorkflowEventSystem object does not either.
Is there something I am missing about how the Event System works that would cause these objects to live on forever? I've included what I consider the relevant code below (some parts summarized). Thanks for any help.
Here's most of the actual TcmExtension:
public class WorkflowEventSystem : TcmExtension
{
public WorkflowEventSystem()
{
this.Subscribe();
}
public void Subscribe()
{
EventSystem.Subscribe<ProcessInstance, FinishProcessEventArgs>(ScheduleForPublish, EventPhases.All);
}
}
ScheduleForPublish creates a PublishScheduler object (class I created):
private void ScheduleForPublish(ProcessInstance process, FinishProcessEventArgs e, EventPhases phase)
{
if(phase == EventPhases.TransactionCommitted)
{
PublishScheduler publishScheduler = new PublishScheduler(process);
publishScheduler.ScheduleForPublish(process);
publishScheduler = null; // worth a try
}
}
The ScheduleForPublish method looks similar to this:
public void ScheduleForPublish(ProcessInstance process)
{
using(Session session = new Session("ImpersonationUser"))
{
var publishInstruction = new PublishInstruction(session);
// Set up some publish Instructions
var publicationTargets = new List<PublicationTarget>();
// Add a PublicationTarget here by tcm id
IList<VersionedItem> itemsToPublish = new List<VersionedItem>();
// Add the items we want to publish by calling GetUsingItems(filter)
// on the workflow process' subject
//Publish the items
PublishEngine.Publish(itemsToPublish.Cast<IdentifiableObject>(), publishInstruction, publishTargets);
}
}
Life-cycle management for TcmExtension classes is quite simple:
when you call Subscribe the TcmExtension object you specify is added to an internal list of subscriptions
when you later call Unsubscribe the same TcmExtension object is removed from the list of subscriptions
Since you never call Unsubscribe your WorkflowEventSystem is never removed and thus will never be garbage collected by .NET. And since your WorkflowEventSystem holds a reference to the PublishScheduler instance it created, that one will thus also never be cleaned up.
The proper boilerplate for a custom TcmExtension is:
public class WorkflowEventSystem : TcmExtension, IDisposable
{
EventSubscription _subscription;
public WorkflowEventSystem()
{
this.Subscribe();
}
public void Subscribe()
{
_subscription = EventSystem.Subscribe<ProcessInstance,
FinishProcessEventArgs>(ScheduleForPublish, EventPhases.All);
}
public void Dispose()
{
_subscription.Unsubscribe();
}
}
Nuno also gave a longer example (handling multiple subscribers) in this article:
http://nunolinhares.blogspot.nl/2012/07/validating-content-on-save-part-1-of.html
I am deserializing a list of objects from an XML file, and would like to bind to the actual content of those objects in my View, passing over a ViewModel. The problem is that file operations are async and this bubbles all the way up to the ViewModel, where Property getters cannot be marked as such...
Problem
I deserialize all XML files in a folder to Profile objects and store them in a List<Profile>. This method (has to be) marked async.
public static async Task<List<Profile>> GetAllProfiles()
{
DataContractSerializer ser = new DataContractSerializer(typeof(Profile));
StorageFolder folder = await ApplicationData.Current.RoamingFolder.CreateFolderAsync("Profiles", CreationCollisionOption.OpenIfExists);
List<Profile> profiles = new List<Profile>();
foreach (var f in await folder.GetFilesAsync())
{
var fs = await f.OpenStreamForReadAsync();
profiles.Add((Profile)ser.ReadObject(fs));
fs.Dispose();
}
return profiles;
}
Ideal solution 1
The binding property in my ViewModel would then ideally call that static method like this
public async Task<ObservableCollection<string>> Lists
{
get
{
return new ObservableCollection<string>(GetAllProfiles().Select(p => p.Name));
}
}
BUT Properties cannot be marked async
Ideal solution 2
public ObservableCollection<string> Lists
{
get
{
return new ObservableCollection<string>((GetAllProfiles().Result).Select(p => p.Name));
}
}
BUT this never executes (it blocks in the await folder.GetFilesAsync() call for some reason)
Current solution
Calls an async Initialize() method that loads the result of the GetProfiles() function in a variable, and then makes a NotifyPropertyChanged("Lists") call:
public ViewModel()
{
Initialize();
}
public async void Initialize()
{
_profiles = await Profile.GetAllProfiles();
NotifyPropertyChanged("Lists");
}
private List<Profile> _profiles;
public ObservableCollection<string> Lists
{
get
{
if (_profiles != null)
return new ObservableCollection<string>(_profiles.Select(p => p.Name));
else
return null;
}
}
Question
Is there a better way?
Is there a pattern/method that I haven't yet discovered?
Edit
The root of the problem appears when doing non-UI code, and you cannot rely on the NotifyPropertyChanged to do some thread-synchronization stuff. -- The method Initialize has to be awaited and ctors cannot be async, so essentialy this is pattern is useless.
public MyClass()
{
Initialize();
}
public async void Initialize()
{
_profiles = await Profile.GetAllProfiles();
}
private ObservableCollection<Profile> _profiles;
public ObservableCollection<string> Lists
{
get
{
return _profiles; // this will always be null
}
}
Properties can't be async so this solution will not work as you mentioned. Task.Result waits for the task to complete, but this is blocking your UI thread where the I/O operation's async callback returns, so you are deadlocking your application, since the callback is never called. Your solution really is the best way. It could be improved though.
You should make the _profiles field an ObservableCollection, so you would not need to convert the List to the OC every time the list is accessed.
Since you are performing an I/O operation that can take arbitrary amount of time - you should enable some sort of a progress indicator while it is in progress.
In some cases you might want the Lists property to be lazier and only call the Init method the first time it is accessed.
How do I execute an asynchronous method from within a controller method and return an HttpStatusCodeResult(200), without the async delegate prematurely terminating its execution?
I am working on an asp.net application and one of my action my home controller takes long time to run(10-30 sec). I want to return an HttpStatusCodeResult(200) and keep running my function. The functoin need not to return anything, but still it's not really a case of fire and forget since I return a response to the server right away with HttpStatusCodeResult.
I tried using delegates, but it seems once I return the status code from the action, the delegate stops executing. Another option is to create a windows service but that would be like using a bazooka to kill a fly. I get very few requests so resources and performance is not an issue in my case. I heard ThreadPool.QueueUserWorkItem is an option, but which is most suitable for my case?
Create a static class that manages this long-running operation. This class will be responsible for creating threads to perform the task and also provides a way to check up on the status of any ongoing operations (i.e. if it's still in progress or has finished, and if so, what the result is).
Your MVC Controller method then uses this class to do the work and present status information or the completed processed data to the user.
Something like this:
public ActionResult GetSomething(String id) {
TaskResult result = TaskClass.GetStatus( id );
if( result == null ) { // the task has not been run, so start it up
TaskClass.StartNew( id );
return new View("PleaseWait");
} else if( !result.IsFinished ) {
return new View("PleaseWait");
} else {
return View("Results", result);
}
}
public static class TaskClass {
private static Dictionary<String,TaskResult> _tasks;
public static TaskResult GetStatus(String id) {
// TODO: Make this code thread-safe
if( _tasks.ContainsKey(id) ) return _tasks[id];
return null;
}
public static void Start(String id) {
_tasks.Add( id, new TaskResult("Working") );
Thread thread = new Thread( SomeExpensiveOperation );
thread.Start( id );
}
}
At the end of SomeExpensiveOperation there would be code that marks the TaskResult as finished.