Android - 我的应用程序替换了共享其内容的应用程序
Android - My app replaces the app which shared its content
我正在开发一个使用 intents(ACTION_SEND) 接收数据的应用程序。我在分享来自 Chrome 的内容时发现了一个奇怪的问题(只发生在 Chrome 83 或更高版本,旧版本无法重现该问题)例如,如果仅选择内容文本,例如,数据正确到达我的应用程序,但如果共享的内容是 URL,它到达我的应用程序,但突然我的应用程序替换了应用程序切换器中的 Chrome。所以,如果我有我的应用程序并打开 Chrome,在数据共享之后我有我的应用程序的两个实例(即使 Chrome 图标出现在那个 window 的顶部,如果我点击它,打开我的应用程序)。
对这里发生的事情有什么想法吗?值得注意的是,我无法使用 Firefox 重现此内容。
我的activity代码:
class MainActivity : HybridActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Log.d("MyApp", "onCreate")
super.onCreate(savedInstanceState)
val testButton = Button(this)
setContentView(testButton)
}
override fun onStart(){
super.onStart()
var bundle = intent.getExtras();
Log.d("MyApp", "onStart intent tostr: " + intent.toString())
if (bundle != null) {
bundle.keySet().forEach {
Log.d("MyApp", "EXTRA:" + it + "=" + bundle.get(it));
}
}
}
override fun onResume() {
super.onResume()
Log.d("MyApp", "onResume")
}
}
意图过滤器在清单中定义如下:
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
</intent-filter>
PS:不知道这是否相关,但我已将清单中的启动模式更改为 singleInstance
,问题仍然可重现...
PS2:我添加了这个 activity 转储,它是在问题出现时生成的:
https://gist.github.com/Leprosy/f63bf02bb1c2887f0e419799d98635ab
更新 2
所以我做了更多的研究,似乎只有在使用 android 共享表时才会发生这种情况。当共享数据的应用程序使用意图解析器时,activity 在新的 task/instance 中打开。例如 firefox 中的共享功能确实可以正确处理它。不幸的是,我找不到与此相关的任何内容以及如何解决它。
原答案
我认为这是一个需要的行为,因为您的应用程序正在处理 url 并且它没有在您的应用程序中打开。
更新
这是官方文档对您正在使用的隐式深度 links 的描述:
隐式深度 link 是指代应用程序中特定目的地的 URI。当调用 URI 时——例如,当用户单击 link——Android 然后可以将您的应用程序打开到相应的目的地。
触发隐式深度 link 时,返回堆栈的状态取决于隐式 Intent 是否使用 Intent.FLAG_ACTIVITY_NEW_TASK
标志启动:
- 如果设置了标志,任务返回堆栈将被清除并替换为深度 link 目的地。与显式深度 linking 一样,在嵌套图形时,每个嵌套级别的起始目的地——即层次结构中每个元素的起始目的地——也被添加到堆栈中。这意味着当用户从深层 link 目的地按下后退按钮时,他们会导航回导航堆栈,就像他们从入口点进入您的应用程序一样。
- 如果未设置标志,您将保留在前一个应用程序的任务堆栈中,其中触发了隐式深度 link。在这种情况下,“后退”按钮会将您带回到上一个应用程序,而“向上”按钮会在您的导航图中的分层父目标上启动您的应用程序任务。
据我了解,这意味着只有启动您的应用的应用才能更改此行为。
来源:https://developer.android.com/guide/navigation/navigation-deep-link#implicit
您的应用实际上并没有替换 chrome,而是来自您应用的共享 link 意图接收器 activity 到达 chrome [=14] 的顶部=].它仍然是 chrome 任务,只是最重要的活动来自您的应用程序。这确保了按返回键会返回到 chrome,即使您会先切换应用几次。
作为一种解决方法,您可以检测到这一点,在这种情况下,创建一个新的意图来启动一个新的 activity 并关闭直接接收它的意图。
编辑:我对此进行了测试,可以通过以下方式完成:
import android.app.Activity
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
class ExternalTextReceiverActivity : Activity() {
val CUSTOM_EXTRA_NAME = "RELAUNCHED"
override fun onCreate(savedInstanceState: Bundle?) {
if (!intent.getBooleanExtra(CUSTOM_EXTRA_NAME, false)) {
this.finish()
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(CUSTOM_EXTRA_NAME, true);
startActivity(intent);
}
super.onCreate(savedInstanceState);
// The rest of your onCreate code here
}
}
如果您只希望 chrome 使用此代码,则可以使用此代码:
import android.app.Activity
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
class ExternalTextReceiverActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
if (intent.getIntExtra("org.chromium.chrome.extra.TASK_ID", -1) == this.taskId) {
this.finish()
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
super.onCreate(savedInstanceState);
// The rest of your onCreate code here
}
}
在 Android 清单中为您的 Activity 结合 android:launchMode="singleInstance"
或 android:launchMode="singleTask"
以确保它不会为同一个 activity.
我正在开发一个使用 intents(ACTION_SEND) 接收数据的应用程序。我在分享来自 Chrome 的内容时发现了一个奇怪的问题(只发生在 Chrome 83 或更高版本,旧版本无法重现该问题)例如,如果仅选择内容文本,例如,数据正确到达我的应用程序,但如果共享的内容是 URL,它到达我的应用程序,但突然我的应用程序替换了应用程序切换器中的 Chrome。所以,如果我有我的应用程序并打开 Chrome,在数据共享之后我有我的应用程序的两个实例(即使 Chrome 图标出现在那个 window 的顶部,如果我点击它,打开我的应用程序)。
对这里发生的事情有什么想法吗?值得注意的是,我无法使用 Firefox 重现此内容。
我的activity代码:
class MainActivity : HybridActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Log.d("MyApp", "onCreate")
super.onCreate(savedInstanceState)
val testButton = Button(this)
setContentView(testButton)
}
override fun onStart(){
super.onStart()
var bundle = intent.getExtras();
Log.d("MyApp", "onStart intent tostr: " + intent.toString())
if (bundle != null) {
bundle.keySet().forEach {
Log.d("MyApp", "EXTRA:" + it + "=" + bundle.get(it));
}
}
}
override fun onResume() {
super.onResume()
Log.d("MyApp", "onResume")
}
}
意图过滤器在清单中定义如下:
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
</intent-filter>
PS:不知道这是否相关,但我已将清单中的启动模式更改为 singleInstance
,问题仍然可重现...
PS2:我添加了这个 activity 转储,它是在问题出现时生成的: https://gist.github.com/Leprosy/f63bf02bb1c2887f0e419799d98635ab
更新 2
所以我做了更多的研究,似乎只有在使用 android 共享表时才会发生这种情况。当共享数据的应用程序使用意图解析器时,activity 在新的 task/instance 中打开。例如 firefox 中的共享功能确实可以正确处理它。不幸的是,我找不到与此相关的任何内容以及如何解决它。
原答案
我认为这是一个需要的行为,因为您的应用程序正在处理 url 并且它没有在您的应用程序中打开。
更新
这是官方文档对您正在使用的隐式深度 links 的描述:
隐式深度 link 是指代应用程序中特定目的地的 URI。当调用 URI 时——例如,当用户单击 link——Android 然后可以将您的应用程序打开到相应的目的地。
触发隐式深度 link 时,返回堆栈的状态取决于隐式 Intent 是否使用 Intent.FLAG_ACTIVITY_NEW_TASK
标志启动:
- 如果设置了标志,任务返回堆栈将被清除并替换为深度 link 目的地。与显式深度 linking 一样,在嵌套图形时,每个嵌套级别的起始目的地——即层次结构中每个元素的起始目的地——也被添加到堆栈中。这意味着当用户从深层 link 目的地按下后退按钮时,他们会导航回导航堆栈,就像他们从入口点进入您的应用程序一样。
- 如果未设置标志,您将保留在前一个应用程序的任务堆栈中,其中触发了隐式深度 link。在这种情况下,“后退”按钮会将您带回到上一个应用程序,而“向上”按钮会在您的导航图中的分层父目标上启动您的应用程序任务。
据我了解,这意味着只有启动您的应用的应用才能更改此行为。
来源:https://developer.android.com/guide/navigation/navigation-deep-link#implicit
您的应用实际上并没有替换 chrome,而是来自您应用的共享 link 意图接收器 activity 到达 chrome [=14] 的顶部=].它仍然是 chrome 任务,只是最重要的活动来自您的应用程序。这确保了按返回键会返回到 chrome,即使您会先切换应用几次。
作为一种解决方法,您可以检测到这一点,在这种情况下,创建一个新的意图来启动一个新的 activity 并关闭直接接收它的意图。
编辑:我对此进行了测试,可以通过以下方式完成:
import android.app.Activity
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
class ExternalTextReceiverActivity : Activity() {
val CUSTOM_EXTRA_NAME = "RELAUNCHED"
override fun onCreate(savedInstanceState: Bundle?) {
if (!intent.getBooleanExtra(CUSTOM_EXTRA_NAME, false)) {
this.finish()
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(CUSTOM_EXTRA_NAME, true);
startActivity(intent);
}
super.onCreate(savedInstanceState);
// The rest of your onCreate code here
}
}
如果您只希望 chrome 使用此代码,则可以使用此代码:
import android.app.Activity
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
class ExternalTextReceiverActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
if (intent.getIntExtra("org.chromium.chrome.extra.TASK_ID", -1) == this.taskId) {
this.finish()
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
super.onCreate(savedInstanceState);
// The rest of your onCreate code here
}
}
在 Android 清单中为您的 Activity 结合 android:launchMode="singleInstance"
或 android:launchMode="singleTask"
以确保它不会为同一个 activity.