深度链接和多个应用实例

Posted

技术标签:

【中文标题】深度链接和多个应用实例【英文标题】:Deep linking and multiple app instances 【发布时间】:2014-11-11 03:47:53 【问题描述】:

我在我的应用中实现了深度链接。我在清单文件中添加了这个意图过滤器,并且深度链接正在工作。

<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>

问题在于,通过深度链接,我的应用在当前应用之上启动。如果我在 Gmail 中并单击一个链接,那么我的应用程序将在 Gmail 之上启动。我想以不同的方式启动我的应用。

如果我的应用程序已经在后台运行,并且我点击了 Gmail 中重定向到我的应用程序的链接,我将同时运行我的应用程序的两个实例;一个在后台,另一个在 Gmail 之上。我想一次只运行我的应用程序的一个实例,因此它不在当前应用程序(Gmail)之上。我该怎么做?

【问题讨论】:

@commonsware 奇怪的是,当您的应用从 Gmail 启动时,它是在与 Gmail 相同的进程中运行还是为它创建了一个新进程?那么,如果你的应用有两个实例,会不会有两个新进程? 【参考方案1】:

您需要在 Manifest 中为您的 Activity 执行以下操作。

android:launchMode="singleTask"

这告诉系统如果 Activity 的现有实例已经创建,则始终启动它。

然后你可以通过重写方法来处理 Intent

onNewIntent 

更多信息请参见http://developer.android.com/guide/topics/manifest/activity-element.html

【讨论】:

如果应用已打开且当前显示在活动 A 中,但深层链接指向活动 B,该怎么办? 如果活动 B 设置了singleTask,如果 B 已经创建,堆栈中的所有活动都将被销毁。因此活动 A 将被销毁。如果活动 B 尚未在堆栈中,它将在活动 A 之上打开。 感谢您的提示! :-) 它可以工作,但是如果在后台打开打开,然后如果我尝试使用启动器图标启动应用程序,那么它会再次重新启动而不是恢复,这是一个缺点。【参考方案2】:

接受的答案对我不起作用,这就是:

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

来自官方文档:

如果设置了,并且正在启动的 Activity 已经在当前任务中运行,那么不会启动该 Activity 的新实例,而是关闭它之上的所有其他 Activity,并且此 Intent 将被传递到(现在在顶部)作为新意图的旧活动。

【讨论】:

不工作...它仍在创建我的应用活动的两个实例 遇到了同样的问题。你可以在***.com/questions/35776907/…看到我的回答【参考方案3】:

我们在深度链接方面遇到了几个问题。 喜欢:

    同一个链接只点击两次,第一次点击触发正确的视图 打开了多个实例 whatsapp 或 facebook 中的链接由于其网络浏览器而在 whatsapp 本身中打开了一个视图。 在 android 6 上只打开了一个实例,但只处理了第一个 意图,第二个和第三个打开应用程序但没有操作,因为意图数据没有以某种方式发生变化。

因此,以下不是对问题的明确答案,而是针对我们遇到的几个问题的全面解决方案。

我们的解决方案:

a) 创建了一个新的 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) 清单:

    <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) 通过调用以下示例函数来处理您的活动 onCreate() 和 onResume() 中传递的新意图:

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
           

【讨论】:

当 MainActivity 由 NavHost 管理时效果很好(从另一个应用程序启动时似乎忽略了 singleTask/singleInstance)【参考方案4】:

我遇到了同样的问题,除了我希望用户使用完整的后台堆栈返回主任务,就好像他们刚刚使用应用程序切换器移动到我的应用程序一样。为此,我不得不重新排序任务。

1) 授予我的应用重新排序任务的权限

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

2) 跟踪 main 任务 ID 是什么

public class MainActivity 
    public static int mainTaskId;

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

3) 当我的深层链接活动启动时,它会保存一些数据以供以后使用,然后将主要任务放在前面

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) 当主任务的后台堆栈顶部的任何活动启动时,它会检查是否有任何已保存的数据要处理,并对其进行处理。

【讨论】:

【参考方案5】:

我只需添加 android:launchMode="singleTask" 即可解决这些问题 在AndroidManifest.xml 文件中

【讨论】:

它可以工作,但是如果在后台打开打开,然后如果我尝试使用启动器图标启动应用程序,那么它会再次重新启动而不是恢复,这是一个缺点。【参考方案6】:

只为一个实例解决这个问题

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>

【讨论】:

SingleTask 并不总是正确的。请阅读 singleInstance 的定义。如果您的应用程序没有公开单个活动,您应该使用单任务而不是单实例。请参阅下面 Alf 的 aswer【参考方案7】:

在离开深度链接Activity时考虑使用finish(),这样如果再次操作深度链接,Activity将被重新创建。 这样可以避免错误和矛盾。

【讨论】:

【参考方案8】:

在经历了多个平台上可用的不同解决方案之后。这是我在您的应用中处理深层链接的最佳实践解决方案。

首先创建一个单独的 Activity 来处理您的深层链接意图,例如。 DeepLinkHandlerActivity。

确保您在清单中指定此 Activity,如下所示:

<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>

将此活动作为“单个任务”。

下一步:设置这个新的活动如下:

    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)
    

注意:SplashActivity 是您的默认活动,即您的启动器活动。

启动器活动代码,例如。

    <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>

就是这样!您的深层链接处理问题已解决!

【讨论】:

【参考方案9】:

(在课程开始时初始化)

String itemInfo == "";

基本比较包名。

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);

这个条件itemInfo.equals(getItem(position).activityInfo.packageName)很重要

【讨论】:

以上是关于深度链接和多个应用实例的主要内容,如果未能解决你的问题,请参考以下文章

来自深度链接意图时如何仅打开 1 个 android 应用程序实例?

带有“App”协议深度链接的 SwiftUI macOS 应用程序打开了新的应用程序实例

将 1 个 VSTS 实例链接到多个 Azure 订阅

广度优先(bfs)和深度优先搜索(dfs)的应用实例

在 Elastic Beanstalk 中扩展实例会破坏 python 烧瓶应用程序的动态图像链接

redis 应用实例(pub/subcache)