Android deep link does not work if the app is opened by deep link already
Asked Answered
I

8

84

Deep link does not work if the app is opened by deep link already.

However, if I open the app not by triggering a deeplink, like clicking the app icon to open the app. Then triggering deeplink afterward would always work.


Here come the details:

So I have my activity set up like this in AndroidManifest, namely LaunchActivity.

<activity
    android:name="some.package.name.LaunchActivity"
    android:screenOrientation="portrait"
    android:theme="@style/Theme.SomeTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <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="dlscheme" android:host="dlhost" />
    </intent-filter>
</activity>

And in LaunchActivity, I would print a log in onCreate() to indicate that it have been there.

I used

adb shell am start -W -a android.intent.action.VIEW -d "dlscheme://dlhost/param" some.package.name

to test the deep link.

With the app killed, I used the above command. It can open the app and route to the correct activity, no problem. And have the following log.

adb shell am start -W -a android.intent.action.VIEW -d "dlscheme://dlhost/param" some.package.name
Starting: Intent { act=android.intent.action.VIEW dat=dlscheme://dlhost/param pkg=some.package.name }
Status: ok
Activity: some.package.name/.activity.LaunchActivity
ThisTime: 898
TotalTime: 898
WaitTime: 919
Complete

However, if I enter the same command again, without killing the app. It would only open the app, but it will not open the correct activity, and produce the following log.

adb shell am start -W -a android.intent.action.VIEW -d "dlscheme://dlhost/param" some.package.name
Starting: Intent { act=android.intent.action.VIEW dat=dlscheme://dlhost/param pkg=some.package.name }
Warning: Activity not started, its current task has been brought to the front
Status: ok
Activity: some.package.name/.activity.LaunchActivity
ThisTime: 0
TotalTime: 0
WaitTime: 6
Complete

with this extra line

Warning: Activity not started, its current task has been brought to the front

I actually also tried this with a website, using this chrome intent:

intent://dlhost/param#Intent;scheme=dlscheme;package=some.package.name;end

and it would behave the same.

Internist answered 11/3, 2016 at 2:14 Comment(2)
Are you seeing the message "Setting last chosen activity" in logcat? I was seeing the same behavior, but that message seems to indicate that this behavior is intended.Stripper
how did you decide that?Intractable
F
58

In the manifest file of your project, you need to add the following to your main activity.

android:launchMode="singleTask"

So, in the manifest, you would have something similar to the below:

<activity android:name="some.package.name.LaunchActivity" 
      android:launchMode="singleTask"
      android:screenOrientation="portrait"
      android:theme="@style/Theme.SomeTheme">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
          <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="dlscheme" android:host="dlhost" />
          </intent-filter>
 </activity>

Basically what this does is create a new task and a new instance will be pushed to the task as the root one. However, if any activity instance exists in any tasks, the system routes the intent to that activity instance through the onNewIntent() method call. In this mode, activity instances can be pushed to the same task. And if the user clicks the BACK key from the current activity, the system will return the user to the previous activity.

On the other hand, in singleTop if an instance of activity already exists at the top of the current task and system routes intent to this activity, no new instance will be created because it will fire off an onNewIntent() method instead of creating a new object.

More information can be found here.

Hope this helps :)

Feltner answered 23/11, 2017 at 9:53 Comment(6)
Hey! Thanks that's a neat way to fix this, thank you. But could you please help me to understand why does it happen? Why android requires singleTask for it's deeplink routing activity to behave correctly? If I open app from deeplink, my DeeplinkActivity is made root for the task. But if i open another activity from it, and finish DeeplinkActivity, won't that make my new activity root one? If that so, why still DeeplinkActivity can't be recreated?Chrischrism
The reason for single task if I recall well is to not have 2 instances of the same activity open at the same time. So in this way, the first activity is kept alive when the deeplink is opened. Deeplink activity becomes the root activity so to say because it's view replaces the previous one you had open.Feltner
Thank you for your answer (: But it bothers me that usually it's ok to have multiple activities opened, i.e. if you launch your application, and then open deeplink, then open different screen in your app and then open deeplink again in works fine, but if your first to launch activity was deeplink activity, second time doesn't work. Like android prevents it's recreation for some reason.Chrischrism
I completely agree with @aleien, that an activity with standard launchMode should be able to open designated activity. Thankfully, this bug is reproduced only on some devices (Nexus 5X) with standard launchMode and only if the app had been initially launched via deep link and opened via deep link again.Cascara
Tks for this. I was a lot of time trying solve this question in a flutter app.Perisarc
Never used Flutter but I am glad this helped.Feltner
R
23

In the manifest file of your project, you need to add the follow to your main activity.

android:launchMode="singleTask"

And handle the deeplink inside onNewIntent()

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_recipe);
    onNewIntent(getIntent());
}

protected void onNewIntent(Intent intent) {
    String action = intent.getAction();
    String data = intent.getDataString();
    if (Intent.ACTION_VIEW.equals(action) && data != null) {
        String recipeId = data.substring(data.lastIndexOf("/") + 1);
        Uri contentUri = RecipeContentProvider.CONTENT_URI.buildUpon()
                .appendPath(recipeId).build();
        showRecipe(contentUri);
    }
}
Reserpine answered 29/3, 2016 at 22:22 Comment(0)
B
9

Add android:launchMode="singleTop" in manifest in your LaunchActivity activity tags

Burnout answered 25/10, 2016 at 13:54 Comment(0)
A
9

I found that adding android:launchMode="singleTask" works. singleTop did not work for me.

Aesir answered 23/12, 2016 at 0:45 Comment(0)
R
4

The Mainefest file looks like this sample

<activity
                    android:name=".user.HomeActivity"
                    android:screenOrientation="portrait"
                    android:exported="true"
                    android:launchMode="singleTop"
                    android:windowSoftInputMode="stateAlwaysHidden"
                    android:theme="@style/AppTheme.NoActionBar" >
                    <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="example"/>
                        <data android:host="example.com"
                            android:pathPrefix="/deeplink"/>
                        <action android:name="android.intent.action.MAIN" />
                    </intent-filter>
                </activity>

HomeActivity

@Override
            protected void onNewIntent(Intent intent) {
                super.onNewIntent(intent);
                Uri data = intent.getData();
                if (data != null)
                    callDeep(data);
                setIntent(intent);
                Log.d("DeepLinking", "new intent value==>" + data + "==== value===>");
            }
        //or in on create 
        @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_home);
          Uri data = intent.getData();
                Log.d("DeepLinking", "intent value==>" + data + "==== value===>" + bundle + "===action==>" + action);
        }
Respire answered 22/11, 2017 at 5:8 Comment(0)
D
4

I was facing similar issue with NavigationUI and AppLink integration. I am following single activity architecture so if the app was already present in the task stack and I wanted to open separate fragment using AppLink it wouldn't work with launchMode="singleTask". I even tried finishOnTaskLaunch but none seems to work.

Then going through the documentation (which i should have done in the first place), I came to know that in the above case the intent is received in onNewIntent() of the activity class and to redirect the intent from current fragment to required fragment we need to call:

// get the instance of navController for activity class, then

navController.handleDeepLink(intent)

This solved my problem of opening the required fragment via AppLink/DeepLink when already other fragment is in the task.

Hope I was able to help.

Delisadelisle answered 2/10, 2020 at 13:31 Comment(1)
Very nice tip.. Changing the launching mode as the other answers has soo many implications.Barracoon
R
2

Add android:launchMode="singleTop" in manifest with your activity

if your launch mode is default then this warning comes " Warning: Activity not started, its current task has been brought to the front " because every time it's creating new instance of your activity while if you use Single Top launch mode then onNewIntent() method called instead of creating a new object

Reger answered 24/11, 2017 at 10:48 Comment(0)
E
2

From the logs its said "Warning: Activity not started, its current task has been brought to the front", so a new instance of the activity is not created. In such cases the new intent will redirected to onNewIntent(Intent intent) of the activity.

In your case I suspect you haven't overridden this method and will be extracting info from the onCreate() mehtod of the activity.

Instead create a method something like extractDataFromIntentAndProcess(Intent intent) and invoke it from oncreate method (extractDataFromIntentAndProcess(getIntent())) & also from onNewIntent method (extractDataFromIntentAndProcess(intent)) of your activity.

Epiclesis answered 24/11, 2017 at 14:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.