【问题标题】:Starting app only if its not currently running仅在当前未运行时启动应用程序
【发布时间】:2015-08-13 04:20:49
【问题描述】:

我正在向用户发送推送通知,点击它会打开应用程序。

我的问题是当应用程序已经打开时,点击通知再次启动应用程序。

我只希望它在应用尚未运行时启动它。

我在通知中使用 Pending Intent:

PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Splash.class), 0);

我看到帖子说使用:

<activity 
android:name=".Splash"
android:launchMode="singleTask"

但问题是我正在运行的应用程序正在运行其他活动,然后在应用程序启动 7 秒后完成的启动画面,所以当应用程序运行时,启动画面不是当前活动

【问题讨论】:

  • 您尝试过这些解决方案吗?
  • 你找到解决办法了吗?

标签: android notifications push-notification android-pendingintent


【解决方案1】:

为您的应用使用“启动Intent”,如下所示:

PackageManager pm = getPackageManager();
Intent launchIntent = pm.getLaunchIntentForPackage("your.package.name");
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, launchIntent, 0);

将“your.package.name”替换为 Android 清单中的包名称。

此外,您应该从清单中删除特殊的 launchMode="singleTask"。标准的 Android 行为将满足您的需求。

【讨论】:

  • 大卫·瓦瑟会尝试一下。你能解释一下这将如何工作吗?如果应用程序正在运行,PendingIntent 是否不会启动?
  • 使用启动 Intent,如果应用程序没有运行,Android 将通过启动根活动来启动应用程序。如果它已经在运行,使用启动 Intent 只会将现有任务从后台带到前台。当您运行应用程序时,这完全一样,按 HOME(将任务推到后台),然后单击 HOME 屏幕上的应用程序图标(从而“启动”应用程序)。这不会再次启动应用程序,它只是将现有任务(无论处于何种状态)带到前台。
  • 你试过了吗?这个解决方案有问题吗?
  • @MS 您可能会看到这个讨厌的 Android 错误:stackoverflow.com/questions/16283079/… 尝试通过单击主屏幕上的应用程序图标来强制停止您的应用程序并重新启动它,然后查看是否可以将其带到正如我在这篇文章中所描述的那样。如果您仍有问题,请打开一个新问题!
  • @DavidWasser 您的回答和您提供的链接似乎有效。
【解决方案2】:
String appPackageName = "";

private void isApplicationInForeground() throws Exception {
    ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        final List<ActivityManager.RunningAppProcessInfo> processInfos = am
                .getRunningAppProcesses();
        ActivityManager.RunningAppProcessInfo processInfo = processInfos
                .get(0);
        // for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
        if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
            // getting process at 0th index means our application is on top on all apps or currently open 
            appPackageName = (Arrays.asList(processInfo.pkgList).get(0));
        }
        // }
    }
    else {
        List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
        ComponentName componentInfo = null;
        componentInfo = taskInfo.get(0).topActivity;
        appPackageName = componentInfo.getPackageName();
    }
}

private void notifyMessage(String text) {
    if (appPackageName.contains("com.example.test")) {
        // do not notify
    }
    else {          
        // create notification and notify user  
    }
}

【讨论】:

    【解决方案3】:

    对于使用 Xamarin.Android 的用户。 David Wasser 的答案的 Xamarin 版本如下:

            //Create notification
            var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
            Intent uiIntent = PackageManager.GetLaunchIntentForPackage("com.company.app");
    
            //Create the notification
            var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title);
    
            //Auto-cancel will remove the notification once the user touches it
            notification.Flags = NotificationFlags.AutoCancel;
    
            //Set the notification info
            //we use the pending intent, passing our ui intent over, which will get called
            //when the notification is tapped.
            notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, PendingIntentFlags.OneShot));
    
            //Show the notification
            notificationManager.Notify(0, notification);
    

    【讨论】:

      【解决方案4】:

      不是在点击通知时显示 Splash 活动,而是显示您的 MainActivity,因为您的 Splash 活动将在一段时间后关闭,但 MainActivity 将保持打开状态并且

      <activity 
      android:name=".MainActivity"
      android:launchMode="singleTask"
      

      【讨论】:

      • 这不可能是我的启动画面完成了所有的初始化
      【解决方案5】:

      使用 Splash 作为 Fragment 而不是 Activity。保留 Splash 片段(7 秒),将其替换为所需的片段(着陆页)。

      将 launchMode="singleTask" 添加到清单中。

      正如Rahul 所述,如果应用程序已经在运行,则调用onNewIntent(),否则onCreate()

      @Override
      protected void onNewIntent(Intent intent) 
      {   
          super.onNewIntent(intent);
      }
      

      选择David 的答案,看起来很有希望。

      【讨论】:

        【解决方案6】:

        当点击通知并且您的代码重定向到您想要的屏幕时,只需通过调用此方法替换该代码并根据“真/假”结果重定向到特定屏幕。

            private boolean isAppOnForeground(Context context) {
            ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
            if (appProcesses == null) {
              return false;
            }
            final String packageName = context.getPackageName();
            for (RunningAppProcessInfo appProcess : appProcesses) {
              if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
                return true;
              }
            }
            return false;
          }
        

        【讨论】:

        • 这样的代码对于这个问题来说太过分了。有一些简单的方法可以解决 OP 的问题,而不是使用像这样的 hackey 变通方法来尝试确定您的进程是否在前台。
        • @DavidWasser 所以通过建议您的代码,如果我在应用程序的第三个屏幕上并且如果我点击通知而不是在第三个屏幕上重定向到第一个屏幕,对吗?
        • @DavidWasser 好的,非常感谢您为我推荐此问题的质量代码。我记下了这一点。
        【解决方案7】:
        Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
        

        也许不要启动 Splash Activity 并重新打开(带到前面) MainActivity 并使用一个侦听器更新 UI,该侦听器告诉您您有一个新通知(带有标志 - 布尔值或要制作的界面)一个听众)。

        【讨论】:

          【解决方案8】:

          您可以使用有序广播来完成此操作。

          1) 将您的PendingIntent 更改为启动BroadcastReceiver,这将决定是启动活动还是什么都不做:

          PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, DecisionReceiver.class), 0);
          

          2) 创建决策BroadcastReceiver

          public class DecisionReceiver extends BroadcastReceiver {
              @Override
              public void onReceive(Context context, Intent intent) {
                  context.sendOrderedBroadcast(new Intent(MainActivity.NOTIFICATION_ACTION), null, new BroadcastReceiver() {
                      @Override
                      public void onReceive(Context context, Intent intent) {
                          if (getResultCode() == MainActivity.IS_ALIVE) {
                              // Activity is in the foreground
                          }
                          else {
                              // Activity is not in the foreground
                          }
                      }
                  }, null, 0, null, null);
              }
          }
          

          3) 在您的活动中创建一个BroadcastReceiver,这将表明它处于活动状态:

          public static final String NOTIFICATION_ACTION = "com.mypackage.myapplication.NOTIFICATION";
          public static final int IS_ALIVE = 1;
          private BroadcastReceiver mAliveReceiver = new BroadcastReceiver() {
              @Override
              public void onReceive(Context context, Intent intent) {
                  setResultCode(IS_ALIVE);
              }
          };
          
          // Register onResume, unregister onPause
          // Essentially receiver only responds if the activity is the foreground activity
          @Override
          protected void onResume() {
              super.onResume();
              registerReceiver(mAliveReceiver, new IntentFilter(NOTIFICATION_ACTION));
          }
          
          @Override
          protected void onPause() {
              super.onPause();
              unregisterReceiver(mAliveReceiver);
          }
          

          【讨论】:

            【解决方案9】:

            notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);

            notificationIntent.putExtras(bundle);
            PendingIntent pintent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            

            【讨论】:

              【解决方案10】:

              如果活动在后台运行,请尝试将其添加到您的意图中以将其置于最前面

              Intent intent = new Intent(this, Splash.class); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);

              【讨论】:

              • 这无济于事。 OP 表示他的Splash 活动在应用程序启动后 7 秒已完成,因此无法将其重新排序到前面,因为它不再处于活动状态。不仅如此,他不想将他的Splash 活动带到前台。他只想在不启动任何活动的情况下将现有任务带到前台(如果应用程序已经在运行)。
              • 哦,我想我误解了情况,谢谢你向我解释。那我想应该是Intent intent = new Intent(this, DesiredActivity.class);?因为如果所需的活动已经在运行,则不会再次创建它,它只会被重新排序到前面
              • 没有。如果应用程序尚未运行,他希望应用程序启动,但如果它已经在运行,他只希望应用程序以其所处的任何状态再次回到前台(即:当它进入后台时,顶部的任何活动都应该是当涉及到前景时,位于顶部)。在这种情况下,他不想开始任何活动。
              【解决方案11】:

              首先在 Manifest 文件的 Application 元素中设置默认任务 android:taskAffinity="com.example.testp.yourPreferredName"。在您的SplashActivity 上维护您的android:launchMode="singleTask"。现在,由于您的 SplashActivity 是您的主要条目,因此将此代码添加到 onResume()onNewIntent()onCreate() (再次考虑不推荐 onResume()) - 遵循代码中的 cmets

              //Note these following lines of code will work like magic only if its UPVOTED.
              //so upvote before you try it.-or it will crash with SecurityException
              ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
              
              List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1000);    
                  for(int i =0; i< taskInfo.size(); i++){
                      String PackageName = taskInfo.get(i).baseActivity.getPackageName();
                      if(PackageName.equals("packagename.appname")){// suppose stackoverflow.answerer.Elltz
                          //if the current runing actiivity is not the splash activity. it will be 1
                          //only if this is the first time your <taskAffinity> is be called as a task
                          if(taskInfo.get(i).numActivities >1){
                              //other activities are running, so kill this splash dead!! reload!!                 
                              finish();
                              // i am dying in onCreate..(the user didnt see nothing, that's the good part)
                              //about this code. its a silent assassin
                          }
                          //Operation kill the Splash is done so retreat to base.
                          break;
                      }
                  }
              

              此代码不适用于 api 21+;要使其工作,您需要使用AppTask,这将为您节省额外的代码行,因为您不会在循环中找到您的Task

              希望对你有帮助

              【讨论】:

              • 这完全是愚蠢的。首先,您还没有解释这段代码在做什么。其次,您正在使用火箭筒制作针孔。第三,ActivityManager.getRunningTasks() 方法不适用于此。第四,您可以通过在onCreate() 中调用isTaskRoot() 来完成完全相同的事情:这将告诉您此Activity 是否是任务中的根活动(即:第一个活动),这就是您要尝试的处理所有其他代码。最后,这是一个 hacky 解决方法,因为它不能解决原始问题。更好的答案实际上可以解决问题。
              • 我很年轻,先生,愚蠢是我为数不多的创造性活动之一,1)这是一个问题的答案,它解决了问题仅在当前未运行时启动应用程序, 2).. 3) 先生,我觉得它是为了它可以做的事情,而不仅仅是调试 4) 先生,还有 singleInstance launchMode这将使isTaskRoot() 在该特定活动上返回 true,即使它不是,因为它存在于另一个任务中,所以所有这些代码都是真正需要的 - 我觉得它解决了问题,我什至尝试过,它确实做到了,你应该试试吧,先生,它是一个火箭筒,可以解决针孔):-@DavidWasser
              • 那我们只好同意不同意。
              • 文档明确指出getRunningTasks() 仅用于调试和呈现任务管理用户界面。 像这个回答者那样使用它是不必要的黑客攻击。
              • 我不知道?先生,我不想变得粗鲁,但我觉得这个问题是关于任务管理的,活动在任务上运行,launchMode 改变了任务的创建方式,所以我看不出你的定义是多么不必要,无论如何我觉得它解决了问题-您可以随时尝试,先生,感谢您的回复指出它的用法,您可以随时投反对票(:@rds btw 没有人得到赏金所以没有难过的感觉jkn
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2010-10-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多