Android MVP - Detaching view (onPause VS onDestroy) and state preservation - android

I'm trying to implement the MVP architecture in my app.
However, after reading some blogs and viewing some sample project samples, I'm not sure I completely understood where is the right place to detach the view, and what should be done once the view attached for the second time after an async operation.
Most of the examples I saw, just sum it all up with a view's null validation check after an async call.
I'll try to make my point clear with an example - Login/Registration by phone number (The main idea is the important thing, and not the example itself)
There is an activity which display a fragment - LoginFragment.
The user enters his phone number and tries to login.
If the user exits - he should get navigated to another activity (after entering the code received by sms..)
If the user doesn't exits, he should get navigated to registration process - RegistrationFragment.
If there was an error, a dialog with error message should appear, ErrorDialogFragment.
Now, in a happy flow where the user presses the login button and waits until the process complete, all good.
But, in a less happier flows (not so frequent ones, but definitely can't get ignored), the user presses the login button and after that presses the home button or alternatively gets a phone call.
In scenario 1, where we attach/detach the view in onCreate/onDestroy, once the async login operation finish and we should replace to RegistrationFragment or show ErrorDialogFragment, there is a chance we will meet the famous IllegalStateException:
getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"
In scenario 2, where we attach/detach the view in onResume/onPause, once the async login operation finish we won't be able to replace fragment or show a dialog because the view is already detached.
In this case, I'm not sure what is the right thing to do.
Should we go with scenario 1 and commit the transaction with commitallowingstateloss?
I'm afraid it is a bad idea.
Or Should we go with scenario 2. In this scenario, we should act accordingly when view attached again, which means saving states (RegistrationRequied, ErrorHasOccured, LoginProcessStillRunning, etc..) in the Presenter/Interactor.
Can someone can shed some light regarding this?
Thanks in advance!

Oh the joys of the Android lifecycle. I feel your pain.
In my personal experience, resorting to commitAllowingStateLoss is usually a symptom of trying to update your Ui (View) while in the background (and as you note, the ui may be destroyed).
What I would suggest is that you don't try to update your ui without checking if the activity has been backgrounded (onStop or onPause depending on the situation). If your ui has been backgrounded, remember the changes you need to make and do them when your Ui is reconnected (onStart or onResume depending on the situation).
In essence I'm saying you should follow Scenario 2. And yes. You will have to save quite a bit of state somehow.
Unfortunately this isn't easy and there are many approaches to doing this ranging from using event buses, all the way through to using RxJava.
Every approach has it's advantages and flaws and they are all really too complex to discuss in detail in a single post.
However, I have a blog post I wrote some time ago on a way of doing this in a way that doesn't require additional libraries.
It's a little out of date now, but it may give you some ideas: A Simple MVP aproach for Android
All the best.
Kind regards,
Chris.

Related

In what order should I call events in this logout sequence?

Writing a kotlin social media app. I want to do 3 things within the logout sequence:
logoutbutton.setOnClickListener() {
//1. db.logout()
//2. finish()
//3. go to login screen
}
Does the order of events matter here? This is the order I am going with right now, but I don't know where the best place to put finish() would be.
Call finish when you want the current activity to popped off the activity stack. You typically do that when the user has indicated they no longer want to interact with it. Other than that, the order of these things don't really matter.
A trap that has caught me a few times is that finish() is actually asynchronous, so can give the illusion that instructions following it are "safe"... until they take a little longer and the activity gets torn down before they complete.
That can make for very difficult bugs to find.
I suggest for safety, call finish last.
I'm assuming db.logout may take some time to complete and/or may have to run on a background thread so some caution may be needed there. Fortunately Coroutines can help a lot .

Android: how to use global variables, activities, onResume(), startActivityForResult and so on

I have some confusion in mind. Being new to android, I am writing an App which should:
check if bluetooth is available
if it is, enable it if turned off
scan for nearby devices and show them to the user
the user clicks (chooses) a device
the app, cyclically, pools the device and shows it status
while polling, the user can ask for more operations (other activities on the same device), then return to main screen
Passages from 1 to 4 are mandatory, but it seems difficult to lay them down in a straight (simple) manner: the main activity, which calls two intents (to enable BT and to scan devices) seems to interfere. It seems that the main activity's onCreate is called too often...
Question #1: should I do all the work in the onActivityResult() method? Or should that method be used to collect results, and the true work should be done in onResume()? I say so because while in step 5, when trying to launch other activities, android complains about "not having a context and you need to set the flag NEWTASK in the intent - "is that really what you want?")" (sorry, can't remember the exact message).
Moreover: the main screen shows some statuses from device, refreshed via postDelayed(), and also accepts button's clicks. When the user clicks a button and a new activity is created, what happens to the main activity? Does it lose its variables, context, or whatever? In that loop, I don't need anymore any result from the user clicked activities, so question #2 is should I use onResume? Or is onCreate() called again? If using onResume() I need to keep track of the app status, where to store that status? I am using a static global class, and it seem to work, but I've read that it is not a good idea.
I know this question is kind of a mess, but I've read google documentation and many questions here in SO, and found a mess. In this moment the app is more or less working, but it does strange things at times and, worse (question #3) by using it a few minutes it gets slower and slower - can it be it leaks memory, or it creates too many threads because of postDelayed()?

Ideal way to tackle pending data changes (when to save changes)?

I have a rather peculiar case on my hands, and Im surprised that noone seems to have written about it/something similar for Android (or my Google skills suck)
Situation #1:
User can input text into field1 and field2.
User can also re-arrange items in a list (displayed in a RecyclerView)
Whenever the user does any of the edits, the UI is already showing the updated data (e.g. editing field1 will show the text as the user types it, and the list of items will show them in the new order as the user re-arranges them).
Saving the data right away here will trigger the UI to refresh (to display the same thing) and give the user a bad experience (field1 focus will shift to the first letter, and the app might crash if the user quickly re-arranges list items).
So it makes sense to store the edits and execute them at a later point.
Situation #2:
User can tap plus/minus buttons to increase/decrease a value
User can input text into field3.
As in situation #1 above, editing the field will already have the UI in the updated state. But, in this case - tapping the plus/minus button will also update the data, but the UI will not be updated (unless the data is saved, and the query ran again...).
Problem:
If data is saved immediately as the user performs an edit, besides doing a lot of saves, it makes for a bad user experience as the UI will refresh in some cases whereas its already up to date.
If the edits are tucked away and performed at a later point, the UI wont refresh.
Im using MVVM, so all performed actions are sent to the viewmodel and it decides what to do. I find myself looking for a solution that works differently across different screens of the app, but I know that would just be shooting myself in the foot and then jumping off a bridge. Surely, there must be someone out there that has come across this challenge and had some insights around it?
Ideal solution: One solution that just works for all the different screens. Do you have it? Please let me know.
/ Desperate Android Dev
First of all, let me start by stating that I don't think there is a correct answer here, but you should consider what your own app does and then determine what you need to do.
Let me explain. Consider two application, one that saves TODO items and the other is a banking application.
I will now explain what I think could work for your application, since you have not mentioned explicitly any requirements that contradicts that.
In situations like that, I believe being optimistic is a good idea. Assuming that things will not fail, and when they do, try to back out. What does that mean?
That means, for example, in the scenario you mentioned, user enters something in a field. You should let the UI update automatically (Nothing we do here, that's just Android), when that happens you save those changes either locally, or to a server, doesn't matter.
Of course, you can optimize, instead of saving each letter, throttle the input somehow, but you get the idea.
This can either be a succeed or a failure, because we are optimistic, we let the UI update and the user get the feel that our application is lighting fast. You don't need to reload anything, or refresh anything. The UI should match your Model state now already.
What if the things go south, and your HTTP request or DB update fails for some reason, then you need to take action. But try to keep your reaction appropriate.
You can handle that failure in so many ways, again, depending on how critical what you are doing in your app really is.
You can just show a Toast, or even do nothing if the user action was so trivial.
You can show the user something a bit more concrete if the action is of some significance, maybe a Snakbar with retry and explanation of what happened.
You kill your process and finish all activities -kidding don't ever do that- but showing a very intrusive pop-up and possibly reverting the UI value to the correct one, if what the user was doing is quite critical.
Now this approach doesn't just give the feel that the app is really fast, but it also keeps things simple.
Another advise is don't try to solve problems that don't exist yet, that means don't start implementing background services and job queues for some local persistence jobs that never outlive a view, and could never will.
Instead, use measurements, log those errors and failures with some tool, and use those stats to know what needs to be fixed -if any-
Back to our two applications, this approach might be perfect for a TODO app, however this might not be too good for a banking app.
Assume the user transfers money, we say immediately, ALL GOOD MATE! and then the request fails and your user's landlord kicks him out for never paying rent.
So it all comes to how sensitive the operations your're doing.

Is there any way to alter the behaviour of Android restarting Activities after a crash

After reading through all the questions on SO regarding this topic i am extremely annoyed by this.
Crashes will happen, no one writes perfect code. And there are apps that require a certain logical hierarchy. The best example is the login-screen idea, where you are in some activity doing something on a server and now the app crashes. After a restart, the app lost all its login-session data and saving it might not be the safest idea. So presenting the user with the login screen after a crash would be the best and most logical thing to do. The best user experience.
However Android decides to remember the Activity stack but only restart the last working activity with a "blank" application under it.
The only viable option i see would be to check in EVERY single activity if some login state is available or not and if not, start the login (launcher) activity with a clear-top or clear-task. But this almost forces you to write a base extends Activity class in which this behavior is implemented and then all activities have to extend that. But what if, for some reason, you cannot extend it because you need to extend some other type of activity?
There is android:clearTaskOnLaunch but this happens every single time the user returns from exiting via home button. There is the antagonist finishOnTaskLaunch that finished an activity every time the user presses the home button. So the Android devs are aware that sometimes one would like the app to appear in a certain state after exit but a crash seems to be exclusive to all that.
Using a custom UncaughtExceptionHandler gives me some chance to act after a crash but as the apps state is unrecoverable i can only perform certain tasks that will happen in addition to Android very own behavior.
So my simple question is, if there is any way, that is built into Android, that allows me to change the after-crash-behaviour of Android in a natural way that does not depend on the version it's running (to some extent ofc) and will result in a somewhat smooth user experience.
I would say the proper way of obtaining the result you would like would be to provide a proper parent or up navigation stack. You can read about it more here https://developer.android.com/training/implementing-navigation/ancestral.html.
But the gist of it would be to use the idea of parent activities to provide proper back navigation. If the activity already exists it will just go back to it, if it doesn't (such as in the crash scenario) it will launch the correct activity when the user navigates back.
NavUtils is a handy class to help build this behavior and is part of the support library which would work on a range of different API levels.

How should I deal with persisting an Android WebView if my activity gets destroyed while showing another one?

I'm building an Android application that's based around an enhanced WebView (based on PhoneGap). I've enhanced the WebView so that from JavaScript running inside you can invoke the native contact picker to choose a phone number (which may be supplied by Facebook for example).
The problem I have is that the native contact picker runs in an activity in another process and the Android docs say that while another activity is open my activity may get destroyed due to memory constraints. I haven't actually seen this happen in my application but if it did then I'm guessing my WebView's state would be destroyed and the code that was waiting for the picked contact would be terminated.
It seems a bit crazy that the activity requesting a contact could be destroyed while the contact picker is open. Does anyone know if that does indeed happen? Is there a way to persist the state of the WebView if it does?
Thanks,
-Shaun
Does anyone know if that does indeed
happen?
You're looking at the problem too simply.
You have a WebView. You open the contacts application. While the user is in the contacts application, a phone call comes in. While on the phone call (using a Bluetooth headset), a text message comes in, so the user opens that up from its Notification. While still on the phone call and texting away, a text comes in with a link, so she taps it and brings up the Browser application.
By this time, your activity is surely destroyed, except on maybe some of the most recent phones that have a fair bit of RAM.
Now, is that common? No. However, this also has nothing to do with the contacts application -- if the user presses HOME, at some point in the future, your activity may be destroyed to free up RAM as well.
Is there a way to persist the state of
the WebView if it does?
That depends on what you consider "the state of the WebView" to be. This really is PhoneGap's job, if you are making a PhoneGap-based app. So, you might consider asking them.
There is no way to persist the DOM. There are trivial ways to persist the URL (see onSaveInstanceState()). And there may be stuff in between that you consider part of "the state of the WebView" that may or may not be possible to save.
The long and detailed answer is a bit complicated, but essentially it comes down to a few points:
If the Android OS has to go cleaning up Activities or Services, it knows how to prioritise which ones should go first. It does this based on whether they're currently in the foreground (last to go), in the middle of executing code, waiting for a result, or simply sitting inactive in the background (first to go). You can be reasonably certain that if your WebView Activity launched the contact picker using the startActivityForResult, it won't be killed
There's a whole system for saving data if an activity is killed, such as the onPause method (which are triggered as soon as your activity leaves the foreground), the onSaveInstanceState method which is called when your activity is about to die. Read up on those to get more information, and the configurationChanged method when the screen orientation changes. If you haven't at least skim read the Activity Lifecycle document on the developers page, you must do that.
Lastly, I'm sure this question has been addressed many times, but with slightly varying wording or situations. Have a look around, see what else you can find.

Categories

Resources