Android中的廣播
廣播接受器偏友,可以比喻成收音機(jī)蔬胯。而廣播則可以看成電臺(tái)。
Android系統(tǒng)內(nèi)部相當(dāng)于已經(jīng)有一個(gè)電臺(tái) 定義了好多的廣播事件位他,比如外撥電話 短信到來(lái) sd卡狀態(tài) 電池電量變化...
廣播的兩種注冊(cè)方式
靜態(tài)注冊(cè)
靜態(tài)注冊(cè)寫在AndroidManifest.xml
氛濒,即使沒有進(jìn)入應(yīng)用(沒有寫在onCreate里面),也可以接收到鹅髓。如接收開機(jī)廣播舞竿。
監(jiān)聽外撥電話
寫個(gè)撥打電話,自動(dòng)加區(qū)號(hào)的功能窿冯。區(qū)號(hào)可以自定義,每次填了區(qū)號(hào)后靡菇,會(huì)存到偏好文件中重归。若不改區(qū)號(hào),每次默認(rèn)打電話都會(huì)加這個(gè)區(qū)號(hào)厦凤。(這個(gè)程序?qū)嵲跊]什么意義)
可直接在Android Studio中File -> New -> Other -> Broadcast Receiver鼻吮。需要重寫OnReceive方法,這個(gè)方法在廣播接受器接收到廣播的時(shí)候被調(diào)用较鼓。
AndroidManifest.xml
下注冊(cè)
<receiver
android:name=".TelReceiver"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
其中android:enabled="true"
表示啟用這個(gè)廣播椎木,默認(rèn)為true违柏。android:enabled="true"
表示這個(gè)廣播接收器可以接收本程序以外的廣播。其默認(rèn)值是由receiver中有無(wú)intent-filter決定的香椎,如果有intent-filter漱竖,默認(rèn)值為true,否則為false畜伐。一般就上面默認(rèn)生成的寫法就好了馍惹,顯式指定為true更直觀不是嗎。
同時(shí)申請(qǐng)權(quán)限<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
玛界,記住一定要申請(qǐng)運(yùn)行時(shí)權(quán)限啊万矾,我傻傻的調(diào)試了半天......
主界面一個(gè)輸入框用來(lái)自定義默認(rèn)區(qū)號(hào),和一個(gè)按鈕保存到偏好文件慎框。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.telbroadcastreceivertest.MainActivity">
<EditText
android:id="@+id/et_area_code"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/bt_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存區(qū)號(hào)"/>
</LinearLayout>
MainActivity
package com.example.telbroadcastreceivertest;
import android.Manifest;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private EditText editText;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.PROCESS_OUTGOING_CALLS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS}, 1);
}
editText = (EditText) findViewById(R.id.et_area_code);
Button button = (Button) findViewById(R.id.bt_save);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String areaCode = editText.getText().toString().trim();
SharedPreferences spf = getSharedPreferences("code", MODE_PRIVATE);
// 保存區(qū)號(hào)到偏好文件良狈,下次撥打電話默認(rèn)加這個(gè)前綴
spf.edit().putString("code", areaCode).apply();
Toast.makeText(mContext, "區(qū)號(hào)保存成功", Toast.LENGTH_SHORT).show();
}
});
}
}
廣播接收器,可以攔截外撥電話的有序廣播
package com.example.telbroadcastreceivertest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
public class TelReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences spf = context.getSharedPreferences("code", Context.MODE_PRIVATE);
String areaCode = spf.getString("code", "");
// 接收前一個(gè)廣播接收者的所設(shè)定的數(shù)據(jù),這里是撥號(hào)器的廣播接收者笨枯,數(shù)據(jù)是電話號(hào)碼
String phoneNumber = getResultData();
// 區(qū)號(hào)一般以0開頭薪丁,若號(hào)碼不以0開頭,就加上區(qū)號(hào)
if (!phoneNumber.startsWith("0")) {
setResultData(areaCode + phoneNumber);
}
}
}
向外撥打電話時(shí)系統(tǒng)會(huì)發(fā)出一個(gè)有序廣播,android.intent.action.NEW_OUTGOING_CALL
馅精,雖然該廣播最終會(huì)被拔號(hào)器里的廣播接收者所接收并實(shí)現(xiàn)電話拔打严嗜,但我們可以在廣播傳遞給拔號(hào)廣播接收者之前先得到該廣播,并添加了區(qū)號(hào)后再撥打出去硫嘶。
填寫默認(rèn)的區(qū)號(hào)阻问。
由于使用真機(jī),撥號(hào)使用了一個(gè)空號(hào)沦疾。
看圖称近,確實(shí)是添加了區(qū)號(hào)再撥打出去的
測(cè)試成功后,趕緊給卸載了哮塞,這會(huì)影響電話撥打功能的刨秆。
動(dòng)態(tài)注冊(cè)
監(jiān)聽SD卡的掛載/未掛載
注意:這里所說的SD卡是真實(shí)的外置SD卡。
之前使用真機(jī)忆畅,還有模擬器衡未,SD卡的選擇是internal storage而非protable storage。這樣就不帶外置SD卡家凯,所以一直接收不到廣播缓醋,折騰了半天。引以為戒绊诲!
AndroidManifest.xml里面
<receiver android:name=".SDReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
<!-- 一定要指定下面這個(gè)scheme為"file"送粱,否則無(wú)法接收到廣播 -->
<data android:scheme="file"/>
</intent-filter>
</receiver>
上面的寫法是靜態(tài)注冊(cè),用動(dòng)態(tài)注冊(cè)的方式如下掂之,也一定要加上scheme抗俄。注意動(dòng)態(tài)注冊(cè)只能當(dāng)前Activity能接收到廣播脆丁,因?yàn)槭菍懺趏nCreate方法里的嘛。
// onCreate方法內(nèi)
IntentFilter intentFilter = new IntentFilter();
// 同時(shí)加了三個(gè)action动雹,可以接收到這三種系統(tǒng)廣播
intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);
// scheme必須加
intentFilter.addDataScheme("file");
// 實(shí)例化廣播接收者
SDReceiver sdReceiver = new SDReceiver();
// 注冊(cè)廣播接收器槽卫,接收一個(gè)實(shí)例和intentFilter。匹配action和data
registerReceiver(sdReceiver , intentFilter);
// !!! 注冊(cè)了還必須注銷胰蝠,在onDestroy方法內(nèi)
@Override
protected void onDestroy() {
unregisterReceiver(telReceiver);
super.onDestroy();
}
動(dòng)態(tài)注冊(cè)歼培,必須注銷。而靜態(tài)注冊(cè)姊氓,無(wú)需操心注銷的事丐怯。
動(dòng)態(tài)注冊(cè)的優(yōu)先級(jí)是要高于靜態(tài)注冊(cè)優(yōu)先級(jí)的
然后新建一個(gè)Broadcast Receiver重寫receive方法喷好,判斷狀態(tài)
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ("android.intent.action.MEDIA_MOUNTED".equals(action)) {
Log.d("SD卡", "外置SD卡存在翔横,且已掛載mounted");
} else if ("android.intent.action.MEDIA_UNMOUNTED".equals(action)) {
Log.d("SD卡", "外置SD卡存在,但未掛載unmounted");
} else if (Intent.ACTION_MEDIA_EJECT.equals(action)) {
Log.d("SD卡", "外置SD卡卸載ejected");
} else if (Intent.ACTION_MEDIA_REMOVED.equals(action)) {
Log.d("SD卡", "外置SD卡移除removed");
}
當(dāng)掛載了外置SD卡時(shí)候梗搅,就會(huì)打印外置SD卡存在禾唁,且已掛載mounted
,正常卸載時(shí)候打印外置SD卡存在无切,但未掛載
和外置SD卡卸載ejected
荡短。當(dāng)拔出了SD卡的情況,不僅打印上面兩個(gè)哆键,還會(huì)打印外置SD卡移除removed
掘托。
拓展
現(xiàn)在的手機(jī)一般很少插入了真實(shí)的外置SD卡,我們之前所說的SD卡指的是虛擬的內(nèi)置SD卡籍嘹,又叫做internal storage闪盔,路徑是/mnt/sdcard
或者/storage/emulated/0
又或者/sdcard
,這三個(gè)路徑指向同一個(gè)地方辱士。
Environment.getExternalStorageDirectory().getPath(); // 這里小米真機(jī)打印/storage/emulated/0
泪掀。從Androidkk4.4開始,第三方應(yīng)用程序是無(wú)法訪問(讀/寫)外置SD卡的颂碘;僅僅只有系統(tǒng)級(jí)別的并且使用系統(tǒng)簽名的APP可以訪問外置SD卡异赫。但就我測(cè)試環(huán)境(API25)來(lái)看,是并不影響監(jiān)聽外置SD卡狀態(tài)的头岔。
還可以判斷內(nèi)置SD卡的狀態(tài)塔拳,使用如下方法。當(dāng)然外置SD的拔插峡竣,都不影響內(nèi)置SD卡靠抑。都會(huì)打印mounted
Log.d("SDcard", Environment.getExternalStorageState()); // 打印mounted
Log.d("SDcard", Environment.getExternalStorageDirectory().getPath()); // 打印 /storage/emulated/0
接收(攔截)短信
注冊(cè)
<!-- 申請(qǐng)接收短信的權(quán)限 -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<!-- 省略部分代碼 -->
<receiver
android:name=".SmsReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
申請(qǐng)的權(quán)限記得要?jiǎng)討B(tài)申請(qǐng)。
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.RECEIVE_SMS}, 1);
}
廣播接收器
package com.example.telbroadcastreceivertest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.SmsMessage;
import android.util.Log;
public class SmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 每一個(gè)object為一條短信,實(shí)際返回的是byte[][],一個(gè)pdu對(duì)應(yīng)byte[]
Object[] smss = (Object[]) intent.getExtras().get("pdus");
Log.d("sms", String.valueOf(smss.length));
for (Object sms : smss) {
// API23后澎胡,必須加入format參數(shù)
String format = intent.getStringExtra("format"); // 模擬器孕荠,打印3gpp
Log.d("sms", format);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) sms, format);
// 獲取發(fā)送短信的內(nèi)容
// 功能更強(qiáng)大娩鹉,如果有,可以得到email message body和address
String dispalyBody = smsMessage.getDisplayMessageBody();
String displayAddr = smsMessage.getDisplayOriginatingAddress();
// 純文本的消息體
String body = smsMessage.getMessageBody();
// 獲取發(fā)送者號(hào)碼
String address = smsMessage.getOriginatingAddress();
Log.d("sms", "disBody:"+dispalyBody); // 由于發(fā)送的就是一般的文本短信稚伍,打印內(nèi)容和下面一樣
Log.d("sms", "body:"+body);
Log.d("sms", "disAddr:"+displayAddr); // 打印內(nèi)容和下面一樣
Log.d("sms", "addr:"+address);
}
}
}
}
API23開始弯予,SmsMessage.createFromPdu(byte[] sms);
被棄用。使用SmsMessage.createFromPdu((byte[]) sms, format);
format可以用String format = intent.getStringExtra("format");
獲取个曙。
這個(gè)例子在真機(jī)上(可惡的小米5S)測(cè)試又失敗了锈嫩。估計(jì)有啥流氓軟件給先攔截了逾滥。不過艳悔,在模擬器上測(cè)試成功制跟。
監(jiān)聽?wèi)?yīng)用的安裝葛闷、更新抬驴、卸載
注冊(cè)
<receiver android:name=".APPReceiver">
<intent-filter>
<!-- 安裝應(yīng)用 -->
<action android:name="android.intent.action.PACKAGE_ADDED" />
<!-- 更新已有應(yīng)用 -->
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<!-- 卸載應(yīng)用 -->
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<!-- 攜帶包名揭绑,這個(gè)必須要加上 -->
<data android:scheme="package" />
</intent-filter>
</receiver>
不需要申請(qǐng)啥權(quán)限
廣播接收器
package com.example.telbroadcastreceivertest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
public class APPReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 判斷廣播類型
String action = intent.getAction();
//獲取包名
// intent.getData().toString
String appName = intent.getDataString();
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
Log.i("APPReceiver", "ADD" + appName);
} else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
Log.i("APPReceiver", "REPLACED" + appName);
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Log.i("APPReceiver", "REMOVE" + appName);
}
}
}
應(yīng)用在更新時(shí)乔夯,會(huì)先REMOVED当犯,然后在REPLACED米绕。在本例中瑟捣,自己本身被安裝和卸載,接收不到ADDED和REMOVED廣播栅干。
應(yīng)用未啟動(dòng)過迈套,接收不到廣播
資料來(lái)自張明云的知識(shí)共享
從Android3.1開始,新安裝的程序會(huì)被置于”stopped”狀態(tài)碱鳞,并且只有在至少手動(dòng)啟動(dòng)這個(gè)程序一次后該程序才會(huì)改變狀態(tài)桑李,能夠正常接收到指定的廣播消息。Android這樣做的目的是防止廣播無(wú)意或者不必要地開啟未啟動(dòng)的APP后臺(tái)服務(wù)窿给。也就是說在Android3.1及以上的版本贵白,在未啟動(dòng)的情況下通過應(yīng)用自身完成一些操作是不可能的,但Android提供了一種借助其它應(yīng)用發(fā)送指定Flag廣播的方式填大,達(dá)到應(yīng)用在未啟動(dòng)的情況下仍然能夠收到消息的效果戒洼。
從Android 3.1開始,系統(tǒng)給Intent定義了兩個(gè)新的Flag允华,分別為FLAG_INCLUDE_STOPPED_PACKAGES(表示包含未啟動(dòng)的App)和FLAG_EXCLUDE_STOPPED_PACKAGES(表示不包含未啟動(dòng)的App)圈浇,用來(lái)控制Intent是否要對(duì)處于停止?fàn)顟B(tài)的App起作用,具體的操作方式如下
Intent intent = new Intent(); intent.setAction("com.xxx.xxx.ACTION_XXXX"); // 這句是關(guān)鍵 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); sendBroadcast(intent);
監(jiān)聽開機(jī)廣播
注冊(cè)
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
申請(qǐng)權(quán)限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
廣播接收器
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
}
一些特殊的廣播
比如操作特別頻繁的廣播事件靴寂,屏幕的關(guān)閉和打開 磷蜀,電量的變化等廣播接收器靜態(tài)注冊(cè)無(wú)效
android.intent.action.SCREEN_ON
android.intent.action.SCREEN_OFF
android.intent.action.BATTERY_CHANGED
android.intent.action.CONFIGURATION_CHANGED
android.intent.action.TIME_TICK
因?yàn)檫@些事android的基本事件,如果大多數(shù)程序都監(jiān)聽百炬,會(huì)大大的拖慢整個(gè)系統(tǒng)(占用內(nèi)存等)褐隆,所以android不鼓勵(lì)我們?cè)诔绦蛲顺龅那闆r下監(jiān)聽這些事件。不過還是可以通過在service里面注冊(cè)廣播接受器...來(lái)解決剖踊。
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
screenReceiver = new ScreenReceiver();
registerReceiver(screenReceiver, intentFilter);
別忘了注銷
@Override
protected void onDestroy() {
unregisterReceiver(screenReceiver);
super.onDestroy();
}
監(jiān)聽網(wǎng)絡(luò)狀態(tài)變化
可以靜態(tài)注冊(cè)庶弃,但是已經(jīng)被棄用衫贬。最好動(dòng)態(tài)注冊(cè)。
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkReceiver = new NetworkReceiver();
registerReceiver(networkReceiver, intentFilter);
取消注冊(cè)
@Override
protected void onDestroy() {
unregisterReceiver(screenReceiver);
super.onDestroy();
}
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
// 獲得正在使用的網(wǎng)絡(luò)歇攻,若無(wú)網(wǎng)絡(luò)連接返回null固惯,所以需要判斷
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isAvailable()) {
String typeName = networkInfo.getTypeName(); // 返回MOBILE或WIFI
Toast.makeText(context, typeName + " network is available", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
}
}
記得申請(qǐng)權(quán)限 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
自定義廣播
標(biāo)準(zhǔn)廣播和有序廣播
- 標(biāo)準(zhǔn)廣播:異步執(zhí)行,廣播發(fā)出后缴守,所有廣播接收器幾乎同時(shí)接收到這條廣播葬毫。無(wú)順序可言,效率高但是無(wú)法被截獲屡穗,也無(wú)法修改數(shù)據(jù)贴捡。可想象成開會(huì)時(shí),聽領(lǐng)導(dǎo)講話村砂。
- 有序廣播:同步執(zhí)行烂斋,廣播發(fā)出后,同一時(shí)刻只有一個(gè)廣播接收器能收到箍镜。當(dāng)這個(gè)廣播接收器處理完成之后源祈,可修改數(shù)據(jù)后傳給下一個(gè)接收器煎源,也可以選擇截獲(不繼續(xù)往下傳遞)色迂,顯然這是有順序的。可想象成一個(gè)人給另外一個(gè)人傳話手销,我可以不告訴你呀歇僧。
發(fā)送標(biāo)準(zhǔn)廣播
定義了兩個(gè)廣播接受器,接收同一個(gè)自定義廣播com.example.broadcasttest.MY_BROADCAST
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
<receiver
android:name=".AnotherReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
然后在某個(gè)活動(dòng)中锋拖,匹配了action在發(fā)送廣播就行了诈悍。
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
sendBroadcast(intent);
兩個(gè)廣播接收器
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
}
}
// 另外一個(gè)文件
public class AnotherReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show();
}
}
上面的兩個(gè)接收器都會(huì)接收到這個(gè)廣播。在無(wú)序廣播中兽埃,abortBroadcast()是無(wú)效的侥钳,而且會(huì)在logcat發(fā)出警告。
發(fā)送有序廣播
還是上面的兩個(gè)接收器柄错,不同的是給AnotherReceiver設(shè)置了優(yōu)先級(jí)舷夺。priority可在[-1000,1000]之間設(shè)置,默認(rèn)為0售貌,值越大優(yōu)先級(jí)越高给猾。
<receiver
android:name=".AnotherReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter android:priority="1000">
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
且在AnotherReceiver中阻斷了傳播
public class AnotherReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show();
// 截獲后阻斷傳播
abortBroadcast();
}
}
這樣只有AnotherReceiver能接收打廣播了,由于在此阻斷了傳播颂跨,所以 MyBroadcastReceiver接收不到敢伸。
不過如果這樣發(fā)送MyBroadcastReceiver就又可以接收到了
sendOrderedBroadcast(intent, null, new MyBroadcastReceiver(), null, RESULT_OK, null, null);
注意第三個(gè)參數(shù),是最終接收器恒削,不管有沒有被截獲池颈,最后都會(huì)傳到這個(gè)接收器尾序。所以,上面的截獲對(duì)于MyBroadcastReceiver是無(wú)意義的躯砰,因?yàn)樵诎l(fā)送有序廣播時(shí)指定了這個(gè)接收器為最終接收器蹲诀。而且最終的Receiver無(wú)需注冊(cè)。把上面的MyBroadcastReceiver注冊(cè)代碼刪掉弃揽,也能接收到脯爪。
本地廣播
前面發(fā)送和接收的都是全局廣播,發(fā)出的廣播可以被任何應(yīng)用程序接收到矿微;而且也可以接收來(lái)自其他任何應(yīng)用程序的廣播痕慢。這樣容易引起安全問題。如發(fā)送一些敏感數(shù)據(jù)涌矢,如果被其他軟件截獲掖举,又或者其他程序不停向我們發(fā)送廣播。
使用本地廣播娜庇,使得廣播只能在應(yīng)用程序內(nèi)部傳遞塔次。廣播接收器也只能接收來(lái)自本應(yīng)用程序的廣播。這樣安全性就得到保障名秀。
本地廣播無(wú)法通過靜態(tài)注冊(cè)來(lái)接收励负。因?yàn)殪o態(tài)就是讓程序在未啟動(dòng)的情況下也能收到廣播。但是在發(fā)送本地廣播的時(shí)候匕得,我們的程序肯定是在運(yùn)行的继榆。因此完全不需要使用靜態(tài)注冊(cè).
需要使用到LocalBroadcastManager
mContext = this;
// 先注冊(cè)
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.example.action.LOCAL_BROADCAST");
localReceiver = new LocalReceiver();
// 獲得實(shí)例
localBroadcastManager = LocalBroadcastManager.getInstance(mContext);
// 使用localBroadcastManager的注冊(cè)方法
localBroadcastManager.registerReceiver(localReceiver, intentFilter);
// 再發(fā)送廣播
Intent intent = new Intent("com.example.action.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);
// 省略了部分代碼
@Override
protected void onDestroy() {
// 取消注冊(cè)也要是要localBroadcastManager的方法
localBroadcastManager.unregisterReceiver(localReceiver);
super.onDestroy();
}
LocalReceiver
package com.example.administrator.broadcasttest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class LocalReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "LocalReceiver", Toast.LENGTH_SHORT).show();
}
}
這樣汁掠,這個(gè)廣播接收器只能接受來(lái)自本應(yīng)用的廣播,而且其他程序也不會(huì)接收到這個(gè)廣播了姜凄。
自定義帶權(quán)限的廣播
誰(shuí)可以接收我發(fā)出的廣播?
假設(shè)appA發(fā)出了帶有權(quán)限的廣播云头,那么appB中的廣播接收器想要接收到昏滴,必須申請(qǐng)appA定義的權(quán)限姻几。
appA中自定義權(quán)限
<permission
android:name="com.example.broadcasttest.RECEICVE_ABC"
android:protectionLevel="normal"/>
其中protectionLevel有如下幾種比較常見
normal:默認(rèn)的络拌,普通權(quán)限譬圣。應(yīng)用安裝前,用戶可以看到相應(yīng)的權(quán)限绳姨,但無(wú)需用戶主動(dòng)授權(quán)登澜。
dangerous:危險(xiǎn)權(quán)限,需要?jiǎng)討B(tài)申請(qǐng)飘庄。Android會(huì)彈出對(duì)話框要求用戶進(jìn)行授權(quán)脑蠕。常見的如:網(wǎng)絡(luò)使用權(quán)限,發(fā)送短信權(quán)限、聯(lián)系人信息使用權(quán)限等谴仙。
signature:只有和該apk(定義了這個(gè)權(quán)限的apk)用相同的簽名的應(yīng)用才可以申請(qǐng)?jiān)摍?quán)限迂求。
appA發(fā)送帶有權(quán)限的廣播
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
sendBroadcast(intent, "com.example.broadcasttest.RECEICVE_ABC");
appB中的廣播接收器若想接收這個(gè)廣播,必須申請(qǐng)權(quán)限<uses-permission android:name="com.example.broadcasttest.RECEICVE_ABC"/>
晃跺,沒有申請(qǐng)權(quán)限的一概收不到揩局。
誰(shuí)有權(quán)發(fā)送廣播給我?
比如appB想發(fā)送廣播給appA掀虎,而appA帶有權(quán)限凌盯。
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
sendBroadcast(intent);
appA中自定義的權(quán)限
<permission
android:name="com.example.broadcasttest.SEND_ABC"
android:protectionLevel="normal"/>
且appA的接收器加上權(quán)限
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:permission="com.example.broadcasttest.SEND_ABC" >
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
這樣appB發(fā)送的廣播,想讓appA也收到烹玉。則必須申請(qǐng)appA定義的權(quán)限十气。appB中
<uses-permission android:name="com.example.broadcasttest.SEND_ABC" />
注意:onReceive方法不要進(jìn)行耗時(shí)操作,當(dāng)該方法運(yùn)行時(shí)間過長(zhǎng)還沒結(jié)束春霍,程序就會(huì)報(bào)錯(cuò)砸西。
by @sunhaiyu
2017.5.11