Android 点击通知栏消息打开activity,并判断app是否运行

简介: Android 点击通知栏消息打开activity,并判断app是否运行

android的通知栏消息点击事件如果是打开一个activity时,我们要考虑两种情况:


应用正在前台运行。

应用已退出。

如果是第一种情况那么就好处理了,直接为Intent设置flag为FLAG_ACTIVITY_NEW_TASK,然后调用context.startActivity方法就行了。flag不是必须的,什么情况下需要设置flag?当在广播接收器中跳转到activity时,当在service中转到activity时。


对于第二种情况,我参照了很多app的做法,现总结为以下两种:


点击通知栏消息打开activity按下返回键后判断app是否启动,如果没有启动就要启动app;

点击通知栏消息时判断app是否正在前台运行,否则先启动app再打开activity,参数通过Intent一层一层往下传递。

需要用到几个方法:获取应用的运行状态,判断应用进程是否在运行,判断某个activity是否存在任务栈里面。


判断某个服务是否正在运行,这个不重要,可能其它地方用到就先贴出来了。 ==


/**
   * 判断某个service是否正在运行
   * 
   * @param context
   * @param runService
   *            要验证的service组件的类名
   * @return 
   */
  public static boolean isServiceRunning(Context context,
      Class<? extends Service> runService) {
    ActivityManager am = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) am
        .getRunningServices(1024);
    for (int i = 0; i < runningService.size(); ++i) {
      if (runService.getName().equals(
          runningService.get(i).service.getClassName().toString())) {
        return true;
      }
    }
    return false;
  }

获取app的运行状态,返回1代表当前应用在前台运行,返回2代表当前应用在后台运行,返回0代表应用未启动(没有一个存活的activity)。

/**
   * 返回app运行状态
   * 
   * @param context
   *            一个context
   * @param packageName
   *            要判断应用的包名
   * @return int 1:前台 2:后台 0:不存在
   */
  public static int isAppAlive(Context context, String packageName) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> listInfos = activityManager
        .getRunningTasks(20);
    // 判断程序是否在栈顶
    if (listInfos.get(0).topActivity.getPackageName().equals(packageName)) {
      return 1;
    } else {
      // 判断程序是否在栈里
      for (ActivityManager.RunningTaskInfo info : listInfos) {
        if (info.topActivity.getPackageName().equals(packageName)) {
          return 2;
        }
      }
      return 0;// 栈里找不到,返回3
    }
  }

判断某个进程是否运行

/**
   * 判断进程是否运行
   * 
   * @param context
   * @param proessName 应用程序的主进程名一般为包名
   * @return
   */
  public static boolean isProessRunning(Context context, String proessName) {
    boolean isRunning = false;
    ActivityManager am = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> lists = am.getRunningAppProcesses();
    for (RunningAppProcessInfo info : lists) {
      if (info.processName.equals(proessName)) {
        isRunning = true;
      }
    }
    return isRunning;
  }

判断某个activity是否在任务栈里面,app启动后会有一个首页,该首页只有当app退出时才会被销毁,因此可用判断MainActivity是否在任务栈里面来判断应用是否已经启动。

/**
   * 判断某一个类是否存在任务栈里面
   * 
   * @return
   */
  public static boolean isExsitMianActivity(Context context, Class<?> cls) {
    Intent intent = new Intent(context, cls);
    ComponentName cmpName = intent.resolveActivity(context
        .getPackageManager());
    boolean flag = false;
    if (cmpName != null) { // 说明系统中存在这个activity
      ActivityManager am = (ActivityManager) context
          .getSystemService(Context.ACTIVITY_SERVICE);
      List<RunningTaskInfo> taskInfoList = am.getRunningTasks(10);
      for (RunningTaskInfo taskInfo : taskInfoList) {
        if (taskInfo.baseActivity.equals(cmpName)) { // 说明它已经启动了
          flag = true;
          break; // 跳出循环,优化效率
        }
      }
    }
    return flag;
  }

接下来是第一种方法的实现:

在需要跳转的activity中或BaseActivity中的onCreate方法中获取intent传递过来的数据,判断是否是从点击通知栏消息跳转过来,并用一个字段保存这个状态,再处理相应的逻辑业务。

private int isNoticeOpen = 0;// 是否是点击消息通知跳转进来的
 ```
 ```java
@Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Bundle bun = getIntent().getExtras();
  if (bun != null) {
    // 判断是否是消息通知点击跳转进行的
    try{
      isNoticeOpen = Integer.valueOf(bun.getString("NOTICE"));
    }catch(NumberFormatException e){
      isNoticeOpen = 0;
      e.printStackTrace();
    }
  }...............获取其它通知传递过来的参数...........
 }
在onDestroy方法中判断该应用是否正在前台运行,但是这里只能用MainActivity是否存在任务栈里面判断,因为当你点击通知消息跳转到某个activity的时候,任务栈里该activity就处于栈顶了,而栈顶的activity的包名就是该应用的包名。
    @Override
      public void onDestroy() {
        super.onDestroy();
        //如果是点击消息跳转进来的,且(该运行的进程里没有该应用进程 或 应用首页的Activity不存在任务栈里面)
        if (isNoticeOpen==1&&
            (!ServiceHelper.isProessRunning(getApplicationContext(),this.getPackageName())
                ||!ServiceHelper.isExsitMianActivity(this,MainActivity_.class))) {
          //启动app
          Intent intent = getBaseContext().getPackageManager()
              .getLaunchIntentForPackage(getPackageName());
          intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
          startActivity(intent);
        }
      }

最后是第二种实现方式


在点击通知消息时就判断应用是否在前台运行,没有就启动应用。这里用到了一个ServiceHelper类,这个类是我写的,为了简写跳转过程而封装一些步骤的类。

1.处理通知消息点击事件跳转到某个页面,这里的Intent并没有设置要跳转到的activity,而是将要跳转到的activity当作Intent的参数。

    /**
   * 处理通知消息点击事件跳转到指定界面
   * 
   * @param msg
   */
  private void handerMsgCallToActivity(Context context, UMessage msg) {
    ......................................................
    Intent intent = new Intent();
    /**
     * 兼容友盟后台直接推送的
     */
    // 获取动作参数
    String actionKey = msg.extra.get("action");
    if (null == actionKey)
      return;
    if ("message".equals(actionKey)) {
      intent.putExtra(UmenPushManager.CALL_TO_ACTIVITY,
          MessageActivity_.class);
      intent.putExtra("NOTICE", true);
      intent.putExtra("msgCount", 999);// 大于0即可
    } else if ("news".equals(actionKey)) {
      String newtype = msg.extra.get("newstype");
      String newsId = msg.extra.get("nid");
      String newsTitle = msg.extra.get("ntitle");
      String newsUrl = msg.extra.get("nurl");
      ..............................
      intent.putExtra(UmenPushManager.CALL_TO_ACTIVITY,DetailsActivity_.class);
      intent.putExtra("NOTICE", true);
      intent.putExtra("news_id", newsId);
      intent.putExtra("url", newsUrl);
    } else if ("outlink".equals(actionKey)) {
      String title = msg.extra.get("title");
      String url = msg.extra.get("url");
      intent.putExtra(UmenPushManager.CALL_TO_ACTIVITY,
          BrowserActivity_.class);
      intent.putExtra("title", title);
      intent.putExtra("url", url);
    } 
    ServiceHelper.startActivityWithAppIsRuning(context, intent);
  }

2.上一步中只是获取并设置页面跳转中要传递的数据并指定了要跳转到哪个页面,而真正的跳转任务交给了ServiceHelper类的startActivityWithAppIsRuning方法实现。在startActivityWithAppIsRuning方法中进行判断应用是否在运行,没有则创建一个Intent,设置跳转目标Activity,该Activity由上一步传过来的Intent获取到。否则就启动应用,intent中传递一个键为FORM_NOTICE_OPEN,值为true的参数标识是从点击消息通知跳转过来的,再将上一步传递过来的intent当做参数传给当前的intent。


    /**
       * 自动判断appUI进程是否已在运行,设置跳转信息
       * 
       * @param context
       * @param intent
       */
      public static void startActivityWithAppIsRuning(Context context,
          Intent intent) {
        int isAppRuning = isAppAlive(context, UmenPushManager.APP_PACKAGE);
        if (isAppRuning != 0) {
          Intent newIntent = new Intent(context, (Class<?>) intent
              .getExtras().getSerializable(
                  UmenPushManager.CALL_TO_ACTIVITY));
          newIntent.putExtras(intent.getExtras());
          newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          context.startActivity(newIntent);
          return;
        }
        // 如果app进程已经被杀死,先重新启动app,将DetailActivity的启动参数传入Intent中,参数经过
        // SplashActivity传入MainActivity,此时app的初始化已经完成,在MainActivity中就可以根据传入
        // 参数跳转到DetailActivity中去了
        Intent launchIntent = context.getPackageManager()
            .getLaunchIntentForPackage(UmenPushManager.APP_PACKAGE);
        launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        launchIntent.putExtra(UmenPushManager.FORM_NOTICE_OPEN, true);
        launchIntent.putExtra(UmenPushManager.FORM_NOTICE_OPEN_DATA, intent);
        context.startActivity(launchIntent);
      }

3.在应用的启动页中不做处理,直接传递给MainActivity,首先是在启动页WelcomeActivity中调用ServiceHelper类的startAppMainActivitySetNoticeIntent方法判断是否从点击通知消息跳转过来,如果是则为跳转到MainActivity的Intent写入传递过来的数据。


   //如果是点击通知打开的则设置通知参数

           ServiceHelper.startAppMainActivitySetNoticeIntent(this, intent);


    /**
       * 启动App时,为跳转到主页MainActivity的Intent写入打开通知的Intent,如果有通知的情况下
       * 
       * @param appStartActivity
       *            app启动的第一个activity,在配置文件中设置的mainactivity
       * @param startMainActivityIntent
       */
      public static void startAppMainActivitySetNoticeIntent(
          Activity appStartActivity, Intent startMainActivityIntent) {
        /**
         * 如果启动app的Intent中带有额外的参数,表明app是从点击通知栏的动作中启动的 将参数取出,传递到MainActivity中
         */
        try {
          if (appStartActivity.getIntent().getExtras() != null) {
            if (appStartActivity.getIntent().getExtras()
                .getBoolean(UmenPushManager.FORM_NOTICE_OPEN) == true) {
              startMainActivityIntent
                  .putExtra(
                      UmenPushManager.FORM_NOTICE_OPEN_DATA,
                      appStartActivity
                          .getIntent()
                          .getExtras()
                          .getParcelable(
                              UmenPushManager.FORM_NOTICE_OPEN_DATA));
            }
          }
        } catch (Exception e) {
        }
      }

4.在MainActivity的onCreate中调用


    /**

       * 如果是从点击通知栏的通知跳转过来的

       */

      ServiceHelper.isAppWithNoticeOpen(this);

1

2

3

4

再看ServiceHelper的isAppWithNoticeOpen方法。

    /**
       * 判断是否是点击消息通知栏跳转过来的
       * 
       * @param mainActivity
       *            主页
       */
      public static void isAppWithNoticeOpen(Activity mainActivity) {
        try {
          if (mainActivity.getIntent().getExtras() != null) {
            Intent intent = mainActivity.getIntent().getExtras()
                .getParcelable(UmenPushManager.FORM_NOTICE_OPEN_DATA);
            Intent newIntent = new Intent(mainActivity, (Class<?>) intent
                .getExtras().getSerializable(
                    UmenPushManager.CALL_TO_ACTIVITY));
            newIntent.putExtras(intent.getExtras());
            mainActivity.startActivity(newIntent);
          }
        } catch (Exception e) {
        }
      }

最关键的一点时,到这一步才处理点击通知消息真正要跳转到的页面。(Class<?>) intent .getExtras().getSerializable( UmenPushManager.CALL_TO_ACTIVITY)获取到的是需要跳转到的页面,其它数据原封不动往下传递过去就行了。

最后附上完整的ServiceHelper类:

/**
 * 后台service组件助手
 * 
 * @author wujiuye
 * 
 */
public final class ServiceHelper {
  /**
   * 判断某个service是否正在运行
   * 
   * @param context
   * @param runService
   *            要验证的service组件的类名
   * @return 
   */
  public static boolean isServiceRunning(Context context,
      Class<? extends Service> runService) {
    ActivityManager am = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) am
        .getRunningServices(1024);
    for (int i = 0; i < runningService.size(); ++i) {
      if (runService.getName().equals(
          runningService.get(i).service.getClassName().toString())) {
        return true;
      }
    }
    return false;
  }
  /**
   * 返回app运行状态
   * 
   * @param context
   *            一个context
   * @param packageName
   *            要判断应用的包名
   * @return int 1:前台 2:后台 0:不存在
   */
  public static int isAppAlive(Context context, String packageName) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> listInfos = activityManager
        .getRunningTasks(20);
    // 判断程序是否在栈顶
    if (listInfos.get(0).topActivity.getPackageName().equals(packageName)) {
      return 1;
    } else {
      // 判断程序是否在栈里
      for (ActivityManager.RunningTaskInfo info : listInfos) {
        if (info.topActivity.getPackageName().equals(packageName)) {
          return 2;
        }
      }
      return 0;// 栈里找不到,返回3
    }
  }
  /**
   * 自动判断appUI进程是否已在运行,设置跳转信息
   * 
   * @param context
   * @param intent
   */
  public static void startActivityWithAppIsRuning(Context context,
      Intent intent) {
    int isAppRuning = isAppAlive(context, UmenPushManager.APP_PACKAGE);
    if (isAppRuning != 0) {
      Intent newIntent = new Intent(context, (Class<?>) intent
          .getExtras().getSerializable(
              UmenPushManager.CALL_TO_ACTIVITY));
      newIntent.putExtras(intent.getExtras());
      newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      context.startActivity(newIntent);
      return;
    }
    // 如果app进程已经被杀死,先重新启动app,将DetailActivity的启动参数传入Intent中,参数经过
    // SplashActivity传入MainActivity,此时app的初始化已经完成,在MainActivity中就可以根据传入
    // 参数跳转到DetailActivity中去了
    Intent launchIntent = context.getPackageManager()
        .getLaunchIntentForPackage(UmenPushManager.APP_PACKAGE);
    launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    launchIntent.putExtra(UmenPushManager.FORM_NOTICE_OPEN, true);
    launchIntent.putExtra(UmenPushManager.FORM_NOTICE_OPEN_DATA, intent);
    context.startActivity(launchIntent);
  }
  /**
   * 启动App时,为跳转到主页MainActivity的Intent写入打开通知的Intent,如果有通知的情况下
   * 
   * @param appStartActivity
   *            app启动的第一个activity,在配置文件中设置的mainactivity
   * @param startMainActivityIntent
   */
  public static void startAppMainActivitySetNoticeIntent(
      Activity appStartActivity, Intent startMainActivityIntent) {
    /**
     * 如果启动app的Intent中带有额外的参数,表明app是从点击通知栏的动作中启动的 将参数取出,传递到MainActivity中
     */
    try {
      if (appStartActivity.getIntent().getExtras() != null) {
        if (appStartActivity.getIntent().getExtras()
            .getBoolean(UmenPushManager.FORM_NOTICE_OPEN) == true) {
          startMainActivityIntent
              .putExtra(
                  UmenPushManager.FORM_NOTICE_OPEN_DATA,
                  appStartActivity
                      .getIntent()
                      .getExtras()
                      .getParcelable(
                          UmenPushManager.FORM_NOTICE_OPEN_DATA));
        }
      }
    } catch (Exception e) {
    }
  }
  /**
   * 判断是否是点击消息通知栏跳转过来的
   * 
   * @param mainActivity
   *            主页
   */
  public static void isAppWithNoticeOpen(Activity mainActivity) {
    try {
      if (mainActivity.getIntent().getExtras() != null) {
        Intent intent = mainActivity.getIntent().getExtras()
            .getParcelable(UmenPushManager.FORM_NOTICE_OPEN_DATA);
        Intent newIntent = new Intent(mainActivity, (Class<?>) intent
            .getExtras().getSerializable(
                UmenPushManager.CALL_TO_ACTIVITY));
        newIntent.putExtras(intent.getExtras());
        mainActivity.startActivity(newIntent);
      }
    } catch (Exception e) {
    }
  }
  /**
   * 判断进程是否运行
   * 
   * @param context
   * @param proessName
   * @return
   */
  public static boolean isProessRunning(Context context, String proessName) {
    boolean isRunning = false;
    ActivityManager am = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> lists = am.getRunningAppProcesses();
    for (RunningAppProcessInfo info : lists) {
      if (info.processName.equals(proessName)) {
        isRunning = true;
      }
    }
    return isRunning;
  }
  /**
   * 判断某一个类是否存在任务栈里面
   * 
   * @return
   */
  public static boolean isExsitMianActivity(Context context, Class<?> cls) {
    Intent intent = new Intent(context, cls);
    ComponentName cmpName = intent.resolveActivity(context
        .getPackageManager());
    boolean flag = false;
    if (cmpName != null) { // 说明系统中存在这个activity
      ActivityManager am = (ActivityManager) context
          .getSystemService(Context.ACTIVITY_SERVICE);
      List<RunningTaskInfo> taskInfoList = am.getRunningTasks(10);
      for (RunningTaskInfo taskInfo : taskInfoList) {
        if (taskInfo.baseActivity.equals(cmpName)) { // 说明它已经启动了
          flag = true;
          break; // 跳出循环,优化效率
        }
      }
    }
    return flag;
  }
}

** 最后想在结尾简单的提一下Activity的四种加载模式:**


1.standard:Activity的默认加载方法,即使某个Activity在Task栈中已经存在,另一个activity通过Intent跳转到该activity,同样会新创建一个实例压入栈中。例如:现在栈的情况为:A B C D,在D这个Activity中通过Intent跳转到D,那么现在的栈情况为: A B C D D 。此时如果栈顶的D通过Intent跳转到B,则栈情况为:A B C D D B。此时如果依次按返回键,D D C B A将会依次弹出栈而显示在界面上。


2.singleTop:如果某个Activity的Launch mode设置成singleTop,那么当该Activity位于栈顶的时候,再通过Intent跳转到本身这个Activity,则将不会创建一个新的实例压入栈中。例如:现在栈的情况为:A B C D。D的Launch mode设置成了singleTop,那么在D中启动Intent跳转到D,那么将不会新创建一个D的实例压入栈中,此时栈的情况依然为:A B C D。但是如果此时B的模式也是singleTop,D跳转到B,那么则会新建一个B的实例压入栈中,因为此时B不是位于栈顶,此时栈的情况就变成了:A B C D B。


3.singleTask:如果某个Activity是singleTask模式,那么Task栈中将会只有一个该Activity的实例。例如:现在栈的情况为:A B C D。B的Launch mode为singleTask,此时D通过Intent跳转到B,则栈的情况变成了:A B。而C和D被弹出销毁了,也就是说位于B之上的实例都被销毁了。


4.singleInstance:将Activity压入一个新建的任务栈中。例如:Task栈1的情况为:A B C。C通过Intent跳转到D,而D的Launch mode为singleInstance,则将会新建一个Task栈2。此时Task栈1的情况还是为:A B C。Task栈2的情况为:D。此时屏幕界面显示D的内容,如果这时D又通过Intent跳转到D,则Task栈2中也不会新建一个D的实例,所以两个栈的情况也不会变化。而如果D跳转到C,则栈1的情况变成了:A B C C,因为C的Launch mode为standard,此时如果再按返回键,则栈1变成:A B C。也就是说现在界面还显示C的内容,不是D。


目录
相关文章
|
2月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
94 6
|
2月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
33 3
|
2月前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
29 3
|
2月前
|
移动开发 小程序 数据可视化
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
372 3
|
3月前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
107 12
|
2月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
21 0
|
3月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
59 4
|
4月前
|
XML Android开发 数据格式
android中两个Activity同时设定了intent-filter的category为android.intent.category.LAUNCHER,会发生什么情况?
本文通过案例分析了在Android中当两个Activity都设置了`android.intent.category.LAUNCHER`类别时,会导致它们同时在应用启动器的"所有应用"页面显示为不同的启动入口。
122 2
android中两个Activity同时设定了intent-filter的category为android.intent.category.LAUNCHER,会发生什么情况?
|
3月前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
61 0
|
4月前
【Azure Function App】本地运行的Function发布到Azure上无法运行的错误分析
【Azure Function App】本地运行的Function发布到Azure上无法运行的错误分析

热门文章

最新文章