I'm developing custom launcher app containing a WebView. When I launch app as a normal app, it works without any issues, after calling startActivity()/ startActivityForResult() (Intent.ACTION_CALL, doesn't occur when using Intent for MediaStore.ACTION_IMAGE_CAPTURE) application calls onPause(), onStop() and other activity starts, after completing its task goes back to the application. However when I set application as launcher, it calls onDestroy() right after onPause() and onStop(). Such behaviour is not desired because I'm trying to keep inserted data in the launcher WebView app.
There are also many errors in logcat right after onDestroy() similar or same as E/libGLESv2﹕ HWUI Protection: wrong calling from app context F:ES3-glDeleteBuffers (when not using it as launcher such errors appear, but sporadically and in different situations).
AppManifest contains
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
I also tried using singleInstance, with that mode application works correctly after installation, but breaks its behaviour after restart.
Using minSdkVersion 19, tested on Android 4.4.2.
Ok, so here's how I managed to solve the issue, and actually what was the problem. I used
Settings.System.putInt(
getContentResolver(),
Settings.System.USER_ROTATION,
// toRotate //Or a different ROTATION_ constant
Surface.ROTATION_270
);
for screen rotation, because I needed to be sure the app was running in landscape mode. The thing is, camera supports landscape mode, but dialer doesn't. So when I launched camera it was without issues, but dialer caused portrait rotation and that caused WebView's default behaviour when rotating the screen, onDestroy() and that causes reload afterwards...
Let begin by changing it to something like this,
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Now lets understand what launchMode(s) are
via the documentation:
android:launchMode
An instruction on how the activity should be launched. There are four modes that work in conjunction with activity flags
(FLAG_ACTIVITY_ constants) in Intent objects to determine what should
happen when the activity is called upon to handle an intent. They are:
"standard" "singleTop" "singleTask" "singleInstance" The default
mode is "standard".*
"singleTask" Does not allow Multiple Instances The system creates the activity at the root of a new task and routes the intent
to it. However, if an instance of the activity already exists, the
system routes the intent to existing instance through a call to its
onNewIntent() method, rather than creating a new one.
"standard" Default. The system always creates a new instance of
the activity in the target task and routes the intent to it.
"singleTop" If an instance of the activity already exists at
the top of the target task, the system routes the intent to that
instance through a call to its onNewIntent() method, rather than
creating a new instance of the activity.
singleInstance" Same as "singleTask", except that the system doesn't launch any other activities into the task holding the
instance. The activity is always the single and only member of its
task.
For more in depth refer to the Documentation
Related
My application have two activities, say MainActivity and SecondActivity. Main activity is declared as android:launchMode="singleInstance" and its orientation is always portrait. Second activity is always has landscape orientation.
In some devices, everything is alright and in task manager there is only one instance of my app, but in some devices (like Samsung S7), when I launch SecondActivity, there will be two instances of my app in task manager like this image:
My guess is that something is wrong with the launchMode of the MainActivty but I need it to be singleInstance. Any suggestion?
EDIT:
MainActivity in manifest:
<activity
android:name=".Activities.MainActivity"
android:screenOrientation="portrait"
android:launchMode="singleInstance"
android:theme="#style/AppTheme"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
and second:
<activity
android:name=".Activities.SecondActivity"
android:screenOrientation="landscape" />
launching code:
Intent intent = new Intent(getActivity(),
intent.putExtra("VideoUri", filmGet.getOutput().getData().getFilmTrailer());
startActivity(intent);
If it helps, I launch SecondActivity from a fragment.
So, after reading #sharan comment and some googling, it made me to read some google documents. According to docs, there isn't any difference between android:launchMode=singleInstance and android:launchMode=singleTask but one. They both make your activity singleton and so you will never have two instances of it. The only difference between them is that singleInstance will prevent the task from attaching any other activity while singleTask has not this limitation. Any other things about them are the same.
So, for anyone who is reading this post, I'll recommend you to never use singleInstance launch mode unless you exactly need what it has. Because if you have only one activity in your app, then there will be no differences between singleInstance and singleTask. And if you have more than one activity, then I'll recommend you to all of your activities belong to one task.
In short, change singleInstance to singleTask and you are go.
I have some problems with proper task stack and bringing to front during deep-linking implementations... I have an Activity declared like this:
<activity
android:name=".activity.ArticleSingleActivity"
android:label="#string/app_name"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="site.com"
android:pathPrefix="/article" />
</intent-filter>
</activity>
It has launchMode="standard" and no parent, because this Activity can be launched from many places in app: native buttons or from WebView's. Even on bottom of almost every instance there are placed related articles, so it might call itself (new instance) and every article should appear in new "window"
And now: when my app is running in background and is visible in app manager deep-linking fires ArticleSingleActivity on top of other activities without a problem. Intent contains these flags:
FLAG_ACTIVITY_BROUGHT_TO_FRONT
FLAG_ACTIVITY_NEW_TASK
FLAG_RECEIVER_FOREGROUND
When app isn't in back stack (lets say removed by user) ArticleSingleActivity is launched without brought_to_front flag and everything starts like usual, but I've found small improper behaviour. When I navigate to another ArticleSingleActivity (e.g. from related on bottom or to any other Activity from side menu), then press home and try to open same deep-linked url then apps is just bring to front. It doesn't show linked article, but Activity which was on top when app was "homed". Also breakpoints aren't firing in onCreate, onActivityReenter or even onNewIntent. In my opinion system "remebers" that it have in back stack launched Acticity matching this url so it just brings to front whole app, but not proper Activity from the stack (no bring_to_front flag like above).
What can I do with this behaviour when I can't even set custom flags for this Intent delivered by system or do any Intent check in onCreate because it isn't called at all in any alive Activity on the stack?
I have two applications, call them app 1 and app 2, and am starting an activity in app 2 from app 1 for a result.
The activity in app 2 has an activity-alias applied to it so I can add an action (used to start it), and requires a signature-level permission (shared with app 1).
The code used to start the app 2 activity from app 1 is simple:
Intent startActivity = new Intent(MY_CUSTOM_ACTION);
startActivity.setPackage(APP2_PACKAGE);
startActivity.putExtra(EXTRA_SOME_PARAM, ...);
startActivityForResult(startActivity, 1234);
This results in the activity appearing as expected. I then get some user input, and clean up app 2's activity like so:
setResult(Activity.RESULT_OK, someData);
finish();
What I am expecting to happen, is for the calling activity in app 1 to be restored to the foreground and so onActivityResult to be called, but what is actually happening, is that app 2's main/launch activity is started instead.
Sometimes I get the onActivityResult callback with the right response code and data, but app 1's activity is still stopped.
My understanding of Android led me to believe that app 2's activity would have been part of app 1's task stack (and opening the task switcher bears this out), and so finishing it would just pop back to the caller.
I've tried:
enabling task reparenting on the app 2 activity
zeroing out the flags on the startActivity Intent
ensuring that app 2's activity doesn't try to start any other activities
tracing through the Activity lifecycle to see what's happening, but all trails go cold at NativeActivityManager...
clearing any existing tasks that app 2 has open
finishing app 2's activity with finishAffinity (doesn't work when returning a result)
None of this seems to have an effect. Many of the Intent flags and launch modes aren't allowed when starting for a result, so I'm at a loss for what to try next.
Thanks!
Here's the <activity> definitions from the manifest in case it matters:
<activity
android:name=".activity.App2Activity"
android:windowSoftInputMode="adjustResize"
android:configChanges="locale"
android:theme="#style/Some.Theme"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias
android:name=".activity.App2ActivityAlias"
android:permission="x.y.z.MY_CUSTOM_PERMISSION"
android:targetActivity=".activity.App2Activity"
android:exported="true">
<intent-filter>
<action android:name="x.y.z.MY_CUSTOM_ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity-alias>
In my application I have three activities:
<activity
android:name="com.example.myapp.SplashScreenActivity"
android:exported="true"
android:launchMode="singleInstance"
android:noHistory="true"
android:screenOrientation="sensorLandscape" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.myapp.MainActivity"
android:exported="false"
android:launchMode="singleInstance"
android:screenOrientation="sensorLandscape" >
</activity>
<activity
android:name="com.example.myapp.ListActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="sensorLandscape" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.myapp.MainActivity" />
</activity>
The first one is the LAUNCHER, SplashScreenActivity, which is a splash screen that disappears quite soon and it's not shown in recent activities, it starts MainActivity. In MainActivity users can select a category and ListActivity shows the items belonging to the given category. This is done with the following code:
Intent i = new Intent(getActivity(),ListActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
i.putExtra("category",mCategory);
startActivity(i);
In ListActivity the onResume method checks for the "category" extra and shows data accordingly. Since Activity launchMode is singleTop, I've also overridden the onNewIntent method to set the new Intent of the Activity.
This works properly if the app doesn't go in background: in this case, when I restart MainActivity and select a category, ListActivity resumes the old Activity showing data belonging to the previously chosen category.
How should I fix flags/launchMode in such way that my app doesn't resume ListActivity with the old data loaded?
You should not use the special launch modes. These are rarely required and only in very specific circumstances. Remove all the launchMode specifiers from your manifest. You also don't need to use these flags when launching ListActivity from MainActivity:
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
Your use of singleInstance launch mode is causing you all these problems.
Assuming you would ditch all the launchModes and Flags, then activity lifecycle gives you all the information needed to implement application as you have described - maybe there is somethings you have not added?
Once application goes to background, then ListActivity will be allowed to save its state, even if system will kill your process (lack of memory etc.), then still - launching back your app will first bring back ListActivity with previously saved instance state. So you will not have behaviour as you describe : when I restart MainActivity and select a category, ListActivity resumes the old Activity showing data belonging to the previously chosen category.
Still, if you need to keep to your current design, then you should somehow inform ListActivity of new data changes, question is if ListActivity.onNewIntent is being called at all in the case your describe? Have you tried adding Intent.FLAG_ACTIVITY_SINGLE_TOP : as per this blog entry: http://www.acnenomor.com/1094151p2/bug-onnewintent-not-called-for-singletop-activity-with-intentflagactivitynewtask ?
First, the use case : 2 phones have my app opened on the same screen. I want one user to be able to share the screen content (data) with the other one without necessarily opening a new instance of the activity when beaming using NFC.
(both Android devices are running Ice Cream Sandwich)
So I have a singleTop activity declared like this in the manifest.
<activity android:name=".activity.MyActivity" android:configChanges="orientation|keyboardHidden" android:launchMode="singleTop">
<intent-filter android:label="#string/activityLabel">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="a.b.c/x.y.z" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/x.y.z"/>
</intent-filter>
</activity>
When a VIEW action is fired and the activity is already on top, the onNewIntent() method is called in the same instance if the activity.
When a NDEF_DISCOVERED action is fired and the activity is already on top, the onCreate() method is called in a new instance of the activity.
You describe the case that the app is already open and the proper Activity is in the foreground. In that case, you can make use of the foreground-dispatching of NFC intents by calling NfcAdapter.enableForegroundDispatch() in your Activity's onResume() (and disableForegroudDispatch() in onPause()). This will force all NFC Intents to be delivered to your Activity (via onNewIntent()).
I don't have an answer for you. But I have a workaround: have the NDEF_DISCOVERED start a new activity. Make that activity invisible (Theme.NoBackground) and in the onCreate, make it start the MyActivity with singleTop and finish immediately. MyActivity should now appear with onNewIntent.
Have you looked at the Android Beam sample:
http://developer.android.com/resources/samples/AndroidBeamDemo/index.html
It implements this behavior that you want (minus VIEW intent filtering). I'm not sure if that intent will affect the NDEF_DISCOVERED one, but it would be interesting to maybe modify the Android Beam sample to see if you can cause the same behavior as your app.
You should use: android:launchMode="singleInstance"
Works for me.