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()
Related
Is it bad practice to pass a UI element, such as a TextView, to AsyncTask.doInBackground() (through AsyncTask.execute()) and to read fields from the view in the background thread? (I know it's not okay to alter GUI elements from a background thread.)
It is not great, for the simple reason that you do not know if that widget is any good anymore.
Suppose the following happens:
You execute the AsyncTask, but it is stuck behind other tasks and does not run right away
The user rotates the screen, or presses BACK, or otherwise destroys the activity
Your AsyncTask finally starts running and you try accessing this widget
In the best-case scenario, the widget is simply wrong. In the worst-case scenario, whatever you call on it causes some sort of crash, because the hosting activity is destroyed.
AsyncTask itself is fairly obsolete; modern Android development uses other things (LiveData, RxJava, and Kotlin coroutines being the biggest candidates). However, if you wish to use AsyncTask, please ensure that it does not try referencing the activity or its widgets from doInBackground().
Theres lots of stuff online about wrapping a presenter into a headless fragment and setting onretaininstance to true. which keeps the fragment from being destroyed. But lets say the presenter was in the middle of a network download and a configuration change occured. The activity is about to be recreated but in the middle of the onCreate call the presenter gets the info and has NO VIEW to deliver to as onCreate did not finish (or did not start yet). so the presenter has this data it would like to update the view with but there is no view yet. Its a timing issue.
How to resolve timing issues like this?
It's all about maintaining data in presenter. Make sure you retain the instance of presenter and update fragment reference to presenter.
If you already have data in presenter just update it on resume of fragment else presenter will give a call back.
TL; DR
Always start long term operations from a service. (or a JobScheduler)
Description
If you are downloading a reasonably big file, use the presenter to trigger an IntentService (or JobScheduler for Android Marshmallow and later), never AsyncTask, Thread or any asynchronous structure that would callback to the main thread.
The presenter survives to onPause, but not to onDestroy, so if the application be killed by the operating system the reference to the activity/fragment will be lost anyway.
If you are running the download in a service, your presenter can check if the file already exists and updates the activity/fragment properly.
Remember that the fragment being destroyed is actually good for the OS, so use early/often persistence of network data :-)
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
When are the view's 'killed' and no more exist in Android ?
For example, suppose I have an asynctask and I run some network related stuff in the doInBackground() method. Then, I need to update my UI views in the onPostExecute() method.
Assume my doInBackground() took a while and while it was being processed the user moved back or even pressed the home button. The task will continue because doInBackground runs on a seperate thread, however, once it is finished and onPostExecute is called to update the views, the views might not be there.
Even if the activity is not visible (either gone to home screen, or another activity), what happens when the views try to get accessed and modifed ? How long do they stay in the 'heap/memory/whatever', do they get garbage collected after onDestroy ? or they stay around after that ?
I know a way to not get into this hastle is to use a switch that gets turned on and off inside onResume and onStop and check it before updating the views in onPostExecute, but I am unsure if this is solid approach applied in the android apps ? if not, what is the suggested way ?
A view inside an activity can be considered like any other object within the activity class. It will stay in the memory as long as it is referenced by the some other object. This object could be another view or activity. This means the view will be gone if:
1) The activity or the parent view remove it removeView() from the view tree thus no one keeps a reference to it.
2) The activity or parent view that contain the view are destroyed/gone from the memory.
If the activity is not visible (either gone to home screen, or another
activity), what happens when the views try to get accessed and modifed
? How long do they stay in the 'heap/memory/whatever', do they get
garbage collected after onDestroy ? or they stay around after that ?
You can access the view, as long as your activity is available. And you can find more about that by reading the Activity Lifecycle
When you try to access a view that is gone from the memory, you will get a NullPointerException. The simple & solid way how you can handle onPostExecute is by checking for null before updating, example:
// inside onPostExecute
if(textView != null) {
textView.setText("Background Method Finished");
}
The advantage of this approach is:
1) You do not have to explicitly keep track of show/hide.
2) Sometimes, view is not on the screen does not mean that it gone from the memory. For example, let say your AsyncTask finishes while your activity is paused/stopped not destroyed. In this case, you can still update the view, so that when the activity is resumed the update is visible and is not lost.
Cancel async task when user leave from that activity. After cancelled task, onPostExecute will not called and capture cancel event in onCanceled event in async task class.
You have many ways
1.You can cancel the asynctask
2.You can kill the process,so there will be nothing in the menory or heap.
I have a tab widget where one of the tabs is a chat-type feature. I want to update the chat data at an interval (variable depending on whether the chat tab is active or not).
The best approach seemed to be using an AsyncTask in my main TabActivity class, as that would avoid any issues of the chat activity being destroyed while in the background, while an AsyncTask was running. I wanted to ensure that the Activity isn't destroyed and recreated, thus causing my AsyncTask to be unable to modify the actual active Activity's data.
However, now that my AsyncTask is in the TabActivity activity, I don't have a direct way to call my Chat's ListAdapter notifyDataSetChanged() from my onPostExecute() method anymore. Is there a way to get a reference to a given Tab's current Activity from the TabHost/TabActivity?
Or, alternatively, can I assume my chat activity will never be destroyed as a child activity of the TabActivity activity (well, never destroyed while the TabActivity is active at least), and then just put the AsyncTask in the Chat Activity?
Really the best way to do this is to have a thread that runs and periodically gets the chat data. Then from your UI you can poll the thread to get new messages. This way you can have the thread running no matter what happens to the Activity.
If you're using the Intent loading mechanism for your tabhost, then you should be safe in assuming the task won't get randomly killed any more than the TabHost itself (only paused), and you can safely modify views in it (as safely as you could from the TabHost, at least).
But beware: Any activity can be killed at any time (example: if a user clicks on a link in the tab that opens a new activity while your task is still running), including the tabhost itself, which can lead to ugly crashes when the task returns and tries to update the UI of the now-dead Activity. So make sure to either cancel your AsyncTasks on destroy, have the asynctasks check that your activity is still active before returning results (see WeakAsyncTask in the Android source tree), or use an Activity-independent (semi)persistent pollable background-thread solution like CaseyB suggests (cwac-bus is a nice premade solution in that vein).
Alternatively you could just have the AsyncTask's UI-updating code catch all exceptions (I've seen a few apps that do this) and fail silently, but that smells funny to me.
Lately I've used a modified version of the WeakAsyncTask in most places that checks that the activity is non-finished on returning, personally.