Every intent starts a new task in Android App - how to prevent? - android

In my application I have several "intents" that I use to transition between different activities in my application. I have noticed a strange behavior that occurs on Samsung devices - but not on Nexus devices - whenever a new intent is created the application launches a second "task" for this new activity! When the user goes to the multi-tasking menu they can see multiple copies of the application! This is not the desired behavior. Any and all advice would be greatly appreciated!
Manifest:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme"
android:launchMode="singleInstance">
<activity
android:name=".MainActivity"
android:screenOrientation="portrait"
android:launchMode="singleInstance">
</activity>
<activity
android:name=".Settings_area"
android:screenOrientation="portrait" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyDieXTCaFoIL0kJ_IM4UMBSQL3sNn92AWM" />
<activity
android:name=".MapsActivity"
android:label="#string/title_activity_maps" />
<activity android:name=".Splash"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".aboutPageActivity" />
<activity android:name=".turnOffFromNotification"
android:noHistory="true"></activity>
</application>
I have already attempted removing the launch modes as well as changing the application launch mode to singleTop and standard.
Intent that creates a second instance:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
/* Create an Intent that will start the Menu-Activity. */
Intent mainIntent = new Intent(Splash.this,MainActivity.class);
Splash.this.startActivity(mainIntent);
Splash.this.finish();
}
}, splashDisplayLength);
return;
}
Intent that creates a third instance:
public void goToAboutPage()
{
Intent goToAboutPage = new Intent(this, aboutPageActivity.class); //create the intent to go to the map screen
startActivity(goToAboutPage); //actually go to the map screen
}
A third instance can also be created from launching a settings intent:
public void changeToSettingsScreen() //changes the screen to the setting screen
{
readyToSendPackets = false;
sendSwitch.setChecked(false);
// textView.setText("NOT sending"); //set the textview to advise users packets are not being sent
Intent goToSettings = new Intent(this, Settings_area.class);
startActivity(goToSettings);
}
I also over rode the onNewIntent Method:
protected void onNewIntent(Intent intent) {
// super.onNewIntent(intent); //REMOVED THIS TO AVOID DOUBLE INSTANTIATION ON TOUCHWIZ IF ANYTHING BREAKS LOOK HERE FIRST
setIntent(intent); //this allows us to recieve the extras bundled with the intent
// System.out.println("Here is the bindle: " + getIntent().getExtras());
if (getIntent().getExtras() != null) //check to see if there are any extras, there wont be on apps first start
{
Bundle extras = getIntent().getExtras(); //get the extras
String methodName = extras.getString("methodName"); //assign the extras to local variables
if(methodName != null && methodName.equals("turn_send_switch_off"))
{
sendSwitch.setChecked(false);
}
//else if(**other actions that may need to be performed can go here**)
}
Thank you very much for any help!!!

Usually if you have to force a single instance of the app, you should avoid putting android:launchMode="singleInstance" on each activity seeing as it would try to launch an instance for each activity.
Removing the launchMode from everything except the application should ensure that only the application runs in a single instance, although what #Shaishav said is true, most of the time you can let android deal with the lifecycle of an application by not setting the launchMode, unless you have a real need to ensure only one instance is running at a time.

Related

Android intent not being sent

I'm using firebase auth in my app. I'm checking if the user is authenticated on my MainActivity just as explained on the Firebase docs and the Udacity course.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebaseAuth = FirebaseAuth.getInstance();
final FirebaseUser user = firebaseAuth.getCurrentUser();
if (user == null){
loadLogIn();
}
authListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
if (user == null){
loadLogIn();
}
}
};
private void loadLogIn(){
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
//Flags prevent user from returning to MainActivity when pressing back button
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
When I step through the code with the debugger, everything works well, the user is null so the loadLogIn method is called, which sends the intent, but Login Activity never opens, and I get a crash because it keeps executing code that needs the user to be authenticated. The onCreate method if my login activity is never called. I cannot understand why the intent is not executing/ what is stopping it. This had been tested and worked perfectly previously.
When the intent line is executed I get the following output on the console :
I/Timeline: Timeline: Activity_launch_request id:com.example.tobias.run time:2369840341
This is my manifest if it helps :
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:name=".app.RunApplication"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".app.MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".editor.EditorActivity"
android:parentActivityName=".app.MainActivity" />
<activity
android:name=".login.LoginActivity"
android:screenOrientation="portrait"
android:theme="#style/LoginTheme" />
<activity
android:name=".settings.SettingsActivity"
android:parentActivityName=".app.MainActivity" />
<activity
android:name=".login.NewAccountActivity"
android:screenOrientation="portrait"
android:theme="#style/LoginTheme" />
<activity android:name=".login.ForgotPasswordActivity"
android:screenOrientation="portrait"
android:theme="#style/LoginTheme"/>
</application>
This issue first appeared when I tried to sign out from my app calling
FirebaseAuth.getInstance().signOut();
the user signed out, but the intent was not sent, which left the app in an invalid state, and then it crashed seconds after trying to execute code that required a user. It was not the first time I had signed out of the app, I have done it multiple times and it worked.
This is a weird issue, I hope someone can help me find the mistake. Thanks in advance.
Problem:
//...
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
//...
Solution:
//...
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
//...
When you use setFlags you are replacing the old flags... when you use addFlags you are appending new flags. Remember, a flag is just a integer which is power of two... in binary, flags look like this: 1, 10, 100, 1000, etc... (which in this case are 1, 2, 4, 8). So, what addFlags does is appending the integer you pass using the | operator.

Multiple Instances of Activity

We have already the check of CATEGORY_MAIN and !isTaskRoot() but even then 2 instances of activity are launched.
SplashActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("OnCreate method.");
if(checkIfActivityIsBroughtToFront() || checkIfActivityIsRootTask()) {
return; // Found that if we finish and don't return then it will run the code below, hence start the recovery task.
}
Log.i("Checking if Recovery is required ...");
new RecoveryTask(SplashActivity.this, this).execute();
}
private boolean checkIfActivityIsBroughtToFront() {
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
// Activity was brought to front and not created,
// Thus finishing this will get us to the last viewed activity
Log.i("Detecting a brought to front, no need for recovery.");
finish();
return true;
}
return false;
}
private boolean checkIfActivityIsRootTask() {
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
finish();
return true;
}
}
return false;
}
Logs
2015-10-22 13:42:25.581 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.587 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.637 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.638 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.828 +0300 GeoFenceManager INFO[pool-5-thread-1] - Removing geofences ...
2015-10-22 13:42:25.872 +0300 GeoFenceManager INFO[pool-5-thread-2] - Removing geofences ...
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="x.y.z"
android:installLocation="internalOnly" >
<application
android:name=".global.GlobalInstance"
android:allowBackup="true"
android:allowClearUserData="true"
android:hardwareAccelerated="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:largeHeap="true"
android:persistent="true" >
<activity
android:name=".activity.SplashActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:theme="#style/Theme.Background" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".receiver.BootUpReceiver"
android:enabled="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
UPDATE:
This is happening after restart, the BOOT_COMPLETED listener is following
public class BootUpReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, SplashActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
Any idea how this can be prevented?
Add android:launchMode="SingleTask" to the splash activity element in the xml. And then on leaving the activity i.e. navigating away from the splash call finish(). The reason you should use this pattern as opposed to "SingleInstance" is that the user can never navigate back to the splash with the back-key (as this is not normal behavior).
private boolean checkIfActivityIsRootTask() {
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
finish();
return true;
}
}
return false;
} ?
if this activity is roottask,it return false;
what's RecoveryTask for?
You said:
We have already the check of CATEGORY_MAIN and !isTaskRoot()
But both the check failed. In your logs that you attached, when both instances are created, neither Detecting a brought to front, no need for recovery. nor Main Activity is not the root. " + "Finishing Main Activity instead of launching. is printed in the logs. This means that in both the methods the if part is never encountered and hence finish() never gets executed. Both the methods returned false.
Also, just because you call finish() and return in onCreate(), it doesn't mean that onStart() or onResume() won't be called.
Why does this happen? Launcher activity will never be called twice (considering that you don't internally start the activity). Probably a bug in the some SDK that the user has.
POSSIBLE FIX:
You could try setting android:launchMode="singleTop" in your manifest for the SplashActivity. This will make sure only one instance is maintained.

Android, how to start separate activity from service without launching the main app activity?

I have separated activity which is launched from service and is used for collecting results after some event.
Problem is that separated activity is launched and after the selectiong of the result by user is closed using
public void closeApp() {
Toast.makeText(getApplicationContext(), "Saved",Toast.LENGTH_LONG).show();
this.finish();
}
Separated activity is closed but "under" this activity is already started main activity of the app and this is not what i want.
I just want to do following:
Display separated activity,
Select result,
Close separated activity
Without starting the main app activity (if user want to see collected result must tap on the app icon).
Here is what i tried:
Manifest:
Main activity:
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="#string/app_name" android:launchMode="singleTop" android:name="CallPlanner" android:theme="#android:style/Theme.Black.NoTitleBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Separated activity:
<activity android:name="com.result.plugin.ReceiverActivity" android:theme="#android:style/Theme.Translucent.NoTitleBar" />
Separetd activity is stared from the service using these tags:
intentOne.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentOne.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
How can i solve it and what is best way how to do it?
Many Thanks for any help.
EDIT: ADDED ACTIVITY CALLING FROM RECEIVER
/*
* CALL RESULT ACTIVITY IN CASE THAT OUTGOING CALL IS ENDED, NUMBER IS AVAILABLE AND APP IS ACTIVE
*/
if (DataHolder.getAction().equals("OUTGOING_CALL")
&& !DataHolder.getPhoneNumber().isEmpty()
&& extraState.equals(TelephonyManager.EXTRA_STATE_IDLE)
&& isAppActive == 1) {
Log.i("Catched", "Catched");
Toast.makeText(context, "Catched",Toast.LENGTH_LONG).show();
// START ACTIVITY AND PASS PARAMS
intentOne = new Intent(context.getApplicationContext(),
ReceiverActivity.class);
// intentOne = new Intent(context.getApplicationContext(),
// CordovaReceiverActivity.class);
if(intentOne == null) {
throw new NullPointerException(
" :: intentOne 'param' was null inside method 'onReceive'.");
}
intentOne.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentOne.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
//intentOne.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
//intentOne.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
//intentOne.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
/*
intentOne.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intentOne.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
intentOne.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intentOne.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
intentOne.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
*/
// PASS PARAM TO CALLED ACTIVITY
Bundle b = new Bundle();
b.putString("phone_number", DataHolder.getPhoneNumber());
// Put your id to your next Intent
intentOne.putExtras(b);
// START ACTIVITY
context.startActivity(intentOne);
};

Starting the Main activity from another activity

I am trying to achieve following case on Android, but no success:
1) Launch Application (Launcher Activity which is a subclass of Base Activity). The Base Activity has code as follows:
///This is in BaseActivity
#Override
public void onCreate(Bundle instance)
{
super.onCreate(instance);
//Config.isLoggedIn() is a static function.
if(! Config.isLoggedIn())
{
////Config.startLoginActivity is a static function
Config.startLoginActivity(this, getIntent());
finish();
}
}
The Config.startLoginActivity functions is defined as
public static void startLoginActivity(final Context ctx, final Intent finishIntent)
{
Intent i = new Intent(ctx, ItemListActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("FINISH_INTENT", finishIntent);
ctx.startActivity(i);
}
Now, the ItemListActivity contains a list of Items as {Item1, Item2, Item3}. In ItemListActivity, I am saving the passed "finishIntent" as
///This is ItemListActivity onCreate Method
if(getIntent().hasExtra("FINISH_INTENT"))
mFinishIntent = getIntent().getParcelableExtra("FINISH_INTENT");
and the onItemListSelected method is described as follows :
#Override
public void onItemSelected(String id) {
Config.setLogInState(true);
if(mFinishIntent != null)
{
Log.i("ITEMLISTACTIVITY", "Class Name = " + mFinishIntent.getClass().getName());
Log.i("ITEMLISTACTIVITY", "Starting mFinishIntent Activity");
startActivity(mFinishIntent);
finish();
}
}
But the issue is the Main Activity is not being launched again, Android takes me to the home screen instead. While looking for a solution, I saw that Google I/O app has the same implementation and that works flawlessly but in my case it is not. I am unable to figure it out. Please help.
Thanks in Advance.
Manifest File is as follows :
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.app.myapplication.ItemListActivity"
android:label="#string/app_name" >
</activity>
<activity
android:name="com.app.myapplication.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Ok Here is a quick help which works for 100 percent which I'm using not mostly but EVERYTIME! you must past it through intent and in your case here it is how it must look like.
Intent intent = new intent(//name of your activity in which you are at the moment.this, //name of activity to which you want to go.class);
startActivity(intent);
Hope this will help

android open dialogue activity without opening main activity behind it

Im writing a program that offers a quick reply dialog upon receipt of an SMS.
However, I am getting an unexpected result. When I receieve an SMS, the appropriate dialog activity comes up displaying the correct phone number and message, however there is a second activity behind it that is the 'default' activity in my program (it is what opens when i launch my application)
I do not want this second activity to come up. The quick reply activity should come up by itself over top of whatever the user was doing before.
The 'floating' activity:
public class quickReply extends Activity {
String mNumber, mMessage;
TextView mMainText;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mMainText = (TextView)findViewById(R.id.mainText);
try{
Intent i = getIntent();
Bundle extras = i.getExtras();
mNumber = extras.getString("theNumber");
mMessage = extras.getString("theMessage");
this.setTitle("Message From:" + mNumber);
mMainText.setText(mMessage);
} catch(Exception e) {
mMainText.setText(e.getMessage());
}
}
}
The call to the activity inside an onReceive()
Intent i = new Intent(context, quickReply.class);
i.putExtra("theNumber", mNumber);
i.putExtra("theMessage", mMessage);
i.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
The Manifest:
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".quickReply"
android:label="#string/app_name"
android:theme="#android:style/Theme.Dialog"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".SmsReceiver">
<intent-filter>
<action android:name=
"android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
the only way I have found that works, in your activity definition in manifest:
android:launchMode="singleInstance"
but then you have to relaunch your main/default activity once the dialog is dismissed. NOTE: you will lose all state from the previous launch, so this is a less than ideal solution.
UPDATE:
you can also do this by:
Intent.FLAG_ACTIVITY_CLEAR_TASK
so here's what I did:
open the original/main activity
from a service, launch the dialog style activity using the above (main goes bye-bye).
when the user dismisses the dialog, start main again with an extra intent (IS_BACK) that is processed in onCreate() and calls:
moveTaskToBack(true);
this will keep the task under the dialog on top and your main in the back of the stack.
You should set the task affinity of the activity to something different than your main activity. This will separate it from the main activity and it will track as a separate task:
<activity android:name=".quickReply"
android:label="#string/app_name"
android:theme="#android:style/Theme.Dialog"
android:launchMode="singleTask"
android:taskAffinity="quickReply"
>

Categories

Resources