在 Application 子类中调用 startActivity 方法

6

我有一个小型的Android应用程序,在其中我直接指定我的应用程序并在ApplicationSubclass的onCreate中进行一些应用程序范围的设置,但是我遇到了以下错误(注意,我知道FLAG_ACTIVITY_NEW_TASK):

Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
    at android.app.ContextImpl.startActivity(ContextImpl.java:644)
    at android.content.ContextWrapper.startActivity(ContextWrapper.java:258)
    at somePart.ofA.nameSpace.ApplicationSubclass.sendNotificationEmail(ApplicationSubclass.java:186)

我会在一些异常被抛出并被我捕获时调用sendNotificationEmail,以便应用程序的用户可以发送一封简短的电子邮件,其中包含有关异常的信息,以便我更容易地修复任何可能出现的问题或指导他们解决问题。

以下是相关代码:

manifest.xml:

<application android:label="@string/app_name" android:icon="@drawable/icon" android:name=".ApplicationSubclass">
// ... some stuff, including all of my app's activities
</application>

应用程序子类被定义为:

public class ApplicationSubclass extends Application {
   // Multiple methods, including an override of onCreate

  public void sendNotificationEmail(String emailBody) {
      Intent emailIntent = new Intent(Intent.ACTION_SEND);
      emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      emailIntent.setType("text/html");
      emailIntent.putExtra(Intent.EXTRA_EMAIL, notificationRecipients);
      emailIntent.putExtra(Intent.EXTRA_SUBJECT, "MyAppName Error");
      emailIntent.putExtra(Intent.EXTRA_TEXT, emailBody);
      try {
          startActivity(Intent.createChooser(emailIntent, "An error has occurred! Send an error report?"));
      } catch (ActivityNotFoundException e) {
          // If there is nothing that can send a text/html MIME type
          e.printStackTrace();
      }
   }
}

我想知道为什么会发生这种情况。我阅读了一些文档,在StackOverflow和互联网上寻找了一些答案。看起来我应该没问题,因为我设置了FLAG_ACTIVITY_NEW_TASK,但显然并非如此!
难道我不能从Application的上下文中尝试执行此操作吗?我必须在我的应用程序中的Activity中进行尝试吗?需要围绕此重新设计有点烦人,并且能够发送异常电子邮件将非常有用!
编辑:Rich提出了一个很好的问题,那就是我为什么要首先这样做呢?答案很简单,因为正在加载应用程序中可能会失败的数据,当发生这种情况时,我希望用户能够发送其中的某些故障数据。
之所以在Application中而不是Activity中,是因为有一些应用程序状态变量我不想每次旋转设备时都失去它们。当我发现对于Activies,每次旋转时都会调用onPause和onCreate时,我将此代码重构到Application中(效果很好!)。我实际上尝试了将这些内容发送到onConfigurationChanged并在我的activity中覆盖该方法的解决方案,但这很混乱,而且出于某种原因,尽管在我的清单中放置了android:configChanges =“keyboardHidden | orientation”,但它并没有正确地为我工作。我确定我可以追求这个选项,但我宁愿将事情留在原地(更清洁)并获得某人发送电子邮件的能力!

问题是,为什么你想在活动范围之外触发这样的意图? - Rich
@Rich 因为我在应用程序启动时加载了各种变量。我希望这些变量保持不变,并且不想每次有人旋转设备时都失去它们。当我发现 onPause 和 onCreate 在旋转时被调用(对于活动)时,我将此代码重构到应用程序中(效果很好!),但是某些加载可能会导致错误,如果发生这种情况,我希望用户能够发送堆栈跟踪的电子邮件! - Kevek
如果您在Application类中有变量,您可以使用getApplicationContext从任何Activity中检索Application类。由于您正在子类化它,因此需要转换该方法的返回值。您可以将其用作存储库,以便在活动超出范围时不会丢失其中的内容。 - Rich
@Rich 是的,那正是我正在做的。而且它正快乐地工作着。问题在于,如果任何方法正在为这些变量加载数据时抛出异常 ApplicationSubclass中,我想捕获该失败信息并提示用户将其发送给我,以便我可以更好地远程诊断出错了什么?-- 在这种情况下,我需要能够从ApplicationSubclass中启动带有emailIntent的Activity。 - Kevek
两个可能的想法:1)通过广播意图发送所需的数据。像应用程序一样从一个共同的基类派生你的活动,并在基类中注册/注销广播接收器,同时在基类中具有启动意图逻辑(这样你总是处于当前活动的范围内)。2)将信息保存到持久存储位置,下次Activity创建自己时(再次需要Activity派生的基类),使用从持久存储加载的数据启动意图。我更喜欢选项1,但两者都可以。 - Rich
显示剩余3条评论
2个回答

17

啊!我明白我犯了什么错了,其实很简单!我将FLAG_ACTIVITY_NEW_TASK设置在错误的Intent上了!我错过了Intent.createChooser(...,...)会返回一个新的Intent,所以您必须在选择器Intent而不是ACTION_SEND Intent上设置标记。

想一想就不难理解,我简直不敢相信我竟然忽略了这个!

所以,如果有人像我一样做了同样的事情,这里是解决方法:

public void sendNotificationEmail(String emailBody) {
        Intent emailIntent = new Intent(Intent.ACTION_SEND);
        emailIntent.setType("text/html");
        emailIntent.putExtra(Intent.EXTRA_EMAIL, notificationRecipients);
        emailIntent.putExtra(Intent.EXTRA_SUBJECT, "MyAppName Error");
        emailIntent.putExtra(Intent.EXTRA_TEXT, emailBody);
        Intent emailChooser = Intent.createChooser(emailIntent, "An error has occurred! Send an error report?");
        emailChooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            startActivity(emailChooser);
        } catch (ActivityNotFoundException e) {
            // If there is nothing that can send a text/html MIME type
            e.printStackTrace();
        }
    }

启动这个活动(emailChooser)会覆盖在您的清单中注册并监听意图过滤器操作MAIN、类别LAUNCHER的活动吗?谢谢! - Throoze

6

我从继承Application的类中启动了一个活动(当用户丢失会话时重新登录),如下所示:

public boolean relogin(Activity act) {      
    Intent intent = new Intent(act,ActivityLogin.class);    
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);     
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
    startActivity(intent);              
    act.finish();// here i finish my current activity
}

我添加了你提供的额外标志,但是没有用。它没有解决我看到的问题。(虽然我不明白为什么,因为我认为设置FLAG_ACTIVITY_NEW_TASK应该允许我从任何地方启动Activity...) - Kevek
2
这应该设置为setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP)吗? - tf.alves
我必须使用act.finish();来完成当前的活动,否则我的应用程序会因以下错误而崩溃。java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState - Ajith M A

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