I was wondering what is the correct way to handle screen rotations while waiting for an asynchronous callback. The callback tries to touch some UI elements on the activity, but as it gets destroyed I get a null pointer exception.
I'm not sure how the logic of your code it set up, but I wouldn't have the callback methods directly touch the UI elements. Instead, have them touch your Model, and when the onCreate method in your Activity is called, you then take the data out of the Model. That way you don't have to rely on the UI elements to be there - you just rely on the Model.
Hope this was helpful - if not, let me know.
Using a Headless Fragment with SetonRetainInstance (true); or Loader, or using the otto library are good ways to accomplish this
Related
I have a pretty odd problem here. In a fragment, I do a process and when the process finishes I show an advert that callsback to the fragment when user clicks/dismisses the ad. The problem is that in some devices when the ad calls back to the handler (that is in the running fragment) the activity containing the fragment has been destroyed, but I need to do some more work through a runnable. So, in this case the runnable throws a NullPointerException int is run method when executed.
I could just check if the activity is still alive and run just the runnable when it is, but in the cases it is not alive I still need to continue to do the part of the job that needs to be done after the ad.
How do you handle this kind of situations? I have been thinking about the problem during some hours without finding a solution to this.
Thanks in advance.
You can use AsyncTask in this case .
AsyncTask processes are not automatically killed by the OS. AsyncTask processes run in the background and is responsible for finishing it's own job in any case. You can cancel your AsycnTask by calling cancel(true) method. This will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object) method is called instead of onPostExecute() after doInBackground() returns.
Hope it helps..
mmm the way this is asked I am not sure what you are asking, perhaps some text connectors might work, I am not sure if this is a quite basic question about state changes or a very complex one.
from what I understood:
wouldn't this be the same problem as when you flip screen? make a Bundle of the data that is restored through activity changes. This way if your activity has been Destroyed you restore it
fragments have a feature that you can use to keep instance alive across a configuration change: retainInstance
setRetainInstance(true) // false by default
Parcelable like Serializable, is an API for saving an object out to a stream of bytes. Objects may elect to implement the Parcelable interface if they are what we will call "stashable" here. Objects are stashed in Java by putting them in a Bundle, or by marking them Serializable so they can be serialized, or by implementing the Parcelable interface. Whichever way you do it, the same idea applies: you should not be using any of these tools unless your object is stashable
---or---
turn that "advert" of yours into an Alert, which wont mess with the Activity.
---or---
run the response on a different thread?
I have inherited some code hence I don't have true freedom to change it. :(
I have a main activity, from which other activities (I will refer to these as sub activities from now on) are called. Whenever one of these completes, it calls finish and returns data to the main activity.
Each activity (including the main one) has a bar on the top that displays a custom view. The custom view contains a canvas which has a drawing that is dependant upon the state of the network.. i.e. wifi/mobile etc...
Since that 'state' data never changes, it's held within a singleton and the view gets data from the singleton to define what it draws. That is working with no issues, i.e. the data is always as I expect it.
When I first launch the MainActivity, as the network changes, the data changes and each call to 'invalidate' the view receives a system call to 'onDraw' as I would expect.
In each of the sub activities the same is again true.
Upon finishing a sub activity and returning to the mainActivity, calls to invalidate no longer cause a call to onDraw to occur.
I have looked at this for quite a while now and just cannot figure out what is going wrong.
In my constructor I have:
setWillNotDraw(false);
Whenever the data changes the following methods are called:
invalidate();
requestLayout();
Now, there's one more thing... upon returning to the activity at that immediate point, I refresh and this DOES draw correctly, i.e. invalidate does trigger an onDraw call... any subsequent network changes (which are propogated) fail to result in the onDraw call.
I'm wondering if this is to do with the view somehow being detached. I can see that 'onDetachedFromWindow' is called, however the trigger for this is the destruction of the subactivity, hence I don't see why that should affect the MainActivity but it's the only thing I can think of.
I'm hoping I've provided enough information for someone to help me...
Well, in the end my answer has very little to do with the question and I guess this is an example of how an issue can be solved by going back to absolute basics and checking for the obvious.
My activities all inherit from an abstract activity. Within that activity there is an instance of the view. The views in which I was having trouble were using that declaration as opposed to having their own instance, hence behaviour from one activity was then affecting another inadvertently.
So, if I'd been able to post up all the code, I'm sure someone else would have spotted this but, unfortunately I couldn't in this instance.
Still, whilst this posting doesn't provide a resolution that will help others, maybe it does say... step back and check the obvious first!
I found a few articles talking about how RxJava/RxAndroid can replace event busses (such as otto)
https://lorentzos.com/rxjava-as-event-bus-the-right-way-10a36bdd49ba#.7a4619qva
https://medium.com/mobiwise-blog/use-rxjava-instead-of-event-bus-libraries-aa78b5023097#.ew28h2urf
A quote from the first article:
Otto from Square got officially deprecated the previous days. In the Android world we can cheer now something like “EventBusses are dead long live RxJava”.
There is one thing I am missing though:
One of the perks of event buses is that they help a lot with the Activity lifecycle in that you don't need to manage registering/unregistering to callbacks manually (and thus avoiding memory leaks easily)
Example flow:
Activity subscribes to an event for getting songs (say SongsAvailableEvent)
We request songs (we make a network request)
We change the device's orientation mid-request
The Activity dies and a new one is built, that is also subscribed to the SongsAvailableEvent
The new activity gets the event and updates the UI, and the old Activity (which is now dead) does not get the event (yay!)
The articles above make it look like this flow is "solved" by RxAndroid/RxJava, but using Rx you still need to subscribe/unsubscribe on an Observable manually when you change the device's orientation. Moreover, if I want to "reuse" the request made in an Observable, I need to somehow persist it so that I will subscribe on that same Observable in the new Activity (I'm not quite sure how to do that, but it is not the point :) ).
My question is: is this problem easily solvable with pure RxAndroid/RxJava, or do I still need to use Rx with an event bus / extend Rx using something like RxLifecycle (which complicates things since I do not manage my Observables in the presentation layer)?
Your Activity's onDestroy can always call unsubscribe.
As for making things work to reuse request- Look into Loaders and LoaderManager. EventBus and RxJava to solve that was never needed.
I would venture to say that there isn't any way out of the fact that at some point in the chain, the Observable has to be tied to the lifecycle of some Android platform object, such as an Activity. Also, because you have not mentioned it as a partial solution, I assume you are avoiding using retained Fragments. If you are creating and holding a reference to the Observable only within your Activity, it is not possible for the results of a request in-flight to survive destruction of the Activity and be automatically subscribed to the new one. In addition, at some point, either during an orientation change, or the Activity finishing in the middle of a network request, your Observable will leak a reference to the Activity (via its subscribe() callback) if it is not unsubscribed on the Activity's onDestroy().
I have found RxLifecycle to be simple to use. My base Activity class has a method on it:
public <T> Observable.Transformer<T,T> bindLifecycleOnMainThread() {
return o -> o.compose(lifecycleProvider.bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread());
}
lifecycleProvider is created as per the instructions for RxLifecycle, depending on how you create your provider. This particular implementation uses bindToLifecycle() rather than specifying an explicit lifecycle event, so its use is contextual. Calling it during onResume will cause it to end on onPause. Calling it during onStart will cause it to end on onStop. Calling it other other times will cause it to end on onDestroy. Since this subscription will be updating the UI, it must only be observed on the UI thread.
This can then then used in the Activity as follows:
yourObservable.compose(bindLifecycleOnMainThread())
.subscribe(event -> handleEvent(event));
Now, where does this observable come from? Well, there's still no magic, and if you want an Observable to have a longer lifespan than the Activity, that means the Observable must be held by a component that lives longer than the Activity. There are many, many ways to do this, but your particular use case maps well to the new ViewModel library included in the Android Architecture framework. If you were to use ViewModels, your ViewModel would have a method that begins the network request, and would have a PublishSubject or PublishRelay that would emit SongsAvailableEvent objects (though I recommend exposing it to your Activity as only an Observable<SongsAvailableEvent>, not a Subject, for good encapsulation!). Your ViewModel would make the network call and forward the results to your Subject.
Finally, your Activity, when created, will immediately get its ViewModel from the ViewModel registry and subscribe to the Observable<SongsAvailableEvent> (which is a Subject/Relay) exposed by the ViewModel, and then bind it to the Activity's lifecycle, as in the example above. The ViewModel will survive any orientation changes of the Activity, and therefore so will the observable. The Observable will then never attempt to deliver an event to a destroyed Activity and the new Activity will immediately begin listening for events.
I believe this strategy promotes good encapsulation, since the Activity does not concern itself with how the network request gets made, and does not concern itself with how the source Observable is created. The only way that the Activity manipulates the Observable is by choosing what happens when it receives an event, and binding the subscription to the lifecycle of the Activity.
This can be endlessly tweaked and refined by composing your Observables but this should get you on the way.
Let's imagine situation:
User click on 'Login' button and Fragment(View) call Presenter's method doLogin().
Presenter starts some async work and now Boom! app is closed(moved to recent apps)
Presenter survives and async work is still happening.
Async work finished while app was in the background.
User came back to app, but he doesn't see any notification that work is finished as view was de-attached:
if(isViewAttached()) {
getView().setLoaded(workResult);
}
And I want to fix it. The only way that I see is to use Queue<MessageToView> and when View has attached again, execute every "Message".
I think that there is a library that can handle my case. So, is it? Or what pattern can I use?
See github pages FAQ section:
Can the Presenter and its view be out of sync during a screen
orientation change?
Excellent question. Mosby assumes that all interaction from Presenter
with the View happens on android’s main UI thread. Hence the answer is
no that cannot happen since screen orientation changes are executed on
the main UI thread as well. So either is a screen orientation executed
completely (view reattached) or the presenter invokes the views method
after view is reattached since both run on main UI thread or the
presenter invokes the views methods before starting screen orientation
change.
So as long as your Presenter invokes View methods on main UI thread everything works out of the box.
Try using the Fragment's onResume() lifecycle method, and then call something like presenter.updateViews()
Background
I'm working on making an app better by supporting its landscape mode. One thing that I use a lot is Loaders, or more specifically AsyncTaskLoaders .
Using Loaders allow you to keep doing a background task even if the activity is being re-created due to orientation changes, as opposed to AsyncTask.
The question
I'd like to ask about the lifecycle of Loaders:
When do they get GC-ed ? Do I have to keep track of them, and if one has a bitmap inside, should I abandon it as soon as possible? Do they perhaps get GC-ed only after the activity is really destroyed (and not because of configuration changes) ?
I've noticed they have states of being stopped. Does this somehow allow me to pause them?
If #2 is true, How would I implement a loader that can be paused on some points of itself?
Can fragments also have Loaders? As I've noticed, it's only for activities.
if #4 is false, what is the recommended way to use loaders in the design pattern of navigation-drawer that replaces fragments in the container?
Can AsyncTaskLoader be interrupted like AsyncTask (or threads)? I've looked at its code and at the API, but I can't find it. I've also tried to find a workaround, but I didn't succeed.
If #6 is false, is there an alternative? For example, if I know that the loader doesn't need to load something, I could just stop it right away. One way I can think of is to set a flag (maybe AtomicBoolean, just in case) that will tell it to stop, and check this value sometimes within. Problem is that I will need to add it even inside functions that it uses, while an easier way would be to call "Thread.sleep(0)" or something like that.
Is there somewhere an explanation of the lifecycle of Loaders?
Do AsyncTaskLoaders work together, at the same time, or are they like the default, current behavior of AsyncTask, which runs only on a single thread ?
1.When do they get GC-ed ? Do I have to keep track of them, and if one has a bitmap inside, should I abandon it as soon as possible? Do they perhaps get GC-ed only after the activity is really destroyed (and not because of configuration changes) ?
Since the loader lifecycle is tied to the activity/fragment lifecycle, it is safe to assume that the garbage collection pretty much takes place at the same time. Take a look at #8 for the lifecycle of loaders. Might give you some ideas.
2.I've noticed they have states of being stopped. Does this somehow allow me to pause them?
No, as far as i know loaders do not have a onPause() per say.
3.If #2 is true, How would I implement a loader that can be paused on some points of itself?
I really have no answer to this one. Would like to know a solution to this myself.
4.Can fragments also have Loaders? As I've noticed, it's only for activities.
Of course fragments can have loaders. Just initialize the loaderManager in the onActivityCreated() method
5.if #4 is false, what is the recommended way to use loaders in the design pattern of navigation-drawer that replaces fragments in the container?
4 is true. So this question is irrelevant i guess.
6.Can AsyncTaskLoader be interrupted like AsyncTask (or threads)? I've looked at its code and at the API, but I can't find it. I've also tried to find a workaround, but I didn't succeed.
I am not sure what do you mean interrupting the loaders. But if you mean having something similar to a isCancelled() method, then there is a method called cancelLoad() on the AsyncTaskLoader. The complete flow is like cancelLoad()->cancel()->onCancelled() i think.
7.If #6 is false, is there an alternative? For example, if I know that the loader doesn't need to load something, I could just stop it right away. One way I can think of is to set a flag (maybe AtomicBoolean, just in case) that will tell it to stop, and check this value sometimes within. Problem is that I will need to add it even inside functions that it uses, while an easier way would be to call "Thread.sleep(0)" or something like that.
Irrelevant again?
9.Do AsyncTaskLoaders work together, at the same time, or are they like the default, current behavior of AsyncTask, which runs only on a single thread ?
Runs on a single thread.
8.Is there somewhere an explanation of the lifecycle of Loaders?
To my best of knowledge:
When activity/fragment is created the loader starts -> onStartLoading()
When activity becomes invisible or the fragment is detached the loader stops -> onStopLoading()
No callback when either the activity or the fragment is recreated. The LoaderManager stores the results in a local cache.
When activity/fragment is destroyed -> restartLoader() or destroyLoader() is called and the loader resets.
I hope this helps. I might be a bit off on some of the answers. I am constantly learning new things myself.
Cheers.