I'm reading article "Managing the Activity Lifecycle" (http://developer.android.com/training/basics/activity-lifecycle/index.html) and I'm little confused. Maybe first I'll try to say what my application do. So, this is some kind of http client. User login to the server and client store authorization (session id). After login, TimerTask is executing, which for every 10 seconds get some small json from server and by the way server know that authorization key is still alive (normally it is valid for ~30 minutes). In this json could be some events which should be shown to the user (I'm using notification manager for this) or questions for which user should answer (I'm showing custom dialog with "Yes", "No", "Don't know" and then POST answer to the server).
This works fine when my application is in foreground, but I don't really know what should I do if the application is stopped/paused.
My doubts:
I want that TimerTask should works even if user click home button or another application appear. There are two reasons: One - user need to be notified about events, two - I need to keep alive session id. But this article say that when activity is in foreground, it should release resources. What this mean? What are restrictions? Must I stop my timer?
Documentation say that system can kill application when it is no longer needed. What does it mean no longer needed? When user don't use it or when application code do nothing for a while? Can my TimerTask keep alive application?
Storing authorization key. I need remember session id in situations where activity is recreated by system like orientation change, etc. I'm using for this SharedPreferences object. Problem is that using this object, key is saved in database and I can't recognize when my application is closed permanently (which mean "logout") or just recreated because orientation is changed. This occur situations when user run application again after couple hours and my activity restore dead session id (my application look like "logged in" because authorization variable is not empty and I use this state as a flag). I need some temporary version of SharedPreferences object. What about bundle object passed in onSaveInstanceState? Is it temporary?
Regards
It looks like you are on the right track. I think that you should continue reading documentation. Some suggestions:
Activity life cycle
Context.bindService()
ServiceConnection
Related
I hope I can explain this well ...
I am trying to understand better how to handle HTTP callbacks in Android, so I have created a simple app that uses Volley for HTTP requests. It only has a button that triggers an HTTP request to a service that, basically, just updates a number in a database and sends it in the JSON response after 5 seconds. The Activity gets the response and displays the response in a TextView. I am testing it in a real device that has enabled the "Don't keep activities" option in Settings - Developer Options.
This is the scenario I am testing:
Start App.
Tap the button that triggers the HTTP request.
Inmediately after tapping the button, tap the device's home button to send the app to background. onDestroy method is called because of the "Don't keep activities" option.
Wait a few seconds for the HTTP response. I can see the device gets it because it is printed in the logcat monitor and the database is updated.
Before running the callback, I check that the activity is still alive. Since the activity has been destroyed, the callback is ignored. If the app is restored from background, there is no crash but the Network Response is missed. Also, if I tap the button again, it sends a new HTTP request and increases the number again ...
So, the questions are:
Which are the best practices to deliver network responses to the UI? I mean, if instead of a simple operation let's say it was a register form and I get a phone call or something that forces me to send the app to background, where anything can happens, how can I make sure to not miss the network callback? Is there something that could delay the callback execution until the app is again in foreground?
Is there a way to save a Bundle like the one in onSaveInstanceState after onDestroy has been called and restore it when app is again in foreground?
Let's say the information that the HTTP response contains is sensitive. Is there a recommended way to handle this case? I was thinking to save the response in the internal storage and check for it when the app is again in foreground, but I don't know if it is possible to do that after onDestroy has been called, or if it not a good idea with sensitive data.
Thanks in advance!
1)YOu can never miss the network callback. You'll be called even if you're in the background, unless your entire app (not just the activity is killed). You'll just get the callback while backgrounded.
2)No. If you need the result of a network call the next time the activity starts like that, I suggest you use a Loader to load the data. That way you can query for the Loader results next time, and start the request only if needed.
3)Do what I suggested in 2 and there's no need for this question, its all in app memory.
In the wonderful article by Chet Haase I read this advice which I find quite important:
never make a network request in your Application object. That object
may be created when one of the app’s Services or BroadcastReceivers is
started; hitting the network will turn code that does a local update
at a specific frequency into a regular DDoS.
The application I work on currently follows this (bad) practise: it performs a user login in Application.onCreate() - on a background thread of course, but still. This is a requirement: user needs to be logged in before any activity would do any other tasks, which usually depend on logged in user. I currently do this using RxJava in a way that any activity task observables are flatMapped onto an userlogin event and it works quite nice.
So if I should take that login task out of Application, where should it go? At first I thought it would be nice to use ActivityLifecycleCallbacks and watch for the first activity to be created. But this callback (onActivityCreated) will be called after creation, which is too late for me.
So I think that this should be done by creating some BaseActivity class and putting login and other initialization calls in it's first onCreate(). But I don't feel this is too good, because I'm mixing some app-wide logic in an activity class, it's smelly...
Anything I could have missed?
SplashActivity
An activity that starts the application. It checks for resources availability and if needed, obtains them. It also checks whether there is an active user session, and if there isn't performs a log in, if there are remembered credentials, or redirects the user to the Login/Register screen
BaseActivity
An activity that is specific for your app and that holds initialization and lifecycle callback code that is applicable for all your activities in the application.
In Android it is generally a good practice to perform no database operation (or at least complex ones) in UI-Thread. I have an activity with a complex form and I want to ensure that all data is saved when the activity goes in the background (e.g. the user presses the home button or a phone call comes in). In the activity’s onPause()-method I can start an AsyncTask which stores the data in database but I can never be sure that the task finishes successfully because android can kill the process before the task finished because the activity and the whole app is in background.
I can save data synchron in the onPause-method but then it’s possible to run in to an ANR.
I know that Android restores the views after the activity was killed but this works only correct when View Ids are unique. I have a lot of programmatically added Views where I cannot ensure the Id’s uniqueness and to use the saveInstanceState-functionality is nearly impossible because I have to save very complex models.
Is there any possibility to ensure that data will be saved before android kills a process without doing it in the UI-Thread?
I created an application once where I had similar data consistency concerns. What I did there is delegate the storing of the data objects to a Service I created just for that purpose. Although this makes the starting/stopping/initialization of your Activity a lot harder (once the activity is started again, you will have to wait for the service to complete its previously started save action), this was the only "Android" way I could think of to deal with this problem.
You might look into using a service for that if you are afraid that the system kills your background-processes before they are completed. This might be over-kill, but will definitely work as expected =) Just google "Android Service Tutorial" if you are unsure how to use them.
-Services won't be killed unless you want them to!
Indeed, if you're running an AsyncTask in onPause(), Android can kill your applications's process without waiting for the worker thread to finish. But it won't kill the process if there's a running Service. So a nice solution here is to implement database synchronization logic using an IntentService.
I'm facing the same question here, when to save data: while the user completes a form or when the activity pauses. Also we must take into consideration screen rotations or other events that might result in data loss.
Here is what I found on the Android developer site:
For content provider data, we suggest that activities use a "edit in
place" user model. That is, any edits a user makes are effectively
made immediately without requiring an additional confirmation step.
Supporting this model is generally a simple matter of following two
rules:
When creating a new document, the backing database entry or file for
it is created immediately. For example, if the user chooses to write a
new e-mail, a new entry for that e-mail is created as soon as they
start entering data, so that if they go to any other activity after
that point this e-mail will now appear in the list of drafts.
When an activity's onPause() method is called, it should commit to the backing
content provider or file any changes the user has made. This ensures
that those changes will be seen by any other activity that is about to
run. You will probably want to commit your data even more aggressively
at key times during your activity's lifecycle: for example before
starting a new activity, before finishing your own activity, when the
user switches between input fields, etc.
This model is designed to
prevent data loss when a user is navigating between activities, and
allows the system to safely kill an activity (because system resources
are needed somewhere else) at any time after it has been paused. Note
this implies that the user pressing BACK from your activity does not
mean "cancel" -- it means to leave the activity with its current
contents saved away. Canceling edits in an activity must be provided
through some other mechanism, such as an explicit "revert" or "undo"
option.
You need to start a backgrounds service daemon with a notification to make sure your data is saved and shut down the service and notification as soon as the data is saved. The notification will be shown until the background service is running as it is mandatory to show services of background service otherwise your application would crash.
I’m developing application for most major Mobile OS like iOS, Windows Phone and Android. I have a request from my client that simply possible to implement in iOS and WP but sounds really tricky in Android.
In iOS and WP, an application lifecycle is controlled through events which an object like UIApplication and Application receives.
In iOS, for ex., applicationDidEnterBackground:, applicationWillEnterForeground:, applicationWillTerminate: and the like, clearly define application states such as Inactive, Active and Background and make the app state management logic really straight forward.
In WP, Application receives well understanding events such as Launching, Deactivated, Activated, and Closing which make it really simple to decide what should be done in each app state logically to save as restore application wide object model.
But in Android, application state management sounds really difficult with Activities’ state changes such as onCreate, onRestart… onDestroy method overriding. My problem arises where I want to control the whole application state when the user session goes expired and I want to redirect user to the sign in activity and shuts down other open activity.
Regarding the fact that calling finish() in an activity’s onCreate(), onRestart() or onResume() is ignored by Android (according to the documentation) .
Even if I override android.app.Application and put the logic there, it sounds like controlling open activities is not possible.
I almost tried all possible combinations of activity launch mode (such as SingleTask and SingleInstance) though I cannot produce behavior like those exist in iOS and WP.
There is another post related to this question which may clarify my problem more.
The question exactly is, “Is it possible to produce iOS or WP application behavior in Android anyway?”
So essentially, once a "session" expires, no matter what the user tries to do, you want them to be redirected to a login activity, yes?
Assuming you have a method you can call which tells you whether or not a session has expired, why no simply check that method in onResume() etc. and if the session has expired, redirect the user to the login Activity?
There's an answer here about the application state that may interest you:
Checking if an Android application is running in the background
With Application, you get the onCreate and you can put some logic here.
So yeah, it's not as straight forward as in iOS but it's doable.
If it's just a session state, create a base activity that check against the session state
and inherit all your activities from it.
You can close all your activties by using the Android SDK before going to the login page or... lock the back button.
I'm trying to implement some automatic logout code for my Application on Android.
I need to detect if all the activities belonging to an Application have entered the background as opposed to working with onPause() and onResume() for each individual activity. iOS has a helpful applicationDidEnterBackground: method that I could utilize, but I'm unable to find a similar function in Android's Application class.
One approach seems to be to have an AtomicInteger counter and increment it once an activity becomes visible and decrement it when it's finished or onStop() gets called. So if the counter becomes zero, I can start a service that runs in the background and handles the logout. Is this how it's usually done?
There is no global callback for this, but for each activity it is onStop(). You don't need to mess with an atomic int. Just have a global int with the number of started activities, in every activity increment it in onStart() and decrement it in onStop().
You really don't want to log out the user when the "application" goes in the background, any more than you log out the user of a Web app when the user switches to another tab or minimizes their browser window for a moment. If you were to do either of those things in a Web app, your users would consider your Web app to be an epic fail. Similarly, if the user gets a phone call with a wrong number, or the alarm clock goes off, they'll be rather irritated with you if they have to immediately go back in and sign in when they were just using your app 5 seconds ago. Here, by "irritated", I mean one-star ratings on the Market and nasty comments.
A Web app automatic log out is based upon inactivity, using a server session cookie.
Similarly, when I build a secured Android app, I'll be implementing an inactivity-based mechanism, perhaps something like this:
Step #1: Create a Session class with a static singleton instance. The Session object holds the last-accessed timestamp.
Step #2: In each activity's onResume(), see if the Session singleton exists. If not, it's a brand-new process, so if this isn't the authentication activity, immediately do a startActivity() to bring up the authentication activity.
Step #3: Back in each activity's onResume(), if the Session object exists, call something like extend(). This would return a boolean, true indicating the session is still good (and the timestamp has been updated to now), false otherwise. If it returns false, do the same stuff as if the Session object were null.
Step #4: Your authentication activity, upon success, sets up the singleton Session object with the current timestamp.
Step #5: Your Session class' extend() method is where you make the determination if the session is too old.
No matter how the user gets into your application, if the session is too old (or it's a brand-new process), they are forced to authenticate. Yet, if the user briefly is interrupted -- where you and/or the user can define "briefly" -- they don't have to re-authenticate.
I think your suggestion is probably the best way to go. Unfortunately I don't think there's an API call to detect if your app is in the background or not. You'll just have to manipulate the onPause() and onResume() methods. Just keep in mind that you'll need need to account for transitions between activities, so once your AtomicInteger reaches 0, I'd wait a short amount of time and recheck that it's still 0 to make sure it wasn't just transitioning activities.
Create an Application class and include in the manifest
<application
android:name="com.example.hello.MyApplication"
public class MyApplication extends Application implements
ActivityLifecycleCallbacks, ComponentCallbacks2
override the following method
#Override
public void onTrimMemory(int level) {
// this method is called when the app goes in background.
// you can perform your logout service here
super.onTrimMemory(level);
}
this is valid of API level 14 and above.
You can even perform the the logout based on the amount of time the app is in background, which i would suggest is a better option. here is what you can do to create as "session timeout"
save the time stamp in SharedPreferences inside the onTrimMemory(int level) method
on all your activities onStrat() get the time stamp from sharedPref and compare it with current time. based on this you can perform a logout.
and clear the shared pref on onCreat of MyApplication