BroadcastReceiver應用詳解以及Android實現點擊通知欄後,先啓動應用再打開目標Activity

BroadcastReceiver也就是「廣播接收者」的意思,顧名思義,它就是用來接收來自系統和應用中的廣播。java

在Android系統中,廣播體如今方方面面,例如當開機完成後系統會產生一條廣播,接收到這條廣播就能實現開機啓動服務的功能;當網絡狀態改變時系統會產生一條廣播,接收到這條廣播就能及時地作出提示和保存數據等操做;當電池電量改變時,系統會產生一條廣播,接收到這條廣播就能在電量低時告知用戶及時保存進度,等等。android

Android中的廣播機制設計的很是出色,不少事情本來須要開發者親自操做的,如今只需等待廣播告知本身就能夠了,大大減小了開發的工做量和開發週期。而做爲應用開發者,就須要數練掌握Android系統提供的一個開發利器,那就是BroadcastReceiver。下面咱們就對BroadcastReceiver逐一地分析和演練,瞭解和掌握它的各類功能和用法。git

首先,咱們來演示一下建立一個BroadcastReceiver,並讓這個BroadcastReceiver可以根據咱們的須要來運行。github

要建立本身的BroadcastReceiver對象,咱們須要繼承android.content.BroadcastReceiver,並實現其onReceive方法。下面咱們就建立一個名爲MyReceiver廣播接收者: 安全

 

package com.scott.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class MyReceiver extends BroadcastReceiver {
	
	private static final String TAG = "MyReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = intent.getStringExtra("msg");
		Log.i(TAG, msg);
	}

}

在onReceive方法內,咱們能夠獲取隨廣播而來的Intent中的數據,這很是重要,就像無線電同樣,包含不少有用的信息。服務器

在建立完咱們的BroadcastReceiver以後,還不可以使它進入工做狀態,咱們須要爲它註冊一個指定的廣播地址。沒有註冊廣播地址的BroadcastReceiver就像一個缺乏選臺按鈕的收音機,雖然功能俱備,但也沒法收到電臺的信號。下面咱們就來介紹一下如何爲BroadcastReceiver註冊廣播地址。網絡

靜態註冊併發

靜態註冊是在AndroidManifest.xml文件中配置的,咱們就來爲MyReceiver註冊一個廣播地址:app

 

<receiver android:name=".MyReceiver">
        	<intent-filter>
        		<action android:name="android.intent.action.MY_BROADCAST"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>

配置了以上信息以後,只要是android.intent.action.MY_BROADCAST這個地址的廣播,MyReceiver都可以接收的到。注意,這種方式的註冊是常駐型的,也就是說當應用關閉後,若是有廣播信息傳來,MyReceiver也會被系統調用而自動運行。異步

動態註冊

動態註冊須要在代碼中動態的指定廣播地址並註冊,一般咱們是在Activity或Service註冊一個廣播,下面咱們就來看一下注冊的代碼:

 

MyReceiver receiver = new MyReceiver();
        
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.MY_BROADCAST");
        
registerReceiver(receiver, filter);

注意,registerReceiver是android.content.ContextWrapper類中的方法,Activity和Service都繼承了ContextWrapper,因此能夠直接調用。在實際應用中,咱們在Activity或Service中註冊了一個BroadcastReceiver,當這個Activity或Service被銷燬時若是沒有解除註冊,系統會報一個異常,提示咱們是否忘記解除註冊了。因此,記得在特定的地方執行解除註冊操做:

 

@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(receiver);
}

 

執行這樣行代碼就能夠解決問題了。注意,這種註冊方式與靜態註冊相反,不是常駐型的,也就是說廣播會跟隨程序的生命週期。

咱們能夠根據以上任意一種方法完成註冊,當註冊完成以後,這個接收者就能夠正常工做了。咱們能夠用如下方式向其發送一條廣播:

 

public void send(View view) {
    	Intent intent = new Intent("android.intent.action.MY_BROADCAST");
    	intent.putExtra("msg", "hello receiver.");
    	sendBroadcast(intent);
    }

 

注意,sendBroadcast也是android.content.ContextWrapper類中的方法,它能夠將一個指定地址和參數信息的Intent對象以廣播的形式發送出去。

點擊發送按鈕,執行send方法,控制檯打印以下:

看到這樣的打印信息,代表咱們的廣播已經發出去了,而且被MyReceiver準確無誤的接收到了。

上面的例子只是一個接收者來接收廣播,若是有多個接收者都註冊了相同的廣播地址,又會是什麼狀況呢,能同時接收到同一條廣播嗎,相互之間會不會有干擾呢?這就涉及到普通廣播和有序廣播的概念了。

普通廣播(Normal Broadcast)

普通廣播對於多個接收者來講是徹底異步的,一般每一個接收者都無需等待便可以接收到廣播,接收者相互之間不會有影響。對於這種廣播,接收者沒法終止廣播,即沒法阻止其餘接收者的接收動做。

爲了驗證以上論斷,咱們新建三個BroadcastReceiver,演示一下這個過程,FirstReceiver、SecondReceiver和ThirdReceiver的代碼以下:

 

package com.scott.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class FirstReceiver extends BroadcastReceiver {
	
	private static final String TAG = "NormalBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = intent.getStringExtra("msg");
		Log.i(TAG, "FirstReceiver: " + msg);
	}

}

 

public class SecondReceiver extends BroadcastReceiver {
	
	private static final String TAG = "NormalBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = intent.getStringExtra("msg");
		Log.i(TAG, "SecondReceiver: " + msg);
	}

}

 

public class ThirdReceiver extends BroadcastReceiver {
	
	private static final String TAG = "NormalBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = intent.getStringExtra("msg");
		Log.i(TAG, "ThirdReceiver: " + msg);
	}

}

而後再次點擊發送按鈕,發送一條廣播,控制檯打印以下:

 

看來這三個接收者都接收到這條廣播了,咱們稍微修改一下三個接收者,在onReceive方法的最後一行添加如下代碼,試圖終止廣播:

 

abortBroadcast();

再次點擊發送按鈕,咱們會發現,控制檯中三個接收者仍然都打印了本身的日誌,代表接收者並不能終止廣播。

 

有序廣播(Ordered Broadcast)

有序廣播比較特殊,它每次只發送到優先級較高的接收者那裏,而後由優先級高的接受者再傳播到優先級低的接收者那裏,優先級高的接收者有能力終止這個廣播。

爲了演示有序廣播的流程,咱們修改一下上面三個接收者的代碼,以下:

 

package com.scott.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class FirstReceiver extends BroadcastReceiver {
	
	private static final String TAG = "OrderedBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = intent.getStringExtra("msg");
		Log.i(TAG, "FirstReceiver: " + msg);
		
		Bundle bundle = new Bundle();
		bundle.putString("msg", msg + "@FirstReceiver");
		setResultExtras(bundle);
	}

}

 

public class SecondReceiver extends BroadcastReceiver {
	
	private static final String TAG = "OrderedBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = getResultExtras(true).getString("msg");
		Log.i(TAG, "SecondReceiver: " + msg);
		
		Bundle bundle = new Bundle();
		bundle.putString("msg", msg + "@SecondReceiver");
		setResultExtras(bundle);
	}

}

 

public class ThirdReceiver extends BroadcastReceiver {
	
	private static final String TAG = "OrderedBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = getResultExtras(true).getString("msg");
		Log.i(TAG, "ThirdReceiver: " + msg);
	}

}

咱們注意到,在FirstReceiver和SecondReceiver中最後都使用了setResultExtras方法將一個Bundle對象設置爲結果集對象,傳遞到下一個接收者那裏,這樣以來,優先級低的接收者能夠用getResultExtras獲取到最新的通過處理的信息集合。

 

代碼改完以後,咱們須要爲三個接收者註冊廣播地址,咱們修改一下AndroidMainfest.xml文件:

 

<receiver android:name=".FirstReceiver">
        	<intent-filter android:priority="1000">
        		<action android:name="android.intent.action.MY_BROADCAST"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>
        <receiver android:name=".SecondReceiver">
        	<intent-filter android:priority="999">
        		<action android:name="android.intent.action.MY_BROADCAST"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>
        <receiver android:name=".ThirdReceiver">
        	<intent-filter android:priority="998">
        		<action android:name="android.intent.action.MY_BROADCAST"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>

咱們看到,如今這三個接收者的<intent-filter>多了一個android:priority屬性,而且依次減少。這個屬性的範圍在-1000到1000,數值越大,優先級越高。

 

如今,咱們須要修改一下發送廣播的代碼,以下: 

 

public void send(View view) {
    	Intent intent = new Intent("android.intent.action.MY_BROADCAST");
    	intent.putExtra("msg", "hello receiver.");
    	sendOrderedBroadcast(intent, "scott.permission.MY_BROADCAST_PERMISSION");
    }

注意,使用sendOrderedBroadcast方法發送有序廣播時,須要一個權限參數,若是爲null則表示不要求接收者聲明指定的權限,若是不爲null,則表示接收者若要接收此廣播,需聲明指定權限。這樣作是從安全角度考慮的,例如系統的短信就是有序廣播的形式,一個應用多是具備攔截垃圾短信的功能,當短信到來時它能夠先接受到短信廣播,必要時終止廣播傳遞,這樣的軟件就必須聲明接收短信的權限。

 

因此咱們在AndroidMainfest.xml中定義一個權限:

 

<permission android:protectionLevel="normal"
    			android:name="scott.permission.MY_BROADCAST_PERMISSION" />

而後聲明使用了此權限:

 

<uses-permission android:name="scott.permission.MY_BROADCAST_PERMISSION" />

關於這部分若是有不明白的地方能夠參考我以前寫過的一篇文章:Android聲明和使用權限

而後咱們點擊發送按鈕發送一條廣播,控制檯打印以下:

咱們看到接收是按照順序的,第一個和第二個都在結果集中加入了本身的標記,而且向優先級低的接收者傳遞下去。

既然是順序傳遞,試着終止這種傳遞,看一看效果如何,咱們修改FirstReceiver的代碼,在onReceive的最後一行添加如下代碼:

 

abortBroadcast();

而後再次運行程序,控制檯打印以下:

 

這次,只有第一個接收者執行了,其它兩個都沒能執行,由於廣播被第一個接收者終止了。

上面就是BroadcastReceiver的介紹,下面我將會舉幾個常見的例子加深一下你們對廣播的理解和應用:

1.開機啓動服務

咱們常常會有這樣的應用場合,好比消息推送服務,須要實現開機啓動的功能。要實現這個功能,咱們就能夠訂閱系統「啓動完成」這條廣播,接收到這條廣播後咱們就能夠啓動本身的服務了。咱們來看一下BootCompleteReceiver和MsgPushService的具體實現:

 

package com.scott.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class BootCompleteReceiver extends BroadcastReceiver {
	
	private static final String TAG = "BootCompleteReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		Intent service = new Intent(context, MsgPushService.class);
		context.startService(service);
		Log.i(TAG, "Boot Complete. Starting MsgPushService...");
	}

}

 

package com.scott.receiver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MsgPushService extends Service {

	private static final String TAG = "MsgPushService";
	
	@Override
	public void onCreate() {
		super.onCreate();
		Log.i(TAG, "onCreate called.");
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(TAG, "onStartCommand called.");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}
}

而後咱們須要在AndroidManifest.xml中配置相關信息:

 

<!-- 開機廣播接受者 -->
        <receiver android:name=".BootCompleteReceiver">
        	<intent-filter>
        		<!-- 註冊開機廣播地址-->
        		<action android:name="android.intent.action.BOOT_COMPLETED"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>
        <!-- 消息推送服務 -->
        <service android:name=".MsgPushService"/>

咱們看到BootCompleteReceiver註冊了「android.intent.action.BOOT_COMPLETED」這個開機廣播地址,從安全角度考慮,系統要求必須聲明接收開機啓動廣播的權限,因而咱們再聲明使用下面的權限:

  1. <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />  

 

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

通過上面的幾個步驟以後,咱們就完成了開機啓動的功能,將應用運行在模擬器上,而後重啓模擬器,控制檯打印以下:

 

若是咱們查看已運行的服務就會發現,MsgPushService已經運行起來了。

2.網絡狀態變化

在某些場合,好比用戶瀏覽網絡信息時,網絡忽然斷開,咱們要及時地提醒用戶網絡已斷開。要實現這個功能,咱們能夠接收網絡狀態改變這樣一條廣播,當由鏈接狀態變爲斷開狀態時,系統就會發送一條廣播,咱們接收到以後,再經過網絡的狀態作出相應的操做。下面就來實現一下這個功能:

 

package com.scott.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.widget.Toast;

public class NetworkStateReceiver extends BroadcastReceiver {
	
	private static final String TAG = "NetworkStateReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		Log.i(TAG, "network state changed.");
		if (!isNetworkAvailable(context)) {
			Toast.makeText(context, "network disconnected!", 0).show();
		}
	}
	
	/**
	 * 網絡是否可用
	 * 
	 * @param context
	 * @return
	 */
	public static boolean isNetworkAvailable(Context context) {
		ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo[] info = mgr.getAllNetworkInfo();
		if (info != null) {
			for (int i = 0; i < info.length; i++) {
				if (info[i].getState() == NetworkInfo.State.CONNECTED) {
					return true;
				}
			}
		}
		return false;
	}

}

再註冊一下這個接收者的信息:

 

<receiver android:name=".NetworkStateReceiver">
        	<intent-filter>
        		<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>

由於在isNetworkAvailable方法中咱們使用到了網絡狀態相關的API,因此須要聲明相關的權限才行,下面就是對應的權限聲明:

  1. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>  

 

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

咱們能夠測試一下,好比關閉WiFi,看看有什麼效果。

 

3.電量變化

若是咱們閱讀軟件,多是全屏閱讀,這個時候用戶就看不到剩餘的電量,咱們就能夠爲他們提供電量的信息。要想作到這一點,咱們須要接收一條電量變化的廣播,而後獲取百分比信息,這聽上去挺簡單的,咱們就來實現如下:

 

package com.scott.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
import android.util.Log;

public class BatteryChangedReceiver extends BroadcastReceiver {

	private static final String TAG = "BatteryChangedReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		int currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);	//當前電量
		int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);		//總電量
		int percent = currLevel * 100 / total;
		Log.i(TAG, "battery: " + percent + "%");
	}

}

而後再註冊一下廣播接地址信息就能夠了:

 

<receiver android:name=".BatteryChangedReceiver">
        	<intent-filter>
        		<action android:name="android.intent.action.BATTERY_CHANGED"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>

固然,有些時候咱們是要當即獲取電量的,而不是等電量變化的廣播,好比當閱讀軟件打開時當即顯示出電池電量。咱們能夠按如下方式獲取:

Intent batteryIntent = getApplicationContext().registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); int currLevel = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); int total = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1); int percent = currLevel * 100 / total; Log.i("battery", "battery: " + percent + "%");

 

 

Android實現點擊通知欄後,先啓動應用再打開目標Activity

 

狀況簡述

在開發Android app的過程當中,遇到這樣一個需求:app中啓動一個Service,該Service在獨立進程中運行,與服務器保持長鏈接,將服務器推送過來的消息在通知欄中顯示,並設置點擊動做,點擊後跳轉到app中對應的Activity。目前遇到的問題是Service以獨立進程運行,在收到消息並彈出通知後,app自己的進程有兩種狀況:

  1. app正在運行
  2. app已退出

對於第一種狀況,處理就很是簡單了,直接將參數傳入Intent並打開對應的Activity便可。

但第二種狀況比較複雜,由於app已經退出,而要打開的Activity中的某些操做是須要依賴app的初始化的,這些初始化操做是在app啓動過程當中進行的。舉個例子,一個購物應用推送了某個新商品的消息,用戶點擊通知後進入商品詳情的Activity,而該Activity中有個訂購Button,點擊該Button後就會從本地中獲取用戶的Id等信息併發一條消息給服務器,告訴服務器某用戶訂購了該商品。這些用戶信息是在app啓動時與服務器進行一系列交互後取得的。若是app退出後直接進入詳情Activity並點擊購買,就會由於獲取不到用戶信息而出錯。

因此目前要解決的問題時,在Notification中設置點擊動做,若是app自己正在運行,直接跳轉到目標Activity;若是app已經退出,先啓動app完成初始化,再跳轉到目標Activity。

方案和思路

咱們假設目前有三個Activity:

  1. SplashActivity 用於顯示app大圖,同時進行用戶登陸等操做,服務器返回數據後跳轉到MainActivity。
  2. MainActivity app的主Activity。
  3. DetailActivity MainActivity中點擊Button進入的Activity,用於顯示某件商品詳情。

而彈出通知的Service在另一個進程中。

咱們要達到的目的是:

  1. 點擊通知欄通知,假如app正在運行,則直接跳轉到DetailActivity顯示具體內容,在DetailActivity中按Back鍵返回MainActivity
  2. 點擊通知欄通知,假如app已經退出,先從SplashActivity進入,顯示app啓動界面,初始化操做完成後進入MainActivity再跳轉到DetailActivity顯示具體內容,在DetailActivity中按Back鍵返回MainActivity。

初步的思路是先判斷app進程是否存在,若是存在的話,就利用startActivities啓動MainActivity和DetailActivity。爲何還要啓動MainActivity而不直接只啓動DetailActivity?由於有以下狀況,進程中的全部Activity都已經退出了,但進程尚未被系統回收,這時判斷進程是否存在返回true,而後只啓動DetailActivity的話,按Back鍵任務棧就直接到底,返回桌面了。而咱們要的效果是按Back鍵返回上一級Activity,也就是MainActivity。

若是app進程已經退出,不存在了,此時就用一個Intent啓動應用,該Intent中包含一個Bundle, Bundle中存有啓動DetailActivity所需的參數,這個Intent傳入SplashActivity後,再由SplashActivity傳給MainActivity,在MainActivity中加入判斷,若是有該參數,則表示應用是從通知欄啓動的,要進行跳轉到DetailActivity的操做,不然就是常規啓動。

代碼實現

有了大概的實現思路後,你們來個demo實際操做一下。
首先,咱們的demo有簡單的組件:

  1. PushService,在新進程中啓動的Service,負責監聽服務器,收到服務器的信息後將消息廣播出去,在本demo中,爲了簡化,只是簡單的廣播一個消息
  2. ShowNotificationReceiver,在新進程中註冊的BroadcastReceiver,收到PushService發的消息後,會在通知欄彈出通知
  3. NotificationReceiver, 在新進程中註冊的BroadcastReceiver,用來設置點擊通知欄通知的動做,打開app中的某個Activity
  4. SplashActivity, app啓動頁面,先是啓動圖片,3s後進入MainActivity
  5. MainActivity,app的主Activity
  6. DetailActivity,app中顯示詳情的Activity

PushService.java

首先是PushService,要在新進程中啓動,要在AndroidManifest.xml中加入如下注冊Service的代碼

<service android:name=".PushService"
                 android:process=":push"/>

PushService的工做很簡單,啓動後發一個廣播在通知欄顯示通知,而後常駐在後臺

public class PushService extends Service{
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("PushService", "PushService onCreate");
        //用AlarmManager定時發送廣播
        AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);

        Intent intent = new Intent(this, ShowNotificationReceiver.class);

        PendingIntent pendingIntent =
                PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        am.set(AlarmManager.ELAPSED_REALTIME, SystemClock.currentThreadTimeMillis(), pendingIntent);

    }

}

ShowNotificationReceiver.java

這個廣播類用來在通知欄彈出通知

public class ShowNotificationReceiver extends BroadcastReceiver{
    private static final String TAG = "RepeatReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "ShowNotificationReceiver onReceive");
        //設置點擊通知欄的動做爲啓動另一個廣播
        Intent broadcastIntent = new Intent(context, NotificationReceiver.class);
        PendingIntent pendingIntent = PendingIntent.
                getBroadcast(context, 0, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
        builder.setContentTitle("這就是通知的頭")
                .setTicker("這是通知的ticker")
                .setContentIntent(pendingIntent)
                .setSmallIcon(android.R.drawable.ic_lock_idle_charging);

        Log.i("repeat", "showNotification");
        NotificationManager manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
        manager.notify(2, builder.build());
    }
}

NotificationReceiver.java

點擊通知欄後,會發送一個廣播,NotificationReceiver收到該廣播後,就會判斷,app進程是否仍然存活,根據app進程的不一樣狀態,定義不一樣的app啓動方式

public class NotificationReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        //判斷app進程是否存活
        if(SystemUtils.isAppAlive(context, "com.liangzili.notificationlaunch")){
            //若是存活的話,就直接啓動DetailActivity,但要考慮一種狀況,就是app的進程雖然仍然在
            //但Task棧已經空了,好比用戶點擊Back鍵退出應用,但進程尚未被系統回收,若是直接啓動
            //DetailActivity,再按Back鍵就不會返回MainActivity了。因此在啓動
            //DetailActivity前,要先啓動MainActivity。
            Log.i("NotificationReceiver", "the app process is alive");
            Intent mainIntent = new Intent(context, MainActivity.class);
            //將MainAtivity的launchMode設置成SingleTask, 或者在下面flag中加上Intent.FLAG_CLEAR_TOP,
            //若是Task棧中有MainActivity的實例,就會把它移到棧頂,把在它之上的Activity都清理出棧,
            //若是Task棧不存在MainActivity實例,則在棧頂建立
            mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            Intent detailIntent = new Intent(context, DetailActivity.class);
            detailIntent.putExtra("name", "電飯鍋");
            detailIntent.putExtra("price", "58元");
            detailIntent.putExtra("detail", "這是一個好鍋, 這是app進程存在,直接啓動Activity的");

            Intent[] intents = {mainIntent, detailIntent};

            context.startActivities(intents);
        }else {
            //若是app進程已經被殺死,先從新啓動app,將DetailActivity的啓動參數傳入Intent中,參數通過
            //SplashActivity傳入MainActivity,此時app的初始化已經完成,在MainActivity中就能夠根據傳入             //參數跳轉到DetailActivity中去了
            Log.i("NotificationReceiver", "the app process is dead");
            Intent launchIntent = context.getPackageManager().
                    getLaunchIntentForPackage("com.liangzili.notificationlaunch");
            launchIntent.setFlags(
                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
            Bundle args = new Bundle();
            args.putString("name", "電飯鍋");
            args.putString("price", "58元");
            args.putString("detail", "這是一個好鍋, 這是app進程不存在,先啓動應用再啓動Activity的");
            launchIntent.putExtra(Constants.EXTRA_BUNDLE, args);
            context.startActivity(launchIntent);
        }
    }
}

SplashActivity.java

SplashActivity.java先是app啓動的圖片,3s後進入MainActivity, 若是啓動SplashActivity的Intent中帶有參數,就將參數取出,放入啓動MainActivity的Intent中

public class SplashActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        //隱藏ActionBar
        getSupportActionBar().hide();
        //使用handler倒數3秒後進入MainActivity
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Intent intent = new Intent(SplashActivity.this, MainActivity.class);
                //若是啓動app的Intent中帶有額外的參數,代表app是從點擊通知欄的動做中啓動的
                //將參數取出,傳遞到MainActivity中
                if(getIntent().getBundleExtra(Constants.EXTRA_BUNDLE) != null){
                    intent.putExtra(Constants.EXTRA_BUNDLE,
                            getIntent().getBundleExtra(Constants.EXTRA_BUNDLE));
                }
                startActivity(intent);
                finish();
            }
        }, 3000);
    }
}

MainActivity.java

MainActivity中,若是有參數傳入,就在初始化結束後,根據參數啓動DetailActivity,若是沒有參數傳入,就此結束本身的任務

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, PushService.class);
        startService(intent);
        setTitle("MainActivity");
        Bundle bundle = getIntent().getBundleExtra(Constants.EXTRA_BUNDLE);
        if(bundle != null){
            //若是bundle存在,取出其中的參數,啓動DetailActivity
            String name = bundle.getString("name");
            String price = bundle.getString("price");
            String detail = bundle.getString("detail");
            SystemUtils.startDetailActivity(this, name, price, detail);
            Log.i(TAG, "launchParam exists, redirect to DetailActivity");
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

DetailActivity.java

比較簡單,顯示傳入的參數便可:-D

public class DetailActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        getSupportActionBar().setTitle("DetailActivity");
        String name = getIntent().getStringExtra("name");
        String price = getIntent().getStringExtra("price");
        String detail = getIntent().getStringExtra("detail");

        ((TextView)findViewById(R.id.name)).setText(name);
        ((TextView)findViewById(R.id.price)).setText(price);
        ((TextView)findViewById(R.id.detail)).setText(detail);
    }
}

項目地址:

    BroadcastReceiver相關代碼下載地址: https://github.com/wangzhiyuan888/BroRecDemo

    Android實現點擊通知欄後,先啓動應用再打開目標Activity相關代碼下載地址: 

            https://github.com/wangzhiyuan888/androidcode

相關文章
相關標籤/搜索