From the documentation regarding the android:configChanges='orientation' attribute of the activity tag in the manifest:
Note: Using this attribute should be avoided and used only as a last-resort. Please read Handling Runtime Changes for more information about how to properly handle a restart due to a configuration change.
Why does it say this?
In the case of threads and networking requests via a service API library, a request could be made with a reference to the original Activity, and then an orientation change could occur, leaving the thread pointing to the old Activity.
While this can be fixed, it's tedious and ugly compared to just handling the configuration changes yourself.
Why should it be avoided?
Edit: I guess I should also ask: would this be an acceptable reason for doing the orientation configuration changes yourself?
Why does it say this?
Because they want you to read the "Handling Runtime Changes" section in the docs. :-)
In the case of threads and networking
requests via a service API library, a
request could be made with a reference
to the original Activity, and then an
orientation change could occur,
leaving the thread pointing to the old
Activity.
For cases where you care about rotations, don't use implicit references to the Activity (e.g., regular inner class), but rather explicit ones (e.g., static inner class). Here is a brand-spankin'-new sample project that demonstrates what I mean.
While this can be fixed, it's tedious
and ugly compared to just handling the
configuration changes yourself.
The recommendation is there, I suspect, because they are afraid newcomers to Android will mess up "handling the configuration changes yourself". For example, they decide to have some different strings for landscape (where you have more horizontal room) and forget to reload them. Or, they decide to have some different images for landscape and forget to reload them. And so on.
Most activities in most apps aren't going to have background threads or sockets or whatever of their own, either because they just don't need them, or because something else is managing them (e.g., a Service). Their stock implementation of destroy-and-recreate typically "just works", particularly with the built-in support for saving the widget state of EditTexts and such.
In addition, you may not save that much by "handling it yourself", because you still need to implement onSaveInstanceState() anyway, to handle scenarios other than configuration changes (e.g., your activity is kicked out of RAM to free up space).
Now, is their phrasing a bit harsh? Probably. Seasoned Android developers can make their own decisions as to which rotation handling strategy to employ. I suspect their tone is to try to scare newcomers into thinking twice before going down this route.
I agree, in certain cases it seems like overkill to have to save state and redraw my views. I can understand if you want to configure a different layout when orientation changes but otherwise it is so much easier to add this to my activity.
#Override
public void onConfigurationChanged(Configuration newConfig) {
if(newConfig.equals(Configuration.ORIENTATION_LANDSCAPE)
|| newConfig.equals(Configuration.ORIENTATION_PORTRAIT)
|| newConfig.equals(Configuration.ORIENTATION_SQUARE)
|| newConfig.equals(Configuration.ORIENTATION_UNDEFINED)) {
} else {
super.onConfigurationChanged(newConfig);
}
}
Related
Is it bad practice to use Global variables instead of bundles to preserve state between screen rotations?
I really like just using global variables to keep state. But since everything suggests using bundles, should I do that? I don't know what pitfalls I could be thinking of.
Here is a snippet of code that demonstrates global variables dont change.
I have to check that the LEVEL_DEFS list is 0 before adding to it, otherwise every screen rotation adds duplicate levels (where I originally assumed I would have to re-add the levels after rotation)
`
val LEVEL_FILES = arrayOf("levels/lv1.txt", "levels/lv2.txt", "levels/lv3.txt")
val LEVEL_DEFS = mutableListOf<LevelDefinition>()
fun getAllLevels(context: Context): MutableList<Level> {
if (LEVEL_DEFS.size == 0) {
for (filename in LEVEL_FILES) {
LEVEL_DEFS.add(LevelDefinition(getTextFileString(context, filename)))
}
}
}
`
When I google screen rotation, all the results suggest that you have to use a bundle to save state.
I also asked chat gpt-3, which insisted that I must use a bundle to save the state during rotation.
Yet, it is clear to me that global variables do not change during rotation. I have used many simulated and real devices and they all behave the same way.
Your global variables will live as long as their scope is in memory, so it depends where you're storing them - if they're at the top level (e.g. defined in the top level of a file, or in an Application component) that state will exist until the process is destroyed.
If your state is stored in an Activity, then that Activity will be destroyed on rotation, so typically you'd use the Bundle passed into onSaveInstanceState to store any data you need, and then restore it when appropriate.
If your app is in the background, its process can be destroyed at any time by the system - in that case, you'd lose your global variable state. For an Activity, those save state lifecycle methods would be called, so you'd still store in the Bundle and receive that Bundle when the app is opened again. The process will have been destroyed, but if you're set up to store and restore that state, you'll handle it just like with a rotation. (This is a different situation to the user actually closing the app, which considers the next time it's opened to be a "fresh start" and doesn't provide any stored state.)
You can also use View Models to store state, which survive rotation but require using SavedStateHandle to behave like the Bundle and survive process death (but again, not actual restarts). More info about your options for storing this transient state here.
So what this means is it depends exactly what you're storing, how long it needs to stick around, and if it's allowed to be destroyed when the app is in the background. That last one is usually a "no" which makes global variables a bad fit in most cases.
You'll need some way to persist that data (e.g. SharedPreferences) and at that point you may as well use that as your state, rather than storing it there and in a variable. Up to you though! Persisting data is also how you'd handle state surviving fresh start app restarts too, e.g. storing a high score that shouldn't disappear when the user closes the app.
(Also I'm not sure what asking a chatbot for advice is worth, no matter how much it "insists"! But that's a whole other topic)
Global variables persist as long as your application is running - however when it is in the background you have no guarantee that the Android OS won't stop and re-start your application (which would lose any global or static variables). Once your application goes to the background, you can no longer be certain they will stick around.
What you have posted looks like it would be fine though if you only access it through the getAllLevels method (since it re-creates it when needed). However, if you are adding levels somewhere else, those could be lost.
If you want to be able to add levels, one possible solution would be to load the data from SharedPreferences inside getAllLevels when the list is empty - and update the SharedPreferences any time levels are added.
I have heard things about how it is bad to use setContentView()
Pattern "One activity, multiple views": Advantages and disadvantages
However I was wondering, would it be unlikely that my application will cause memory leaks, if I use setContentView() once in the onResume() method of my activity?
Whenever the user opens my app, it checks to see if something has been enabled in settings. If it has been enabled then the app uses a different screen compared to the original screen.
Therefor my code looks like this:
#Override
protected void onResume() {
super.onResume();
InputMethodManager im = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
String list = im.getEnabledInputMethodList().toString();
if(Stuff is true){
setContentView(R.layout.activityscreen_enabled);
}
}
}
Would using setContentView() be unlikely to cause memory leaks and other such problems? Or is there a better solution?
I'm doing Android since few years now and I have never done that because I like to stick to the pattern which is almost always having the setContentView in the onCreate.
However, I do not believe that you would have big troubles doing that (for the memory leaks I mean).
Nevertheless, I do not see the point of doing such a thing, the pattern of the Activity (or how I understood it) is more:
I create a view in the onCreate and I update its data in the onResume and if the data are A then add/remove this view and if the data are B add/remove this other view.
To be complete, I read your (really good) link and I think you maybe misunderstood how you can apply what Commonsware is saying: you can have multiply views without having different setContentView: your view structure needs, in this case, to be really modular and you will be able to load all the subviews dynamically (or, at least, it's how my colleague and I are doing ;) ).
For your example, I would have an empty layout for the base of the activity (let's say a blue background) and then for every view I want to have (every case), I would have a dynamic layout that I load at some point in the life cycle (probably at onResume). I do not believe that what you're doing is particularly bad but I doubt that it was thought like this ^^
This link agrees with me
If you need multiple screens use a Fragment or even create a new Activity inside of messing around with the view for some reasons
It's not good to have single Activity for the whole app or it will be so long and complicated.
Your onResume() would need to handle the new views and their ids, onClickListeners... etc.
onResume() is called many times unlike onCreate() so it would be a waste of time and memory to load the views over and over.
According to android doc in activity life cycle about onPause() and onResume()
Because this state can transition often, the code in these two methods should be fairly lightweight in order to avoid slow transitions that make the user wait.
In my app, the activity starts a thread which download images from server. There are more than 30 images. While downloading, the user can switch to another activity. So I want that the thread should stop doing whatever it is doing. I have used mThread.interrupt() method in onDestroy(), but it did not worked. How to do that?
I can suggest simpler&safer approach to handle this
Use common value across your app;
SharedPreferences or inside Application context
Key=IsStopDownload
Value= True / False
part.1)
in Application context (MyApplication)
private boolean isAppActive = false;
public void setAppState(Context context, boolean state) {
isAppActive = state;
// note: you can also store this in SharedPreferences
}
public boolean getAppState(Context context) {
return isAppActive;
// note: you can also load this from SharedPreferences
}
part.2)
Activity onResume and onPause are the guaranteed places to identify state of your activity.
onResume -> activity is active
onPause -> activity is not active
part.3)
let your thread check the activity state and if not active, it can stop itself, thats safer then making external interrupt. threads can act weird when interrupted from outside, it is safer to break the loop from inside.
e.g.
((MyApplication)context.getApplicationContext()).getAppState(context);
if thats false, thread stops itself
hope this helps...
---
Social Coding #AspiroTV
When user will switch the activity , this one will not destroyed but paused so trying your code is onPause() might work .
First of all, you're probably much better using an AsyncTask than a Thread, so, personally, I wouldn't use a Thread at all. In my humble opinion and small experience, in that situation where you're using them, they grow and grow until you have spaguetti code.
And second, as Dr. Nik said, this task is typically better served using a Service. It's is, in my opinion, the best and safer thing you can do.
I would point out several reasons why you should use one:
The service does not need to stop because the activity goes away.
Services are very easy and quick to implement. And the notification code for completion is also easy.
You are downloading images, and it's always better to do the job at once if you can, to save bandwidth and connection time for the user and cache images locally. This used to be worse because today cell phones are full fledged computers. But it's always important to code for efficiency. Therefore, IMHO, you should keep the need for a connection as small/quick as possible.
Finally, even using AsyncTasks inside an activity demands a tricky (simple, but still tricky) code to catch and detach the task when the Activity is going away, and a check for nulls when it's coming back. Since you're downloading images, and that can take a time and it's very possible that the user may demand an orientation change (turn the device to landscape), you will need that. Search stackoverflow for "orientation change asynctask" for examples.
There are probably other reasons, too, but those are on the top of my head right now. And of course, it's my opinion.
I have been going gaga to figure this out.
Although I have read a lot that on Orientation Change, Android kills an activity and starts it as a fresh one, and the only way to handle this is to save all the stuff inside onSaveInstanceState() and try to restore it inside onCreate().
But my activity does a lot and different kind of network activities at different times and if the orientation is changed when the network activity is being performed, I'll have to handle a lot of different and complex scenarios.
Is there any simple way to just point Android that this activity doesn't need to be redrawn at all when the orientation is changed so that it automatically saves all the data and re-uses it?
I wonder if there's any thing like that.
Yes, you can add attribute android:configChanges="orientation" to the activity declaration in the AndroidManifest.xml file.
EDIT:
The purpose of the android:configChanges attribute is to prevent an activity from being recreated when it's really necessary. For example the Camera application uses this attribute because it the camera preview screen mustn't be recreated when an orientation change happens. Users expect the camera preview to work without any delays when they rotate their devices and camera initialization is not a very fast process. So it's kind of a native behavior for the Camera application to handle orientation changes manually.
For most applications it doesn't really matter if an activity is recreated or not during orientation changes. But sometimes it's more convenient to persist an activity during this process because of slow activity creation, asynchronous tasks performed by an activity or some other reasons. In this case it's possible to tweak an application a little and to use the android:configChanges="orientation" attribute. But what is really important to understand when you use this tweak is that you MUST implement methods for saving and restoring a state properly!
So to sum up this answer, the android:configChanges can allow you to improve the performance of an application or to make it behave "natively" in some rare cases but it doesn't reduce the amount of code you have to write.
But my activity does a lot and different kind of network activities at different times and if the orientation is changed when the network activity is being performed, I'll have to handle a lot of different and complex scenarios.
Then move that logic out of the activity and into a service.
Yes, you can add attribute
android:configChanges="orientation" to
the activity declaration in the
AndroidManifest.xml file.
IMHO, it's better to declare
android:configChanges="orientation|keyboard|keyboardHidden"
About the blog post you gave the link in another answers. I guess here is the answer:
If your application doesn't need to
update resources during a specific
configuration change and you have a
performance limitation that requires
you to avoid the Activity restart,
then you can declare that your
Activity handles the configuration
change itself, which prevents the
system from restarting your Activity.
I spoke as well with an android developer about this problem. And he meant following. If you don't have different layouts for landscape and portrait orientation, you can easy use configChanges.
I solved my problem by adding this to my activity in my manifest file
android:configChanges="keyboardHidden|orientation|screenSize"
Just answered this question earlier: Android - screen orientation reloads activity
In your case you want to completely prevent Android from killing your Activity. You'll need to update your manifest to catch the orientation change, then implement the orientation change callback to actually do whatever you need to do (which may be nothing) when an orientation change occurs.
if you are doing a lot of networking inside Asynchronous task maybe you should use onRetainNonConfigurationInstance()
ans then get the data back in your onCreate() method like this tutorial or this
add android:configChanges="orientation" to your activity in manifest and add this code in your activity class and check..i hope it will help for you.
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main);
}
this method will be called when orientation is changed nothing else if u don't want to change anything let it be blank
android:screenOrientation="portrait" in the activity tag in the manifest will lock your orientation.
Check this link for more inforation.
I'm developing an Activity that does some of its own state management. I'm trying to differentiate the following onResume cases:
New launch
task switch (home button long-click)
resume after other activity in the same application
wake-up after sleep
orientation change
Is there something in the Activity's intent, or elsewhere, that can help me differentiate these?
For the curious and some context... I'd like to preserve my internal history stack on 4 & 5. On cases 2 & 3, I would preserve the same current page, but erase the history (allow the normal back button functionality to take over at that point). Case 1 would initialize to the activity's internal start page (and can be detected easily enough with some help from onCreate).
Is there something in the Activity's intent, or elsewhere, that can help me differentiate these?
Item #4 has nothing to do with onResume(), AFAIK.
Item #5 would be better handled via android:configChanges and onConfigurationChange() though you could "detect" it by returning something from onRetainNonConfigurationInstance() and seeing if it is there in onResume() via getLastNonConfigurationInstance().
The others aren't just three cases, but probably twice that, once you start taking into account things like "being kicked out of memory to free up RAM" as a possibility.
Off the cuff, it feels like you made some unfortunate architectural decisions ("internal history stack...erase the history...allow the normal back button functionality to take over at that point"). Android is designed around lots of cheap activities, and you appear to be violating that precept. You are welcome to do so, but bear in mind that Android support for your chosen pattern may be limited.