1. 程式人生 > >四大元件之廣播接收者(BroadCastReceiver)

四大元件之廣播接收者(BroadCastReceiver)

為什麼需要廣播接收者?
生活中有什麼樣的人需要聽廣播呢?比方說老人聽聽廣播解解悶,計程車司機聽聽廣播瞭解一下當前的路況資訊,寂寞的大學生聽一些音樂和情感夜話等等,他們都是有接收廣播的需求的,在android開發中很多app也需要接收這樣的廣播來獲取一些資訊,所以在app內就必須定義廣播接收者(收音機)。
想聽廣播必須有兩樣東西,第一是電臺,第二是收音機。
Android系統內部已經定義好了許多電臺,對應一些廣播事件,比如外撥電話,簡訊到來,sd卡狀態,電池電量等等資訊…
此時我們的APP便可以使用broadcastReceiver去接收系統已經定義好的這些事件。
最需要了解的是:定義廣播接收者的目的是為了方便開發者進行開發的。


下面我們從最簡單的廣播接收者講起,接收系統定義好的一條廣播事件。
廣播接收者案例——ip撥號器
此案例當手機外撥電話時,app接收到廣播並進行一定的處理。
1.新建一個android工程,定義一個類繼承BroadCastReceiver,實現其onReceive()方法。當接收到廣播時,執行onReceive()方法。此處相當於買了一個收音機。

public class OutGoingCallReceiver extends BroadcastReceiver {

	//當進行外撥電話的時候 呼叫
	@Override
	public void onReceive(Context context, Intent intent) {
		
		}
		
	}

	
}

在清單檔案中註冊廣播接收者(相當於給收音機安上電池),在意圖過濾器中定義接收的廣播型別為外撥電話,相當於給收音機調頻。

 <!-- 配置廣播接收者 -->
        <receiver android:name="com.example.ipdial.OutGoingCallReceiver">
            <intent-filter >
                <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
            </intent-filter>
            
        </receiver>

增加相應的許可權(不是每一個廣播接收者都需要增加)

 <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="16" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

這樣一個最簡單的靜態方法註冊廣播接收者就定義好了。當外撥電話時,就會執行onReceive()方法。靜態註冊(通過清單檔案註冊)的廣播接收者在Activity退出後仍然生效。
但是我們需要對廣播接收到的資訊做一些處理,此處在onReceive()方法就不能進行空實現了,需要對接收到的資訊做處理。

public class OutGoingCallReceiver extends BroadcastReceiver {

	//當進行外撥電話的時候 呼叫
	@Override
	public void onReceive(Context context, Intent intent) {
		//[1]獲取當前撥打的電話號碼
		String currentNumber=getResultData();
		System.out.println("currentNumber:"+currentNumber);
		//[2]在當前的號碼前面加上17951
		//[2.1]判斷當前撥打的號碼是否是長途
		if(currentNumber.startsWith("0")){
			//[2.2]修改撥打電話的號碼
			setResultData(“17951”+currentNumber);
			System.out.println("聯合內容:"+“17951”+currentNumber);
		}
	}
}

這樣外撥電話時,就會在電話號碼前自動加上17951.
其中getResultData()是獲得廣播的內容。
setResultData();是修改廣播的內容。
案例二——在廣播中開啟Activity
注意,在廣播接收者中是不能直接開啟Activity的。如果需要開啟,需要新增一個任務棧的標記。
此案例實現在重啟時開啟Activity,並且禁用返回鍵,使得當前裝置只能使用當前開啟的app。
MainActivity

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	//當按返回鍵的時候呼叫此方法
	@Override
	public void onBackPressed() {
		// TODO Auto-generated method stub
		//此方法呼叫父類的finish()方法,如果註釋掉,則返回鍵會失效。
		//super.onBackPressed();
}
	
}

註冊廣播接收者,使得在收到重啟廣播時開啟指定Activity

public class BootReciver extends BroadcastReceiver{

	//當手機重新啟動的時候呼叫
	@Override
	public void onReceive(Context context, Intent intent) {
		//在這個方法裡開啟activity
		Intent intent2=new Intent(context,MainActivity.class);
		
		//&&&注意  不能在廣播接收者裡直接開啟activity  需要新增一個標記  新增一個任務棧的標記
		intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		//由上下文開啟activity
		context.startActivity(intent2);
	}
}

清單檔案中註冊,“調頻”至手機重啟時接收廣播。

<receiver android:name="com.example.bootreciver.BootReciver">
            <intent-filter >
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

增加許可權

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

這樣,在重啟裝置時,將直接開啟該應用介面,並禁用返回鍵(可以同時禁用home鍵)。
接下來我們討論廣播的型別——有序廣播和無序廣播
1有序廣播
類似中央傳送紅標頭檔案,按照一定的優先順序進行接收
特徵:
①有序廣播可以被終止
②有序廣播的資料可以被修改
2無序廣播
比如新聞聯播 每天晚上7點準時開播
特點:
①無序廣播不可以終止。
②資料不可以被修改。
之前討論的兩個案例都是系統定義好的廣播,我們接收就可以了。現在我們以傳送無序廣播和有序廣播為例,來自定義傳送廣播,自己來搭建電臺。
傳送無序廣播
MainActivity

//點選按鈕傳送一條無序廣播
	public void click(View v){
		Intent intent=new Intent();
		intent.setAction("com.exmple.custom");//定義廣播接收者在清單檔案中應該指定的action
		intent.putExtra("name", "新聞聯播每天晚上七點準時開播");
		sendBroadcast(intent);//傳送無序廣播
	}

傳送廣播到此為止,不用定義清單檔案之類的東西。主要程式碼就是sendBroadcast(intent);
用intent來指定廣播的名稱及內容。
接收無序廣播
在另一個應用中定義廣播接收者

public class WuxuReceiver extends BroadcastReceiver {

	//當接收到我們傳送的自定義無序廣播時執行
	@Override
	public void onReceive(Context context, Intent intent) {
		String content=intent.getStringExtra("name");
		Toast.makeText(context, content, 1).show();
	}
}

清單檔案宣告廣播接收者

<receiver android:name="com.example.receivewuxubroadcast.WuxuReceiver">
            <intent-filter>
                <action android:name="com.exmple.custom"/>
            </intent-filter>
        </receiver>

當傳送廣播時,第二個應用就會列印"新聞聯播每天晚上七點準時開播"的Toast資訊。
傳送有序廣播
MainActivity

//點選按鈕 傳送有序廣播 發大米
	public void click(View v){
		Intent intent=new Intent();
		intent.setAction("com.example.sendrice");
		/*intent 意圖
		 * receiverPermission  接收許可權
		 * resultReceiver 最終的receiver
		 * scheduler handler
		 * initialCode 初始碼
		 * initialData 初始化資料
		 * initialExtras 額外的資料 
		 * */
		sendOrderedBroadcast(intent, null, new FinalReciver(), 
				null, 1,"習大大給每個人發了一千斤大米" ,null );
	}

接收有序廣播
一級一級接收,定義省,市,鄉,農民四個接收級別。

public class ProvienceReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //獲取傳送廣播攜帶的資料
        //廣播攜帶資料用getResultData();  intent攜帶用getStringExtra();
        String content = getResultData();
        Toast.makeText(context,"省:"+content,Toast.LENGTH_SHORT).show()
}

public class CityReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //獲取傳送廣播攜帶的資料
        //廣播攜帶資料用getResultData();  intent攜帶用getStringExtra();
        String content = getResultData();
        Toast.makeText(context,"市:"+content,Toast.LENGTH_SHORT).show();
    }
}

public class CountryReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //獲取傳送廣播攜帶的資料
        //廣播攜帶資料用getResultData();  intent攜帶用getStringExtra();
        String content = getResultData();
        Toast.makeText(context,"鄉:"+content,Toast.LENGTH_SHORT).show();
    }
}

農民

public class NongminReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //獲取傳送廣播攜帶的資料
        //廣播攜帶資料用getResultData();  intent攜帶用getStringExtra();
        String content = getResultData();
        Toast.makeText(context,"農民:"+content,Toast.LENGTH_SHORT).show();
    }
}

清單檔案中宣告優先順序
優先順序一般是1000以下,有相對的大小即可。

<receiver android:name=".ProvienceReciever">
            <intent-filter android:priority="1000">
                <action android:name="com.example.sendrice">
                </action>
            </intent-filter>
        </receiver>
        <receiver android:name=".CityReciever">
            <intent-filter android:priority="500">
                <action android:name="com.example.sendrice">
                </action>
            </intent-filter>
        </receiver>
        <receiver android:name=".CountryReciever">
            <intent-filter android:priority="100">
                <action android:name="com.example.sendrice">
                </action>
            </intent-filter>
        </receiver><receiver android:name=".NongminReciever">
        <intent-filter android:priority="-100">
            <action android:name="com.example.sendrice">
            </action>
        </intent-filter>
    </receiver>

這樣傳送完廣播後,廣播就會從省市鄉農民一級一級往下傳。
有序廣播可以被更改:比如廣播發到省裡時是1000斤大米,省裡在往下發的時候剋扣了500斤。

public class ProvienceReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //獲取傳送廣播攜帶的資料
        //廣播攜帶資料用getResultData();  intent攜帶用getStringExtra();
        String content = getResultData();
        Toast.makeText(context,"省:"+content,Toast.LENGTH_SHORT).show();
        //修改廣播資料
        setResultData("習大大給每人發了500斤大米");
    }
}

這樣廣播在往下發的時候就變成了"習大大給每人發了500斤大米"。
如果省裡財政緊張,把大米全部截留了,不再往下發,這就屬於終止廣播,之後的優先順序低的市、鄉、農民廣播接收者都不能在收到廣播。

public class ProvienceReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //獲取傳送廣播攜帶的資料
        //廣播攜帶資料用getResultData();  intent攜帶用getStringExtra();
        String content = getResultData();
        Toast.makeText(context,"省:"+content,Toast.LENGTH_SHORT).show();
        //終止廣播
        abortBroadcast();
    }
}

但是習大大不會任由廣播接收者截留,所以在傳送有序廣播時定義看最終的廣播接收者,無論該條廣播是不是被終止了,最終的廣播接收者都會收到最後的廣播資訊,檢查是否有人剋扣大米,報告給習大大。
在傳送有序廣播的應用中定義最終的廣播接收者:
注意在傳送有序廣播時第三個引數,必須在引數中拿到此最終的廣播接收者例項才能不用註冊。

public class FinalReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //FinalReciever有特權,不用在清單檔案中註冊,並且就算廣播被終止,最後FinalReciever也能收到廣播。
        //用來檢查廣播傳遞的情況。
        Toast.makeText(context,"報告習大大:"+getResultData(),Toast.LENGTH_SHORT).show();

    }
}

此時執行程式,無論是否被終止, FinalReciever都會最後收到該廣播資訊。

最後一個重點:
廣播的靜態註冊和動態註冊。
靜態註冊:所有在清單檔案中宣告的廣播接收者註冊方式。
動態註冊:不在清單檔案中宣告,通過程式碼的方式進行廣播接收者的註冊。

以上講的所有註冊方式都是靜態註冊,就算Activity退出,廣播接收者依然生效。
動態註冊的廣播接收者一旦註冊它的Activity退出,廣播也隨之失效。
如果該廣播頻繁被髮送,則監聽該廣播的廣播接收者應該使用動態註冊的方式。比如解鎖屏。
MainActivity
動態註冊廣播接收者

public class MainActivity extends AppCompatActivity {

    private ScreenBroadCastReciever screenBroadCastReciever;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        screenBroadCastReciever = new ScreenBroadCastReciever();

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.intent.action.SCREEN_OFF");
        intentFilter.addAction("android.intent.action.SCREEN_ON");
        //動態註冊廣播接收者
        registerReceiver(screenBroadCastReciever,intentFilter);
    }

廣播接收者定義

//註冊操作頻繁的廣播接收者(例如解鎖螢幕),在清單檔案中配置無效,在程式碼中動態配置。
public class ScreenBroadCastReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //獲取當前廣播的事件型別
        String action = intent.getAction();
        if("android.intent.action.SCREEN_OFF".equals(action)){
            System.out.println("螢幕鎖屏了");
        }
        if("android.intent.action.SCREEN_ON".equals(action)){
            System.out.println("螢幕解鎖了");
        }
    }
}

最後需要在Activity銷燬的時候取消廣播接收者的註冊。

 @Override
    protected void onDestroy() {
        //當activity銷燬的時候要取消註冊廣播接收者(否則會發生Activity洩露,當Activity銷燬的時候,依然在註冊)
        //當activity銷燬,廣播也失效。
        unregisterReceiver(screenBroadCastReciever);
        super.onDestroy();
    }

問題:為什麼操作特別頻繁的廣播事件要搞一個動態註冊呢?
假如這種操作頻繁的廣播事件可以通過靜態方式註冊,那麼在應用退出後,該廣播接收者仍然生效,如果你的裝置中有1000個應用同時註冊了這種操作頻繁的廣播接收事件,此時我一鎖屏,1000個應用都接收到了這個事件,如果一個應用申請一兆記憶體,那麼瞬間你的裝置將要申請1000M的空間,我此次頻繁發廣播,系統要頻繁申請這1000M記憶體,對於android來說,記憶體是有限的,所以此時通過程式碼註冊,應用退出後就不再接收廣播事件,節省了記憶體。

最後強調一下一個知識點:
靜態註冊應用退出後仍然生效,動態註冊Activity退出後廣播接收者也隨之失效。

如果想要動態註冊的廣播接收者不隨Activity退出而失效,可以把動態註冊廣播接收者程式碼放在服務中。
在服務中註冊廣播接收者

package com.example.registbroadcast;

import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;

public class ScreenService extends Service {

	private ScreenReceiver receiver;
	@Override
	public IBinder onBind(Intent intent) {
		
		return null;
	}

	@Override
	public void onCreate() {
		//頻繁操作,動態註冊
		//[1]建立ScreenReceiver物件
		receiver = new ScreenReceiver();
		//[2]獲取IntentFilter例項  目的是新增action
		IntentFilter filter=new IntentFilter();
		//[3]新增action
		filter.addAction("android.intent.action.SCREEN_OFF");
		filter.addAction("android.intent.action.SCREEN_ON");
		//[4]動態註冊廣播接收者
		registerReceiver(receiver, filter);
		super.onCreate();
	}
	@Override
	public void onDestroy() {
		//當服務被銷燬的時候  取消註冊廣播接收者
		unregisterReceiver(receiver);//取消註冊
		super.onDestroy();
	}
}