从Activity上下文之外调用startActivity()方法

462

我在我的Android应用中实现了一个ListView。我使用ArrayAdapter类的自定义子类来绑定这个ListView。在重写的ArrayAdapter.getView(...)方法内部,我分配了一个OnClickListener。在OnClickListeneronClick方法中,我想启动一个新的活动。我遇到了异常:

Calling startActivity() from outside of an Activity  context requires the  
FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

我该如何获取ListView(当前Activity)所在的Context?


1
我认为Alex的答案应该是你问题的“被采纳”的解决方案,因为它以更通用的方式纠正了你提到的错误。 - devanshu_kaushik
15
我喜欢那个“这真的是你想要的吗?”... 我之前收到过一条消息,说“你确定没有忘记取消某个广播接收器吗?”太棒了!向那些放置这些小提示来帮助我们的人致敬。 - Nerdy Bunz
1
我遇到了这个问题。当我将targetSdkVersion更新为28时。 - illusionJJ
28个回答

693

你可以通过以下方式来缓存Context对象:

  • 在你的适配器中通过构造函数进行缓存,或者
  • 从你的视图中获取。

如果必要,作为最后的手段,

  • 在你的Intent中添加FLAG_ACTIVITY_NEW_TASK标志:

_

myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

编辑 - 我建议避免设置标志,因为它会干扰事件的正常流程和历史堆栈。


6
TextView的_autoLink_功能怎么办,我无法控制系统创建的Intent(因此无法控制标志)? - Alex Semeniuk
101
当我执行 context.startActivity(intent); 时,出现了异常。我只是将 contextApplicationContext 更改为 Activity 类型,问题就解决了。 - Sufian
1
@AlexSemeniuk - 只要将活动作为上下文传递给适配器,autoLink 就可以正常工作。 - Georges
我通过构造函数传递了Context对象,但它没有起作用。但FLAG_ACTIVITY_NEW_TASK对我非常有效,谢谢。 - Hiren
从视图中获取上下文,例如 view.getContext() 对我很有用! - Meet Vora
显示剩余2条评论

125

你可以使用addFlags而不是setFlags来实现它。

myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
根据文档,它会:添加附加标志到意图中(或与现有标志值一起)。 编辑 请注意,当您使用标志时要小心,因为如Alex Volovoy的答案所说,您可能会更改历史堆栈:
避免设置标志,因为这会干扰事件的正常流程和历史堆栈。

1
我有一个非常类似的问题。你有没有遇到过历史堆栈或其他答案中提到的任何问题? - Einar Sundgren
1
我不确定你具体需要什么,但你可以像这样启动一个没有历史记录的活动: Intent intent = new Intent(Intent.ACTION_VIEW, "http:\www.google.com")); intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); startActivity(intent); - Bruno Bieri
为什么不建议在这里添加标志?干扰事件和历史堆栈的正常流程有多重要? - Jason Krs
@JasonKrs 你可以使用 addFlags。只需注意,根据您添加的标志,可能会更改历史堆栈。在这种情况下,可以使用 FLAG_ACTIVITY_NEW_TASK 标志。有关更多详细信息,请阅读:https://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK - Bruno Bieri

78

不要使用 (getApplicationContext),而是使用 YourActivity.this


1
这对我有用,即使用activity.startActivity而不是context.startActivity - Phani Rithvij
2
最佳答案! - amirhossein
这帮助我找出了我的问题。我的代码最终看起来像这样:context.currentActivity?.let { /* 使用上下文进行操作 */ } - Abe
在Kotlin中,这个语法将会是this@YourActivity(如果你在除了你的活动之外的其他作用域,比如compose函数)。 - undefined

66

如果您因使用以下方式创建选择器而遇到错误:

Intent sharingIntent = new Intent(Intent.ACTION_VIEW);
sharingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sharingIntent.setData(Uri.parse("http://google.com"));
startActivity(Intent.createChooser(sharingIntent, "Open With"));

将标志设置为创建此类选择器:

Intent sharingIntent = new Intent(Intent.ACTION_VIEW);
sharingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sharingIntent.setData(Uri.parse("http://google.com"));

Intent chooserIntent = Intent.createChooser(sharingIntent, "Open With");
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(chooserIntent);

4
非常有用。确切地说,选择器意图应该具有这个标记! - Mahdi
2
这是正确的解决方案,也是必须要做的事情,在意图选择器中创建新任务。 - Rafael Guimarães
1
这是唯一对我有效的解决方案。 - Kenneth Murerwa

15

另外,如果您想在片段中以列表视图形式显示链接,请不要这样创建

adapter = new ListAdapter(getActivity().getApplicationContext(),mStrings);

改为调用

adapter = new ListAdapter(getActivity(),mStrings);

适配器在两种情况下都能正常工作,但链接只在最后一种情况下可用。


@user2676468:这个解决了我的自动链接问题。 - Head Geek
这应该是一个可以接受的答案,而不是使用标志,这样更好!! - Gastón Saillén
@GastónSaillén,我不使用getApplicationContext()(除了应用程序初始化),但是捕获了这个异常。因此,情况可能会有所不同。 - CoolMind
这是我的问题,我在上下文中使用了getApplicationContext()。将“this”设置为上下文可以正常工作,因为它与当前活动相关联。 - Brlja

14

我觉得你可能在错误的位置实现了OnClickListener - 通常情况下,你应该在Activity中实现OnItemClickListener,并将其设置在ListView上,否则你会遇到事件问题...


2
你带领我找到了解决方案。我需要使用一个分配给ListView的OnItemClickListener。以下是一些链接供其他人参考: http://developer.android.com/reference/android/widget/AdapterView.OnItemClickListener.html http://www.androidpeople.com/android-custom-listview-tutorial-example-part-2/感谢您的帮助。 - Sako73
请提供通用答案。Alex Volovoy的下面的答案以通用方式解决了问题。 - devanshu_kaushik
为了后代:如果你直接在组件上定义setListener(new Listener),需要一个Context,那么你就会创建一个对整个Activity的隐式引用,这将会像你无法想象的那样泄漏内存。可以通过创建静态内部类监听器或将监听器移动到单独的类中来避免这种情况,如果它需要能够处理来自多个来源的输入。 - G_V

12

Android 28(Android P) 中的 startActivity 方法

if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
        && (targetSdkVersion < Build.VERSION_CODES.N
                || targetSdkVersion >= Build.VERSION_CODES.P)
        && (options == null
                || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
    throw new AndroidRuntimeException(
            "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
}

因此,最好的方法是添加FLAG_ACTIVITY_NEW_TASK

Intent intent = new Intent(context, XXXActivity.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);

这是针对28及以上设备所必需的。 - Md Mohsin

9
CustomAdapter mAdapter = new CustomAdapter( getApplicationContext(), yourlist);

或者

Context mContext = getAppliactionContext();
CustomAdapter mAdapter = new CustomAdapter( mContext, yourlist);

改为以下内容

CustomAdapter mAdapter = new CustomAdapter( this, yourlist);

8

如果您在某个方法的监听器中创建一个意图

override onClick (View v).

然后也可以通过这个视图调用上下文:
v.getContext ()

甚至不需要SetFlags ...


那个错误的情况是什么?v.getApplicationContext()? - CoolMind

4

是的,你只需要按照上面所描述的做法,但措辞已经改变了...intent.SetFlags(ActivityFlags.NewTask); - Luke Alderton
Intent intent = new Intent(); intent.AddFlags(ActivityFlags.NewTask); intent.SetData(Android.Net.Uri.Parse(baseUrl + @"name_of_apk")); Android.App.Application.Context.StartActivity(intent);意图 intent = new Intent(); intent.AddFlags(ActivityFlags.NewTask); intent.SetData(Android.Net.Uri.Parse(baseUrl + @"name_of_apk")); Android.App.Application.Context.StartActivity(intent); - KennyAli
在Xamarin.Android中不需要使用SetFlags。可以使用Xamarin.Essentials.Platform.CurrentActivity.StartActivity(intent); - Junaid Pathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接