Deep linking and multiple app instances
Asked Answered
S

9

79

I have implemented deep linking in my app. I added this intent filter in my manifest file, and the deep linking is working.

<intent-filter>
    <action android:name="android.intent.action.VIEW" /> 
    <category android:name="android.intent.category.DEFAULT" /> 
    <category android:name="android.intent.category.BROWSABLE" /> 
    <category android:name="android.intent.category.VIEW" /> 
    <data
        android:host="www.mywebsite.com"
        android:pathPrefix="/something"
        android:scheme="http" />
</intent-filter>

The problem is that through deep linking, my app is launching on top of current app. If I am in Gmail and I click a link, then my app is launching on top of Gmail. I want to launch my app differently.

If my app is already running in background and I click on a link in Gmail which redirects to my app, I will have two instances of my app running at the same time; one in the background, and another on top of Gmail. I want to run only one instance of my app at a time, so it's not also on top of the current app (Gmail). How can I do that?

Socinian answered 17/9, 2014 at 7:42 Comment(1)
@commonsware As a curiosity, when your app was launched from Gmail, was it running in the same process as Gmail or was a new process created for it? So, if you have two instances of your app, will there be two new processes?Chartism
E
12

the accepted answer didn't work for me, here is what did:

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();

from the official doc:

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.

Endurance answered 11/6, 2015 at 20:32 Comment(2)
Not working... it is still creating two instances of my app activityGolub
Had the same issue. you can see my answer at #35777407Organza
F
92

You need to do following things for your Activity in your Manifest.

android:launchMode="singleTask"

This tells the system to always launch the existing instance of the Activity if it is already created.

And you can handle the Intent then by overriding the method

onNewIntent 

See http://developer.android.com/guide/topics/manifest/activity-element.html for more information.

Floristic answered 23/9, 2014 at 14:24 Comment(4)
What if the app is opened and currently displaying at activity A, but the deeplink points to activity B?Jolley
If activity B has singleTask set, all Activities above in the stack will be destroyed, if B is already created. So activity A will be destroyed. If activity B is not yet in the stack, it will be opened on top of activity A.Floristic
Thanks for the tips ! :-)Pufahl
It works but if the open is opened in background and then again if I try to launch the app using the launcher icon then it again restarts instead of resume which is a disadvantage.Swagman
E
12

the accepted answer didn't work for me, here is what did:

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();

from the official doc:

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.

Endurance answered 11/6, 2015 at 20:32 Comment(2)
Not working... it is still creating two instances of my app activityGolub
Had the same issue. you can see my answer at #35777407Organza
P
5

Well we had several issues with deeplinks. Like:

  1. click on the same link twice only, first click triggered the correct view
  2. opened multiple instances
  3. Links in whatsapp or facebook opened a view in whatsapp itself because of their web browser.
  4. on android 6 opened only one instance, but was only handling the first intent, second and third opens app but no action because the intentdata did not changed somehow.

So the following is not an clear answer to the question more an allround solution for several issues we had.

Our solution:

a) Created a new FragmentActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2); //well can be anything some "loading screen"

    Intent intent = getIntent();
    String intentUrl = intent.getDataString();
    Intent newIntent = new Intent(this, MainActivity.class);
    newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
    newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    newIntent.putExtra("intentUrl",intentUrl);
    newIntent.setAction(Long.toString(System.currentTimeMillis()));

    startActivity(newIntent);
    finish();
}

b) Manifest:

    <activity
        android:name="YOUR.NEW.FRAGMENT.ACTIVITY"
        android:label="@string/app_name"
        android:launchMode="singleTop">
        <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" />
            <data android:scheme="https" />
            <data android:scheme="scheme1" /> <!-- sheme1://-->
            <data android:host="yourdomain.com" />
            <data android:host="www.yourdomain.com" />
        </intent-filter>
    </activity>

c) handle the passed new intent in your activities onCreate() and onResume() by calling the following example function:

private void handleUrl(Intent i){
    String intentUrl = null;
    if (i != null) {
        intentUrl = i.getStringExtra("intentUrl");
        if (intentUrl == null){
            //hmm intent is damaged somehow
        } else {
            //because of onResume()
            if ( i.getBooleanExtra("used",false) ) {
                return;
            }
            i.putExtra("used", true);

           //DO SOMETHING WITH YOUR URL HERE
    }       
}
Platoon answered 18/1, 2017 at 12:19 Comment(1)
This works great when MainActivity is managed by a NavHost (which seems to ignore singleTask/singleInstance when launched from another app)Baseboard
T
3

just solve this issue for only one Instance

android:launchMode="singleInstance"

<activity
    android:name=".SplashScreen"
    android:screenOrientation="portrait"
    android:launchMode="singleInstance"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="nvd.abc" />
    </intent-filter>
</activity>
Tadd answered 17/4, 2017 at 9:42 Comment(1)
SingleTask is not always correct. please read the definition of singleInstance. you should use singletask not singleinstance if your app doesnt expose a single activity. See aswer from Alf belowTeofilateosinte
P
3

I had this exact same problem, except I wanted the user to land back in the main task with the full back stack, as though they had just used the app switcher to move to my app. To accomplish this I had to reorder tasks.

1) Give my app permission to re-order tasks

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.company.app">
     <uses-permission android:name="android.permission.REORDER_TASKS"/>
</manifest>

2) Keep track of what the main task ID is

public class MainActivity {
    public static int mainTaskId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super(savedInstanceState);
         //set the main task ID
         taskId = getTaskId();
    } 
}

3) When my deep link activity is launched it saves some data for use later and then brings the main task to the front

public class DeepLinkActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super(savedInstanceState);

        //persist deep link data
        Uri uri = intent.getData();
        String action = intent.getAction();
        saveForLater(uri, action);

        if(isTaskRoot()){
            //I'm in my own task and not the main task
            final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
            activityManager.moveTaskToFront(MainActivity.taskId, ActivityManager.MOVE_TASK_NO_USER_ACTION);
            }
        }
    }
}

4) When whatever activity is at the top of the main task's back stack starts, it checks if there's any saved data to work on, and works on it.

Pocosin answered 31/10, 2017 at 20:35 Comment(0)
S
3

I solve these issue by just add android:launchMode="singleTask" in AndroidManifest.xml file

Stauder answered 24/7, 2018 at 7:40 Comment(1)
It works but if the open is opened in background and then again if I try to launch the app using the launcher icon then it again restarts instead of resume which is a disadvantage.Swagman
M
0

Consider using finish() when leaving the deep link Activity, so if the deep link is operated again the activity will be recreated. This can avoid errors and contradictions.

Minna answered 3/11, 2019 at 22:33 Comment(0)
D
0

After going through with different solutions available on multiple platforms. Here is my solution for best practise for handling Deep linking in your app.

First create a separate Activity to handle your deep links intents for eg. DeepLinkHandlerActivity.

Make sure you specify this Activity in manifest as follows:

<activity android:name=".DeepLinkHandlerActivity"
            android:launchMode="singleTask">
            <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:host="www.xyz.com"
                    android:pathPrefix="/abc"
                    android:scheme="https" />
             </intent-filter>

put this activity as "Single Task".

Next: Setup this new Activity as follow:

    class DeepLinkHandlerActivity : BaseActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.whaterever_your_layout)
        handelIntent(intent)
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        handelIntent(intent)
    }

    private fun handelIntent(intent: Intent?){
        intent?.setClass(this,SplashActivity::class.java)
        startActivity(intent)
    }
}

Note: SplashActivity is your default activity i.e. your launcher activity.

Launcher activity code eg.

    <activity
            android:name=".splash.SplashActivity"
            android:theme="@style/SplashTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
   </activity>

That's it! your deep link handling problem is solved!

Dispassion answered 22/4, 2021 at 20:15 Comment(0)
L
-1

(initialize at the starting of class)

String itemInfo == "";

Basically compare the package name.

if(!itemInfo.equals(getItem(position).activityInfo.packageName)) 
{ 
    intent.setComponent(new ComponentName(getItem(position).activityInfo.packageName, 
                                          getItem(position).activityInfo.name));

    itemInfo = getItem(position).activityInfo.packageName;
    ((AxisUpiActivtiy) context).startActivityForResult(intent, RequestCodes.START_INTENT_RESPONSE);
}

this condition itemInfo.equals(getItem(position).activityInfo.packageName) is the important

Locomobile answered 10/1, 2019 at 9:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.