Is there any best practices when changing activities in Android?
It seems very odd to me, just make an Intent and start another activity over and over.
If I just start another activity and finish the last one, when I have to move back, I need to load all the stuff back. But when I don't do it, does not seem to be the right thing to do in terms of memory handling.
Is it correct to save all the activities in a kind of ActivitiesPool or something like this? Or I will always have to choose between keeping them opened or reload them?
Any directions?
Thanks
It seems very odd to me, just make an Intent and start another activity over and ove
that's how it works on Android. To be more precise we (as developers) are not even allowed by the System to call new Activity() you just startActivity(intent);
I need to load all the stuff back
again, the framework is taking care of loading/unloading resources as needed
Is it correct to save all the activities in a kind of ActivitiesPool
no, it's not correct, don't touch them.
If you want the user to be able to click the back button and go to the previous activity, DO NOT call finish(); on it. You can use onPause()/onResume() callbacks to handle background operations and UI status should be saved on #Override protected void onSaveInstanceState(Bundle outState) callback. You put all the UI state that you need in there. If the system needs memory it will destroy the activity and whenever the user is going back to it, it will create it again onCreate(Bundle savedInstances) and then you'll have all the UI state saved there for you to proper re-create the state where the user was before.
That's how it works.
Related
I have 2 activities. Main Activity A & Activity B
I do not want Activity A to destroy. I am starting Activity B in a new task.
public static void startActivity(Class<?> startClass) {
Intent intent = new Intent(Constants.getActivity(), startClass);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Constants.getActivity().startActivity(intent);
}`
Constants.getActivity() returns the Context on current activity
startClass is the either activity "A" or activity "B"
Thing is they create/destroy the activities and they leak. Am I doing it wrong? How can I start activity "B" from activity "A" and vice versa keep them both in background when I dont need them.
First of all, what are you trying to do? You should always separate things you want to do in the background from your UI. Think of your Activities are simply a container to show the UI, everything else can be stored and restored from persistent storage or savedinstance bundles.
It is very crucial that you understand the difference between Activity lifecycle and Service lifecycle here.
I'm going to refer to my answer from another question here:
Your app will be in the background, onResume and onPause will both be called, provided that the OS have enough memory to keep the new app and all the old apps.
If you have a long running process that you need while the user not looking at it, use a service.
If you need the user to return the app in the same state, you need to do the work in onResume and onPause to save the state information and initialize your UI again when the user comes back. If you are really worried that it can get killed (well, it shouldn't lose the bundle I think), you can store them in SharePreferences for your app.
If you want to know when the app returns from that specific share intent, use startActivityForResult
You cannot keep an activity "alive" as you said. When an activity is paused, Android system is always able to claim its memory and unload it, at any time.
But if you want to switch from A to B, then maybe A and B can be different views inside a single activity. Maybe you'll want to have a look at http://developer.android.com/reference/android/widget/ViewFlipper.html
When you use tasks, cleanup is very important. You need to cleanup all tasks in the activity. readthis
If the activity does not have a lot of crazy initialization, just use finish and onCreates. Else be aware that onResume will be called often as you switch between activity's. Cleanup will be crucial here. If you dont cleanup, its possible one of your activities (with dead object reference from the other cleaned up activity) may come back up from the activity stack and throw exceptions. Its very difficult to debug this kinda exception.
I have an Activity that should only get created once. That is, onCreate can only be called once. If it's called again, I want the Activity to do nothing.
Is it advisable to do the following?
protected void onCreate(Bundle savedInstanceState) {
this.setTheme(android.R.style.Theme_Translucent_NoTitleBar_Fullscreen);
super.onCreate(savedInstanceState);
if(onCreateWasCalledAlreadyBoolean) {
setResult(RESULT_OK);
finish();
return;
}
//Do other stuff here
}
I assume you understand how the activity life cycle works. I mean, you are not trying to avoid something that does not apply here (thinking that onCreate may be called multiple times whenever it just onRestarts etc.).
Technically, it's perfectly fine.
However, you should be worrying more about why you need to call your activity ("A") again if it shouldn't be created at all, if that's what you're thinking.
If you've caught yourself checking if your activity A was already "called" (?), this could mean the previous activity ("B") has a mistake in the logic flow of the app, and that B instead should be checking if it must in fact start that activity A. I mean, if you need to decide if you must call an activity, check before starting it.
I don't think that's applicable if you're restarting the activity (e.g.: go Home, then navigate back), but then again you should be restarting it from where you left (B for what I can tell). You won't be navigating back to A. And you didn't give much detail, so I'd guess this is some kind of splash screen, like evilmage93 said.
If that's indeed some kind of splash screen, I would advise to show it whenever the user navigates back all the way to remove your app from the task stack (contrary to his advice). In other words, whenever the user restarts the app from its "front door".
Although that's ultimately a design decision, I prefer to see the splash screen whenever the app is being loaded ("entered") in the stack for the first time, and it should work fine if you (obviously) finish A before calling B (the splash screen is supposed to finish itself when done, even in its first run). It's a matter of consistency: the same app should behave the same way whenever the user performs the same task (start app from its "front door").
Still, I answered your question covering some general aspects because you asked in such way.
// edited:
Finally, by looking at that onCreateWasCalledAlreadyBoolean I'm afraid you may be trying to reinvent part of the activity life cycle mechanism. In this case, don't: proceed with your regular activity logic because the user expects that behavior. Generally I wouldn't advise people to break the normal loading of an activity just because it was killed and restarted by the system.
I don't see why not. Wouldn't it be simpler to not restart the activity at all though?
What are you worried about NOT being okay? Performance..Uncaught exceptions..Code clarity?
I've got four activities which the application cycles through. The first one fetches huge amount of data, so I don't wish to do that more than once. Thing is that if user presses back key on last activity, I want to return to the first one without reloading the activity. I'm currently thinking startActivityForResult methods and finish the two previous ones, but there may be a better solution?
Scenario:
Hasslarn,
The first thing that you must understand is that you have very little choice as to whether an Activity will be reloaded or not. That is determined by the system (largely). With that said, there are a number of things you can do to limit the system's desire to kill the Activity. Additionally, you may use a number of tools at your disposal to limit the impact of such a possible closure
Finish every child Activity as it becomes unimportant. This will free resources lowering the need to get rid of unused Activities (even temporarily).
Find a simple, but clever way to limit loads.
Based on the information provided, your proposed solution is a viable way to accomplish both. However, I would ask "Is Activity B required to be active while Activity C is open?" If not, you may want to do the following:
startActivityForResult(Activity B)
When Activity B is done, send result back to Activity A and startActivityForResult(Activity C)
Finally, when Activity C is done, you may startActivityForResult(Activity D) and back will work with no effort and you won't have to close the other activities.
Furthermore, if you press Back on Activity C, you can supply a cancelled result to Activity A forcing a restart of Activity B if you so desire. Depending on the required processing for Activity B, this may not be any hassle at all for your app.
Regardless of how you approach this, I recommend finding a way to cache this data such that onResume(), you may reload quickly if needed. This is because there is NO way to ensure that your original Activity will not be released to make way for the others.
Hope this helps,
FuzzicalLogic
Try using this:
Intent a = new Intent(this,A.class);
a.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(a);
return true;
Activity A will get reordered to front, without creating a new instance. If you want to pass extras through intent , you can get the intent extras in onNewIntent(intent) in Activity A.
At the moment I'm a little bit confused about the lifecycle management in Android. There are at least 4 possibilities to resume retained data after an Activity comes back to the foreground:
Android handling: If there is enough memory, Android stores and resumes the important data (checked radio buttons, text of EditText,-... and so on) after Activity restart, the user has the same state as before as the Activity went into background.
onPause, onResume: Overriding onPause and save the important data to a database or text file and resume it when onResume is executed next time.
onSavedInstance(Bundle), onRestoreInstance(Bundle): I can save the data as key-value-pair into bundles and restore them after onRestoreInstance is executed.
onRetainNonConfigurationInstance(), getLastNonConfigurationInstance(): I handle all my storage issues in one big object and read getLastNonConfigurationInstance() out when onCreate is executed.
Although it is confusing which approach is best, I guess it relies on development experience to know when to use which possibility. If you have some good examples for each I would be glad, but this is not my question. I wonder how to deal with all that when I have different Activities and one Activity will be killed by Android when it pauses in background:
In my case I have a MainActivity and a MessageActivity. The MessageActivity consists of a ViewSwitcher which consists of two states. State one is a radio button choice list. State two is an EditText with two buttons (send and abort). When I monkey test each state, hit the Android home button, and restart the application, the right Activity with the right state and the old data comes into foreground, when I leave the handling to Android. So that works.
But what happens when Android destroys the MessageActivity in background:
If I use the Android way, the data is lost and I guess MainActivity (instead of MessageActivity->state(1 or 2)) will start next time after I relaunch the application (is that correct?). So when I'd like to keep the data of MessageActivity, I have to use one of the other three possibilities.
How to do that neatly, when the application entry point (so the MainActivity) differs from the last active Activity. The problem is that I have to resume a special Activity with a special state of ViewSwitcher. I could start MessageActivity out of MainActivity with startActivity(Intent) in onStart() or onResume() method (because MainActivity is probably the entry point) but then I run into a lot of logical problems in Lifecycle management. Due to this fact I don't think that this is the right way to do that.
But, what's the right and best way to do that?
I guess MainActivity (instead of MessageActivity->state(1 or 2)) will start next time after I relaunch the application (is that correct?)
No, I don't believe this is correct, depending on what your code does in onCreate(). It certainly doesn't need to be correct if you go about things the right way. A simple way to test this is to rotate your screen, which recreates the running activities, unless you have overridden the default configuration change behaviour.
I recommend reading this section in the android docs carefully:
http://developer.android.com/guide/topics/fundamentals/activities.html#SavingActivityState
In particular:
even if you do nothing and do not implement onSaveInstanceState(), some of the activity state is restored by the Activity class's default implementation of onSaveInstanceState(). Specifically, the default implementation calls onSaveInstanceState() for every View in the layout, which allows each view to provide information about itself that should be saved. Almost every widget in the Android framework implements this method as appropriate, such that any visible changes to the UI are automatically saved and restored when your activity is recreated. For example, the EditText widget saves any text entered by the user and the CheckBox widget saves whether it's checked or not. The only work required by you is to provide a unique ID (with the android:id attribute) for each widget you want to save its state. If a widget does not have an ID, then it cannot save its state.
What this means is, that so long as you don't force any UI state in any onCreate() calls, your activity stack and UI state will be restored.
Personally, my preferred approach is to keep as little state as possible in member variables of my activities, saving and restoring it with onSave/RestoreInstanceState(), and relying on the default implementations to save the rest of the UI state (text box contents, etc). Data that should persist between sessions I commit straight to my DB or preferences as soon as it's changed (e.g. in the on-click handler). This means I don't need to worry about the activity lifecycle for that. As much as possible, my UI just presents a view of the data in my DB (using CursorAdapter etc.).
Edit:
Regarding restoration of the whole activity stack:
When the user leaves a task by pressing the HOME key, ... The system retains the state of every activity in the task. If the user later resumes the task by selecting the launcher icon that began the task, the task comes to the foreground and resumes the activity at the top of the stack.
(See http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html)
It's not my attempt for a best answer, but it's too long to get in the comments section.
First I will suggest not to rely on the "Android way" - this will result in inconsistent application behavior depending on the free memory of the device - bad practice.
My suggestion is to save your state-dependent data in key-value pairs in SharedPreferences, every time you go into onPause() in your MessageActivity. Store a flag in SharedPreferences, which indicates which was the Activity that was last opened (if you only have two Activities you can easily go 0/1 or true/false flags).
When you re-launch your application, it's normal to start the Activity marked in your AndroidManifest.xml as "entry point". So naturally you'll check the flag in onResume() in your MainActivity and start the other Activity if needed. In MessageActivity's onResume() check the values in SharedPreferences and fill in what's necessary...
If your application is "resumed" to the last Activity in the ActivityStack this will call onResume() in the last Activity in the ActivityStack.
The way I have handled an issue like this in the past, is to have a service running in the background, which handles the flow of information from different activities via either Intents and listeners (preferable, since they are the most easily decoupled solution), or if you are extremely careful, and the only viable solution for some reason is to store the data through direct property access or method calls, you can use static properties/methods on the service class as well. However, I would strongly recommend using the Intent/listener method as it is generally more flexible, thread safe, and decoupled. Additionally, it is wise to make sure that not much is happening at any point in time (in other words, only use this service for Intent handling) when it's not needed, otherwise the Service will tend to hog CPU time as well as RAM, when it's not really needed.
Some resources to look at when it comes to this approach would be IntentService and its related classes, including the superclass, Service. IntentService, however, it is worth noting handles a few more things about async Intent processing, etc that Service does not automatically come with.Hope this helps you!
login.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String name=username.getText().toString();
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = settings.edit();
editor.putString("username", name);
if(name.equals("xxx")) {
Intent intent=new Intent(currentactivity.this,nextactivity.class);
intent.putExtras(bundle);
startActivityForResult(intent,0);
}
}
});
I have app that connects to internet to get data. I can access data multi-level.
So let say I start at level 3 and on level 4 I decide to go back, whenever I press back the previous activity reloads the data from the internet.
Is there any possibility to prevent that?
I have tried to run the activity in single-top mode.
Move the data loading code to the single-exec event: onStart or
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null) {
// here
}
}
You have a lot of options here.
The simpler one is to avoid stopping your activity when you switch to another one. When you go to level 4 by means of startActivity(), your onPause() is called on the level 3 activity but it continues living in the background. Then when you come back from 3, onResume() is called.
So as long as you don't load data on resume nor finish() your level 3 activity, you should be fine. If the system happens to be very short on resources then level 3 activity might be killed (though this is very rare) and restarted when you get back to it, but if it happens it means the system needed memory for something important and it's probably fine to reload.
Now, there are other ways. It's usually much better to do it as I described above, but if for some reason you want to finish your level 3 activity, Here are your options.
As it has been noted, you may elect to dump your data somewhere. The saved instance state is an option - though if it's heavy data, more than a few kilobytes, it's not recommended. The idea is, you save your data in onSaveInstanceState() in the Bundle and restore it in onCreate().
If it's heavy data, you would be better off dumping it in a cache file.
If you have a data model, and want to use the same data across several activities, maybe a widget and possibly even a different app, you may want to consider building a ContentProvider to supply the data. It would live independently of the other parts of your application and manage access to the data. Other parts would query it for the data they would need.
The neat thing about that is, it abstracts the data away from the rest of the program. It can be accessed from anywhere and caching policies and everything is handled in a dedicated place. The drawback is, it's significantly more complicated.
One possible solution would be to work with states. You basically have a boolean which indicates if your activity has performed a certain action.
If the action was performed you it won't reach that code again. Of course that flag must be saved somewhere in the Application context or in SharedPreferences.
You can add this parameter to your intent to prevent reloading.
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(new Intent(this,My.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));