1. 程式人生 > >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);

再看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