How to subscribe to events in OnEnable when the object that is subscribed to is initialized in Start? - unity3d

When using (C#) events in Unity, OnEnable and OnDisable are used to subscribe and unsubscribe to the events, so callbacks won't be triggered on disabled objects. However, sometimes the object that is subscribed to can be initialized only at Start (that is called after OnEnable) which means that when OnEnable is called for the first time, you cannot subscribe to the object as it's not initialized yet (later OnEnables will work fine). One fix for that, as also described here, is to use a flag "hasStarted", so the first time you will subscribe at Start and set it to true, but any other time the object is enabled you will subscribe using OnEnable (making sure that hasStarted is true, that is, it's not the first OnEnable call).
However, that seems really ugly, especially since this situation is not rare at all. And I wonder if there are any other ways to get such a functionality without using a one-time flag.

The methods go Awake->OnEnable->Start so you can do the event subscription inside OnEnable only. It's a bit convoluted to my liking as well, but you won't need any additional flags.
Just tested it for UI Toolkit because I had a similar issue (I was subscribing in Awake for button clicks so when setting back to active I had no more event subscriptions since Awake wasn't called again and OnDisabled unsubscribed automatically). (MainMenu->Settings->MainMenu situation).

Related

Is there a standard / built-in way to handle race conditions with CKFetchRecordZoneChangesOperation?

I'm building my first CloudKit application, and am using CKFetchRecordZoneChangesOperation on startup to get any new records changed while the current device was offline.
I am also calling CKFetchRecordZoneChangesOperation when I receive a subscription notification of changes.
It is possible the subscription notification could come in before the startup call finishes. I am currently using a lock to prevent the 2nd call from starting until the recordZoneFetchCompletionBlock handler is called, signalling that the first one is done. This works, but it also smells a bit hacky.

What are the best practices when working with data from multiple sources in Flutter/Bloc?

The Bloc manual describes the example of a simple Todos app. It works as an example, but I get stuck when trying to make it into a more realistic app. Clearly, a more realistic Todos app needs to keep working when the user temporarily loses network connection, and also needs to occasionally check the server for updates that the user might have added from another device.
So as a basic data model I have:
dataFromServer, which is refreshed every five minutes, and
localData, that describes what changes have been made locally but haven't been synchronized to the server yet.
My current idea is to have three kinds of events:
on<GetTodosFromServer>() which runs every few minutes to check the server for updates and only changes the dataFromServer,
on<TodoAdded>() (and its friends TodoDeleted, TodoChecked, and so on) which get triggered when the user changes the data, and only change the localData, and
on<SyncTodoToServer>() which runs whenever the user changes the todo list, or when network connectivity is restored, and tries to send the changes to the server, retrieves the new value from the server, and then sets the new dataFromServer and localData.
So obviously there's a lot of interaction between these three methods. When a new todo is added after the synchronization to the server starts, but before synchronization is finished, it needs to stay in the local changes object. When GetTodosFromServer and SyncTodoToServer both return server data, they need to find out who has the latest data and keep that. And so on.
Coming from a Redux background, I'm used to having two reducers (one for local data, one for server data) that would only respond to simple actions. E.g. an action { "type": "TodoSuccessfullySyncedToServer", uploadedData: [...], serverResponse: [...] } would be straightforward to parse for both the localData and the dataFromServer reducer. The reducer doesn't contain any of the business logic, it receives actions one by one and all you need to think about inside the reducer is the state before the action, the action itself, and the state after the action. Anything you rely on to handle the action will be in the action itself, not in the context. So different pieces of code that generate those actions can just fire these actions without thinking, knowing that the reducer will handle them correctly.
Bloc on the other hand seems to mix business logic and updating the state. API calls are made within the event handlers, which will emit a value possibly many seconds later. So every time you return from an asynchronous call in an event handler, you need to think about how the state might have changed while that call was happening and the consequences this has on what you're currently doing. Also, an object in the state can be updated by different events that need to coordinate among themselves how to avoid conflicts while doing so.
Is there a best practice on how to avoid the complexity that brings? Is it best practice to split large events into "StartSyncToServer" and "SuccessfullySyncedToServer" events where the second behaves a lot like a Redux reducer? I don't see any of that in the examples, so is there another way this complexity is typically avoided in Bloc? Or is Bloc entirely unopinionated on these things?
I'm not looking for personal opinions here, only if there's something I missed in the Bloc manual (or other authoritative source) about how this was intended to work.

Flux - How to deal with multiple stores updating the same view?

I'm a beginner to the flux model but I think I understand it at a high level:
event creator -> events -> dispatch -> store -> view and around we go!
Given that the flux model supports multiple stores, if you have say an event loop that dispatches to 2+ stores, that in turn updates the same view.
How do you manage any inadvertent flicker that would come from that process?
I haven't quite enabled/used react yet (I assume a catch all answer will be that react handles this heavy lifting part of reducing this) but conceptually how could this work outside a specific implementation.
Since store changes are applied serially across stores, do you just wait until all the stores are down processing the dispatcher, and then allow them individually to fire all their changes? Even then you still would loop through and dispatch events at the end, and you'd still potentially have overlapping updates to the UI.
Thanks!
You have different options here:
The vanilla solution is to utilize a waitFor() function in your store-structure, and ensure that in the end each component has only one store it listens to. More or less like this:
Caveat is that your action types and store structures need to be in sync: Each action needs to communicate to all stores that are included in a waitFor cycle. The example in the picture will fail to trigger a render. The top-most store is not listening to the action from dispatcher, and the right store will keep waiting for update. Also, the red line may cause a similar dead end, if it is only 1 of the components. My way of dealing with this is: make all stores in the first line listen to ALL actions, and if the action is irrelevant: emit change.
The other option is to consolidate your data into a single store.
This does not make the issue go away: you need to handle the dependency issues inside the single store. But it does take away the hassle of many actions, many waitFors, and many change emissions..
Remember that the action is processed synchronously - all stores will have emitted, the controller views with have called setState, etc. before the stack unwinds and browser gets a chance to re-render the DOM, so flicker is not possible (the browser won't render in the middle of a function running, since otherwise all DOM manipulation code would cause random flickering).
However, as you say, there will potentially be multiple stores emitting changes, and multiple components listen to them, and hence you may end up calling 'setState' multiple times (even on the same component). This sounds inefficient, but under most circumstances it isn't. As long as the current action originated from an event that came from React (e.g. an event handler added to a component in the JSX), React automatically batches all calls to setState and only does the re-render to the DOM (i.e. any required DOM updates) once, immediately (and synchronously) after you have finished processing the event.
There is a case to be aware of - if you dispatch an action from something outside of a React event handler (e.g. a promise.then, an AJAX callback, setTimeout callback, etc.) then React will have to re-render for every single call to setState in that function, since it doesn't know when else to do it. You can avoid this by using the undocumented batched rendering feature (0.14, note that 0.13 had a different API for this):
ReactDOM.unstable_batchedUpdates(myFunctionThatDispatchesActions);
An alternative might be to use an off-the-shelf Flux implementation which does this for you already. See e.g. https://github.com/acdlite/redux-batched-updates

How to observe UI events with the correct Scheduler with RxJava?

Say I have a generic 'Button' in any kind of modern UI framework (I'm working with Android's right now but the problem is pretty much plaform-agnostic). I can put a listener onto this button to listen for click events. My listener gets notified about click events on the UI-thread. This is a problem from a reactive-point-of-view, more specifically in the context of threading.
I can easily create a Subject for the click events. The Observer would most likely want to do processing on another thread. But I can't easily extract (without hacking or ugly workarounds) the Scheduler's Worker from an .observeOn() operator, for dispatching the event onto the correct Worker.
How do I create an Observable stream from these click events, so that the stream will respect the Scheduling requirements of the .observeOn() operator?
It's not clear to me what exactly the issue is here. ObserveOn puts the events to the specified scheduler, which behind the scenes may end up on different threads for different subscribers. If you want to make sure every subscriber will receive the events exactly on the same thread, you need to have a Scheduler with exactly one backing thread. The easiest way of getting such Scheduler is by wrapping an ExecutorService:
Scheduler s = Schedulers.from(Executors.newSingleThreadedExecutor());
PublishSubject<Event> ps = PublishSubject.create();
Observable<Event> events = ps.observeOn(s);
events.subscribe(...);
events.subscribe(...);
ps.onNext(...);

Google In-App Billing causing Exception

In one of my Android apps, I'm trying to implement a simple grab of the inventory from Google's In-App billing, but it keeps giving me errors at the line of
mHelper.queryInventoryAsync(mGotInventoryListener);
with the message that
IabHelper is not setup. Can't perform operation: queryInventory
Here is all the IabHelper code.
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(TAG, "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
}
});
//check to see if ads have been removed(bought)
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
}
else {
// does the user have the premium upgrade?
if(inventory.hasPurchase(REMOVE_ADS)) removeAdsPurchased = true;
// update UI accordingly
}
}
};
mHelper.queryInventoryAsync(mGotInventoryListener);
The short answer is that your queryInventoryAsync() call should be made from inside your onIabSetupFinished() method. This is an asynchronous call, and so you cannot just proceed with using the IabHelper instance until that callback has been invoked to tell you that the helper's communication with the billing service has been established. The way your code is presently written, you have a race condition, and your queryInventoryAsync() call is going to win that race and attempt to use the IabHelper object before it has been set up, which is the cause of your problem.
Also, any further code in UI handlers that relies upon this object (e.g., the handler for a button that initiates a purchase) should test for a fully set-up IabHelper object, and should not allow the user to use that UI element until the IabHelper instance created in onCreate() has successfully completed setup. The easiest way to handle this situation is to simply disable such UI elements until the setup callback has been invoked to indicate that setup has completed successfully.
That is the easy part. The more serious problems occur when you have actions that occur immediately after your onCreate() method runs (i.e., not under the control of the user), that require the use of a fully set-up IabHelper instance. This typically occurs as a result of activity lifecycle calls - specifically, onResume() (if there is something requiring an IabHelper instance that must be done each time your app comes to the foreground, and not just when onCreate() is called) and, most notably, in onActivityResult() (which is invoked when the user completes or aborts an interaction with the billing interface - e.g., as part of making an in-app payment).
The problem is that your app may be stopped by the OS (e.g., to make room for the billing interface itself when the user initiates a purchase), causing your IabHelper instance to be destroyed along with your app, and that instance will have to be regenerated when your onCreate() is next invoked, and setup will once again be initiated in onCreate(), and once again you will need to wait for setup to complete before doing anything else with that object.
One notable situation where this can occur is during the user's interaction with the billing interface as part of the purchase process. The result of that interaction will be communicated to your app via onActivityResult(), which itself needs to use a fully set-up IabHelper object, and so if your app gets flushed from memory while the user is interacting with the billing service to make (or cancel) a purchase, then onActivityResult() will have to wait for the IabHelper instance to get set up again (after it is re-created in onCreate() before it can use it.
One way to handle this would be to set up and post to a queue of pending actions requiring an IabHelper instance, that your onResume() and/or onActivityResult() code adds to, and have that queue processed by your onIabSetupFinished() method once the IabHelper setup (initiated by onCreate()) has completed.
It's not trivial. And last time I checked, the TrivialDrive sample app did not handle the above situation.
The best way to test this kind of use case is to use the developer option "Don't Keep Activities," which causes your app to be destroyed each time the user leaves it, to simulate what the OS will do when it needs to reclaim memory, so that you can make sure your app works under those conditions.
Great stuff from Carl. I "think" I am seeing a similar thing, though my app is crashing via:
java.lang.IllegalStateException: IabHelper was disposed of, so it cannot be used.
And it my case, rotating the device one way, then immediately back to the original orientation SOMETIMES causes this crash. Seems that there's a "window" of time where this crash "might" happen (due to the asynchronous nature of IAB like Carl explained).
My fix
My "fix" was to make mHelper static, and only instantiate it if (mHelper == null), and NOT destroy it in the activity's onDestroy() method. This way, once it's setup, it sticks around, and there's no need to worry about asynchronous operations (causes by device orientation) anymore.
Not sure if this is the right fix or not, but thought I'd mention it in case it help others.
Simply make the checkNotDisposed() method synchronised and the problem goes away. This is because it is sometimes called in a separate thread and does not always have the latest value of mDisposed, which may have been set in the main thread:
private synchronized void checkNotDisposed()