Hi guys i have an app which is almost totally complete.It is a music player.It plays music from a service.
When i press back button that activity which started the service is obviously destroyed.(i want the activity to be destroyed so that the user can navigate other activities.The reason i am telling this is because singleton|singleinstance etc won't solve my problem) But i don't know how i can recreate the activity from the notification.
Also when the activity which started the service is visible(and not destroyed) then too if i click on the notification, my app stops unexpectedly.And hence i have two problems to solve here.
I am using a global variable which is a list and hence i don't think i need to save data before destruction.I think i know the concept how the activity state can be restored and recreated but i think i am not totally aware how it can be done.I am not sure in which method the activity state should be restored and how can it be restored ??Also i am starting the service in onCreate method.Should i change it to some other method?Can starting the service in onCreate be a problem when restoring the activity ?
Extra Note :Global Variables are available throughout the application and are independent of an individual activity,as they extend Application.Also Bundle will only help when the activity is destroyed by the system.When Pressing the back button we explicitly destroy the activity and hence data cannot be saved into the bundle as onSaveInstanceState is never called by the activity.
Activity state should be restored in onCreate. The onCreate method gets a Bundle as parameter which contains key-value pairs of data. If this parameter is null, it means that no previous state was saved. Just check if it's not null, and restore things if necessary:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
...
if(savedInstanceState!=null) {
// Restore activity state based on saved data!
}
}
Of course you need to save data if you want to use it at restoration. System automatically calls the activity's onSaveInstance state method in which you can put data into the bundle.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("key", value); // Store data
}
Please note that this all applies to the case when system destroys your application automatically. (For example, when other apps in the foreground run out of memory.) This mechanism is to get your activity back to a state in which the user last saw it.
If the data is consistent and reflects with the Service, then you need to bind to the Service and get the data from the IBinder (which can wrap a Service object). I, personally, prefer this method because I simply don't like overriding the Application.
An alternate method is to save data via the SharedPreferences. Save it your Activity state in onPause() and restore in onCreate(). Assuming you're not saving hundreds of items, it is very low overhead. This should only be used for data that reflects the state of the Activity itself.
You can play and stop your player based on Activity life cycle like
OnPause -you should call player.stop()
onresume you should call play and
ondestroy you need to release your player instance.
call a thread inside your service to get data from server so that it will work smoothly.
you should release mediaplayer instances by calling release or reset .
You can use Pending intent in notification so when you will see notification then you can open your activity whatever mentioned in pending intent.
Related
I am not understanding how android activities are managed.
I have an activity and every once in a while i have noticed that, when my app goes into the background, android destroys whatever the current activity is (say Activity3) and several other singletons and objects etc. Thats fine. The problem is when the app is resumed then intuition tells me that since android has destroyed the activity and objects for memory or whatever, then android would just restart the app completely from Activity1 so all the objects and data members would get properly initalized.
NOT SO!
It seems that when my app is resumed, the Activity3 is recreated and onCreate is called with the same parameters as it was the first time (when it was called from Activity2) only this time all the singletons and other objects that were initialized in Activity1 and Activity2 are recreated with their default values and are rendered useless.
How is this a safe policy/technique? How can android just randomly destroy objects and activities and then when the user resumes just call onCreate on the recent activity and expect everything to be hunky doory and NOT have to go through the proper startup procedure/initialization?
UPDATE / SOLUTION
Thanks to the commentors for their excellent info.
ACCORDING TO ANDROID DOCUMENTATION
onCreate
Bundle: If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null.
THEREFORE what I ended up doing is I set TWO flags. One in onSaveInstanceState in the Bundle so to know that it is a valid Bundle set by me. The other in the class itself to determine if onCreate was called because of recreation or Auto-Rotation. And so in onCreate I checked to see if onSaveInstanceState is not null, check the Bundle flag, and check bInit (which defaults to false). If both flags are true then it means android dumped and destroyed our apps memory and the safest way to ensure everything is initialized again in a linear-style application is to just restart it and launch the beginning activity.
public class SomeMiddleActivity extends AppCompatActivity
{
private static boolean bInit = false; // only way it will be false again is if android cleared our memory and we are recreating
#Override
public void onSaveInstanceState(Bundle state)
{
// set a flag so that onCreate knows this is valid
state.putBoolean("StateSaved", true);
super.onSaveInstanceState(state);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
// this must be called first always for some reason
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
if (savedInstanceState.getBoolean("StateSaved", false) && !bInit)
{
// we were recreated... start app over
Intent intent = new Intent(getApplicationContext(), Startup.class);
startActivity(intent);
finish();
return;
}
}
bInit = true; // this will stay true until android has cleared our memory
.......
}
Although this has worked thus far, if anyone has a different suggestion let me know. I will be posting another article on this.
And FYI: the onSaveInstanceState(Bundle, PersistableBundle) version of onSaveInstanceState is never called ever so I dont know why they even implement it. (?)
#goldenb #Rishabh Thanks to goldenb and Rishabh for the insight.
Android, if destroys, also gives you tools to handle it.
Mobile devices have limited amount of memory which needs to be shared among Applications running simultaneously. Thus, smart resource allocation is necessary. The Apps running on foreground are used by End-User and gain high priority for better performance and user experience. Thus, applications running in background need to release the resources to suffice the memory requirements for foreground applications. Hence, background applications are destroyed (not completely) sometimes (in case of low memory).
Android Activities have Callbacks likes onSaveInstanceState() and onRestoreInstanceState() which enable you to save your current state of Activity (i.e., values of variables) when it is destroyed and retrieve them when the Activity is recreated.
You can get more information from here: How to save and retrieve the state of Activity using onSaveInstanceState and onRestoreInstanceState.
You can perform validations on the retreived state to ensure the Activity performs exactly as it was doing pre-destruction. You would find it very easy and logical once you get hands-on it.
Just giving my 50 cents on the issue. The correct way to deal with the issue of an activity being killed by the system for its resources in background is a common problem in android and according to Google the solution for this is:
onPause() is where you deal with the user leaving your activity. Most
importantly, any changes made by the user should at this point be
committed (usually to the ContentProvider holding the data).
Emphasis is mine. But what this means is that the Android lifecycles are designed so that under normal conditions onPause should be called as an Activity or Fragment is sent to the background. They hint at this in several of the android documentation pages:
As your activity enters the paused state, the system calls the onPause() method on your Activity, which allows you to stop ongoing actions that should not continue while paused (such as a video) or persist any information that should be permanently saved in case the user continues to leave your app.
Also worthy of your attention: if you wish that views are restored during Activity recreation, you should have set the ID attribute of all views ;)
Note: In order for the Android system to restore the state of the
views in your activity, each view must have a unique ID, supplied by
the android:id attribute.
PS.
You were wondering why onSaveInstanceState(Bundle, PersistableBundle) is not called, one possibility is that you do not have the right activity attribute set
This is the same as onRestoreInstanceState(Bundle) but is called for
activities created with the attribute persistableMode set to
persistAcrossReboots..
I have a list activity which does the usual list activity type stuff. All of the activity setup was being done in the onCreate() method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_streams);
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this));
mActionBar = getActionBar();
mStreamListAdapter = new StreamListRowAdapter(this, R.layout.stream_list_row);
mStreamListView = (ListView)findViewById(android.R.id.list);
mStreamListView.setAdapter(mStreamListAdapter);
}
I do not have onStart() and onResume() overrides. I can successfully navigate out of this activity based on the menu buttons pressed and then navigate back to this activity. When I navigate away from this list activity via startActivity(newIntent), I do not call finish, but I do call finish in the newIntent activity, when coming back to this list activity. Everything works fine, unless either the ListActivity itself or one of the new Activities have been sitting in the background for a long time (i.e. I switched to another app). Long time is measured in hours. Sometimes (NOT always), when I come back to my app after this delay, the list activity does not display the list, the menu is not displayed either, just a blank screen with the activity title displayed in the ActionBar.
I realize that I need to handle coming back to the list Activity via onResume() (and maybe onStart()). But onResume() is always called after I navigate back to this list activity, how do I really know if the variables representing the ListAdapter and ListView have actually been destroyed by the OS and need to be recreated inside onResume(), I don't want to recreate them every time onResume() is called. Can I just check to see if they are equal to null? It's hard to test this, as this does not happen very regularly.
Thank You,
Gary
It sounds like you are missing some basic information about Android and Activity's lifecycle. First I want to mention how Android saves it's state. There are 2 important points here. When leaving an Activity it is at risk of being destroyed it calls the hook method
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("myKey1", 1);
outState.putString("myStringKey", "hello");
}
When this is called, this is a chance for you to save all the data that you'd like to persist. This could be ids, strings, arraylists, and nearly any other type of data.
Now that you've saved your data, in the onCreate method of your Activity you are passed back all of these values.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String myStr = savedInstanceState.getString("myStringKey");
int myInt = savedInstanceState.getInt("myKey1", -1);
}
// do the rest of your setup
}
On the first time onCreate is called savedInstanceState is null. To answer your question: "how do I really know if the variables representing the ListAdapter and ListView have actually been destroyed by the OS" this is how you can know. If savedInstanceState is not null, your stuff has probably been destroyed. However, I will say that anytime onCreate gets called that Activity was already completely destroyed anyway.
The other thing I will mention is Android is allowed to kill your backgrounded Activity whenever it needs to reclaim some memory. When it does this, it will call the onSaveInstanceState method for you. Android does not have to restart the application at the "first" or "MAIN" screen of your application. It can start off several Activities in if that's where the user left off. I mention this because it sounds like you're having issues whenever Android decides to create your activities after it clear's it's memory. The best way to make sure your application works well when this happens is to just rotate the phone. Rotate the phone from horizontal to landscape and/or back will cause the Activity to be destroyed and recreated with onSaveInstanceState called and passed back in to the onCreate. Based upon the information you've provided this is the best I can suggest for now.
Cheers.
I have finally figured out what was going on. I have 2 activities, ActivityA and ActivityB, and an Application object. Upon the application launch, ActivityA initializes some globals that are stored in the Application object and then starts ActivityB. When my app is restarted after being in the background for a long time, the onCreate() of the Application is called and then onCreate() of ActivityB is called. ActivityB relies on the variables in the Application object that normally are initialized by the ActivityA, but ActivityA is never called this time around and the globals are not initialized. I am still not clear why the ActivityA just hangs, instead of crashing with some message stating that the variables are null, which is what onCreate() in the Application object sets them to, but regardless, when this occurs, I need to go back to ActivityA() and reinitialize everything as I normally would, when the application is launched.
I now have a boolean in the Application object that indicates whether or not ActivityA has been executed after the onCreate() of the Application object has been called. ActivityB checks this boolean flag, and if not set, then simply start ActivityA to perform the on launch type initialization. I actually have to do this check in pretty much every activity.
I have an Android app that has a class that stores a reference to several entity classes. This class works well but with one issue.
If I have data in this class and then move the app to the background (press the home button) and wait long enough and do other things with my phone, when I bring the app to the foreground the data is gone. The app will load to the last opened screen (not restarting the app) but all data that was stored in this class is gone.
It doesn't happen always so I assume it is some sort of memory management issue but I am trying to figure out a way to keep this data persistent or, if it existed and now does not, have the app restart from the beginning.
How can I keep this data / rebuild it?
Review the Activity Lifecycle to better understand how and when your activity will be destroyed, created, etc.
Before your activity is destroyed the onSaveInstanceState(Bundle) method will be called on it. Override the method to save state to the bundle. Data saved in this manner will be provided to onCreate(Bundle) in subsequent calls.
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("myKey", "myValue"); // trivial, but for illustration purposes.
}
U can Save Data in Bundle object, this object will be passed as a parameter to the Oncreate of activity that you are loading. if The data you are storing is small then you can use sharedpreference and get it back while loading the activity.
My guess is like you might be clearing the object of your entity class in onpause method of your activity, so you may be loosing out data, just check that once..
Does anyone know of an exhaustive list of when the savedInstanceState bundle will be used in an activity?
I know it's used when the device orientation changes. However, it doesn't seem to be used when the user force closes the app from the Android settings, but this might be due to something in my code.
What other cases are there?
To be clear, by "used" I mean when onCreate() is called, the savedInstanceState bundle is not null and contains the data I passed into it the last time onSaveInstanceState() was called.
It's used when the Activity is forcefully terminated by the OS (ex: when your Activity is in the background and another task needs resources). When this happens, onSaveInstanceState(Bundle outstate) will be called and it's up to your app to add any state data you want to save in outstate.
When the user resumes your Activity, onCreate(Bundle savedInstanceState) gets called and savedInstanceState will be non-null if your Activity was terminated in a scenario described above. Your app can then grab the data from savedInstanceState and regenerate your Activity's state to how it was when the user last saw it.
Basically in onCreate, when savedInstanceState is null, then it means this is a 'fresh' launch of your Activity. And when it's non-null (if your app saved the data in onSaveInstanceState(...), it means the Activity state needs to be recreated.
onSaveInstanceState is used to store data only for application lifetime (i.e. temporarily)
The data is held in memory only until the application is alive, in other words this data is lost when the application closes, so in your case when you force close the app onSaveInstanceState is not used.
It can only be called when you do operations while your application is still alive, for e.g. when you change the screen orientation the activity is still intact so onSaveInstanceState is called.
However if you want to permanently store the data you would have to use SharedPreferences and SQLite database.
I am refering to http://developer.android.com/reference/android/app/Activity.html.
I have an activity which can be "interrupted" by the user, e.g. the user opens the menu to call the preferences screen. When calling the preference screen onSaveInstanceState(Bundle) is called and I can save my data. That's fine so far. But upon pressing the back button onRestoreInstanceState(Bundle savedInstanceState) is NOT called.
So how can I save my state? Do I have to do it when calling the new activity? But how?
The only way I can think of, is saving the state by passing the state to the new activity, do nothing there with the saved data, return it to the first activity and restore the state in onActivitResult. But that would mean that I have to pass data back and forth just to restore the state. Doesn't seem to be a nice solution.
Probably a bad answer, but are you sure onRestoreInstanceState needs to be called?
The point of the bundle and onSaveInstanceState / onCreate with bundle / onRestoreInstanceState is to save transient data for an activity that is in the history stack, in case the activity must be killed to reclaim some memory. If it is killed, the activity can be restored, as if it were never killed, via onCreate / onRestonreInstanceState. However, if the activity was not killed, there may be no need to restore its transient data - presumably it is just as it was.
The Android documentation is careful to point out that onSaveInstanceStae / onRestoreInstanceState are not lifecycle methods, so aren't guaranteed to be called during lifecycle state transitions. If you need to hook into certain lifecycle transitions, take a look at the lifecycle hook methods. For example, onResume is called when the activity becomes the foreground activity and onPause is called when it is no longer the foreground activity.
Have a look at the Application Fundamentals, specifically this part:
Unlike onPause() and the other methods discussed earlier, onSaveInstanceState() and onRestoreInstanceState() are not lifecycle methods. They are not always called. For example, Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key). In that case, the user won't expect to return to the activity, so there's no reason to save its state.
Because onSaveInstanceState() is not always called, you should use it only to record the transient state of the activity, not to store persistent data. Use onPause() for that purpose instead.
Basically, any persistant data should be written out in your onPause() method and read back in onResume(). Check out the Data Storage article for ways of saving data.
Your ListView should not be cleared after returning from the Pref screen unless the Activity has been destroyed while the Pref screen was showing (in which case onCreate should have been called with the saved bundle).
The savedInstanceState is only used when the Activity has been destroyed and needs to be recreated. In this case, it looks like your ListActivity has not been destroyed.
Are you clearing your ListView manually somewhere?