I am wondering about the following.
In the Android documentation they recommend the following:
"Tip: When your configuration Activity first opens, set the Activity result to RESULT_CANCELED, along with EXTRA_APPWIDGET_ID, as shown in step 5 above. This way, if the user backs-out of the Activity before reaching the end, the App Widget host is notified that the configuration was cancelled and the App Widget will not be added."
https://developer.android.com/guide/topics/appwidgets/index.html#Configuring
But isn't that redundant, since the default value is RESULT_CANCELED (0) anways? Am I missing something? Can there be cases where the result is not at 0 when we open the configuration activity?
The important part of that statement is "along with EXTRA_APPWIDGET_ID". You're correct that the result code will be RESULT_CANCELED by default, but there isn't going to be a result Intent with the Widget ID attached by default.
Certainly, any launcher that allows Widgets should be able to handle it gracefully if that Intent is not set, but it's a known issue that at least some do not.
Related
I'm trying to apply single top to an activity that starts with a request code. I have tried both applying launchMode=singleTop to the activity in the Android Manifest, as well as setting the Intent flag for singleTop. However, if I launch the activity with a request code other than the default (-1), it will never call onNewIntent and will instantiate several activities on top of each other on the stack. Once I remove the request code and have it default to -1, however, it respects the launchMode I define.
I carefully read over the documentation, but was unable to find anything there or elsewhere online that might explain why android seems to ignore launch mode when an activity is started with a request code. Is there a work around for this? Why does it do this in the first place?
My application is launched using a tag, and based on the information contained in tag, it further proceeds. Now my app can also be started by using touching icon, and later it asks user to touch the tag. Small flow would be as below.
So MainActivity may contains tag data(if started from TagProcessorActivity), or may not contain data (if started from icon launch). Data is passed as intent extra value from TagProcessorActivity to IconLaunchActivity then to MainActivity. After main activity, app operation proceeds. When I leave the main activity, all my previous activities gets finish. I have checked onDestroy() is called for each activity. Now if I logout after MainActivity, (Logout simply a feature that closes all existing activity), and relaunch my application from recent app, my tag details still appears in MainActivity, which I dont know why.
To make is more clear my questions are:
1) Why activity which was destroyed still contains the information from previous launch.
2) I know about removeExtra() method, but is there some better options to tackle this problem.
3) and none the less, is there some thing wrong in my code or android is keeping that instance of intent extra?
PS: Not clear which piece of code to post, so if required feel free to ask for code.
Applications never exit in Android. onDestroy only destroys the activity, not any static variables left in the app. These will keep their value the next time an Activity is launched. This can be combined with some other features (like launching from the recent tasks menu causing you to launch the same intent) and this is the behavior you will get. The answer I always used was to detect this case (by checking the intent, there's a field that says if this is a restart or fresh), and ignoring the intent extras if so.
A finished task launched from Recents (as opposed to home screen launcher icon) will receive the old Intent, including its Extras, Data, etc. There is a way to know that it was launched from Recents so you can handle the Intent appropriately.
protected boolean wasLaunchedFromRecents() {
return (getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY;
}
In my humble opinion, that flag is poorly named (other flags referencing the Recents list actually use that word, e.g. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, FLAG_ACTIVITY_RETAIN_IN_RECENTS) and the documentation was never updated to reflect the fact that many popular Android devices have a dedicated button for Recents:
This flag is not normally set by application code, but set for you by the system if this activity is being launched from history (longpress home key).
I'm using an Intent Filter in my activity to retrive an url clicked on by the user.
In my activity onCreate method I have the following code
Intent intent = getIntent();
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
url = intent.getDataString();
showDialog(DIALOG_ID);
}
It works great except when I rotate my phone. Even if tha dialog was closed prior to the rotation, it reopen every time I change the phone orientation.
I can I avoir that.
For your information I don't want to lock the orientation
Another solution that doesn't require handling configuration changes yourself might be to simply check if the savedInstanceState Bundle parameter in onCreate is null before showing the dialog.
If you look at the docs for onCreate you can see that savedInstanceState will be non-null when the activity is recreated (due to configuration changes for example) and thus will be null when the activity is run fresh.
You would typically call setIntent(null) to remove the intent used to summon the activity. However, in practice, it doesn't always work. Apparently, a common workaround is to set the intent's action or data or both to null, depending on what you use in code. In your case, after showing the dialog, I would probably go with intent.setAction(null).
This is a pretty simple fix. In you manifest file, locate activity and add this:
android:configChanges="keyboardHidden|orientation"
This will prevent your logic in onCreate to fire again (or so I believe)
Quoted from here:
In some special cases, you may want to bypass restarting of your
activity based on one or more types of configuration changes. This is
done with the android:configChanges attribute in its manifest. For any
types of configuration changes you say that you handle there, you will
receive a call to your current activity's
onConfigurationChanged(Configuration) method instead of being
restarted. If a configuration change involves any that you do not
handle, however, the activity will still be restarted and
onConfigurationChanged(Configuration) will not be called.
I have never seen this issue until today. I did getIntent().setAction("");, and solved it. No more brain damage :)
I have implemented notification within service. Notification works fine. But in the case when app is running already and I clicked on android status bar notification it launches new copy of my app. which is obviously wrong. It should be if user click on status bar notification, should start app if app is not running already otherwise it should opens other activity (message activity in my case). I tried many suggestions provided in similar type of problem posted here but I didn't get solution in my case.
How about setting the launchMode to SingleInstance in the app's manifest.
There's also the trick I picked up on here about having a special NotificatonActivity that the notification calls:
Notification to restore a task rather than a specific activity?
Here i manage to escape out from this problem. First of all i defined Boolean variable in entry activity or class which extends application. i set attribute to that variable true when application runs and false when application exit. and i check that Boolean variable to which activity to open. in my case this solve the problem. may be it helps to your case too.bt scenario may be different. good luck
Using a boolean is not a good solution for this problem. For more info please look at the life time of the application. Instead you should change your launchMode to singleTask or singleInstance in your target activity and receive your intent from onNewIntent() method of that activity.
But be aware, if you call super.onNewIntent(intent); then a second instance of the activity will be created. So don't call that and do whatever you want with your intent.
Now that I have researched this even more I am rewriting this to make it clearer. If you are looking for more info, there is some available in older edits.
What is happening:
(This refers to an application that has not set any launchMode
settings and so is using the defaults)
You launch an app from the Market or from the Installer. This
launches the root/main activity of the application with the
FLAG_ACTIVITY_NEW_TASK flag and no categories. Right now the
applications stack is [ A ]
Then you proceed to the next activity in the application. Now the
stack in this task is [ A > B ]
Then you press the home key and then relaunch the same application
by pressing it's icon from either the home screen or the app tray.
What is expected at this point is that activity B will show, since
that is where you left off. However A is shown and the tasks stack is
[ A > B > A ] This second instance of A is launched with the
following flags: FLAG_ACTIVITY_NEW_TASK,
FLAG_ACTIVITY_RESET_IF_NEEDED, and FLAG_ACTIVITY_BROUGHT_TO_FRONT. It
also has the android.intent.category.LAUNCHER category.
At this point, if you hit the back key, it will return you to B, as it
was when you left it.
Looking at the documentation it seems as if
FLAG_ACTIVITY_BROUGHT_TO_FRONT should only be set for activities that
use the singleTask or singleTop launchModes. However, this
application has not set any launchModes and is therefore using the
default standard launchMode.
I assume this is not suppose to happen in this case?
I should also note, that once it gets into this weird state, then it happens everytime the app is launched from the home screen or app tray. If the task is finished (restarting the
phone, force stopping the app, or hitting back all the way through the
stack) will fix this issue and will no longer launch it incorrectly.
It only happens if you launch the app from the installer or market and
then try to launch it from the launcher.
So in summary, why is this happening? Is there a way to prevent it?
Here is a workaround I have come up with so far. Some other workarounds I have seen involved looking at the currently running tasks. However, I really did not want to have to ask for another permission (GET_TASKS) from the user just to make a work around.
Please let me know if you see any holes in this.
In the main/root activity's onCreate method, check if the intent has
the FLAG_ACTIVITY_BROUGHT_TO_FRONT set and if so, call finish(). This
then pops the extra instance of A off the stack [ A > B > A ] becomes
[ A > B ] and from the users perspective, it launches into the
activity they were expecting.
It seems to work in all of my tests so far. My only worry is that if
there is some weird case where someones launcher would always flag a
launch with FLAG_ACTIVITY_BROUGHT_TO_FRONT even if the app wasn't
already in a task, and therefore would completely lock them out
because it would call finish() and not have anything in the stack to
return to.
--
As requested in the comments here is how you can check if an intent a specific flag:
int flags = intent.getFlags();
boolean hasFlag = flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT == Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT;
--
Also I should note that I am still seeing this problem occur sometimes with this fix in place. It doesn't seem to be a perfect solution.
Overriding of onConfigurationChanged() should help you to retain the state.
http://developer.android.com/reference/android/app/Activity.html
Configuration Changes
If the configuration of the device (as defined by the Resources.Configuration class) changes, then anything displaying a user interface will need to update to match that configuration. Because Activity is the primary mechanism for interacting with the user, it includes special support for handling configuration changes.
Unless you specify otherwise, a configuration change (such as a change in screen orientation, language, input devices, etc) will cause your current activity to be destroyed, going through the normal activity lifecycle process of onPause(), onStop(), and onDestroy() as appropriate. If the activity had been in the foreground or visible to the user, once onDestroy() is called in that instance then a new instance of the activity will be created, with whatever savedInstanceState the previous instance had generated from onSaveInstanceState(Bundle).
This is done because any application resource, including layout files, can change based on any configuration value. Thus the only safe way to handle a configuration change is to re-retrieve all resources, including layouts, drawables, and strings. Because activities must already know how to save their state and re-create themselves from that state, this is a convenient way to have an activity restart itself with a new configuration.
In some special cases, you may want to bypass restarting of your activity based on one or more types of configuration changes. This is done with the android:configChanges attribute in its manifest. For any types of configuration changes you say that you handle there, you will receive a call to your current activity's onConfigurationChanged(Configuration) method instead of being restarted. If a configuration change involves any that you do not handle, however, the activity will still be restarted and onConfigurationChanged(Configuration) will not be called."