Android中的廣播

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)限的廣播

詳見WJ_S的CSDN

誰(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市址儒,隨后出現(xiàn)的幾起案子芹枷,更是在濱河造成了極大的恐慌,老刑警劉巖莲趣,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸳慈,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡喧伞,警方通過查閱死者的電腦和手機(jī)走芋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)潘鲫,“玉大人翁逞,你說我怎么就攤上這事「嚷兀” “怎么了挖函?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)浊竟。 經(jīng)常有香客問我怨喘,道長(zhǎng),這世上最難降的妖魔是什么振定? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任必怜,我火速辦了婚禮,結(jié)果婚禮上后频,老公的妹妹穿的比我還像新娘梳庆。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布靠益。 她就那樣靜靜地躺著丧肴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胧后。 梳的紋絲不亂的頭發(fā)上芋浮,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音壳快,去河邊找鬼纸巷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛眶痰,可吹牛的內(nèi)容都是我干的瘤旨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竖伯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼存哲!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起七婴,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤祟偷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后打厘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體修肠,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年户盯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嵌施。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡莽鸭,死狀恐怖吗伤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蒋川,我是刑警寧澤牲芋,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站捺球,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏夕冲。R本人自食惡果不足惜氮兵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望歹鱼。 院中可真熱鬧泣栈,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至疼进,卻和暖如春薪缆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伞广。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工拣帽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嚼锄。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓减拭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親区丑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拧粪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.廣播的分類 (1)按照發(fā)送的方式分類 標(biāo)準(zhǔn)廣播是一種異步的方式來(lái)進(jìn)行傳播的,廣播發(fā)出去之后沧侥,所有的廣播接收者幾...
    曹豐斌閱讀 34,144評(píng)論 0 22
  • android中有很多需要數(shù)據(jù)交互的部分可霎,交互的方式也有很多種不同,四大組件各有各的方法正什, 今天主要是談?wù)勱P(guān)于回調(diào)...
    sososun閱讀 6,399評(píng)論 0 8
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,129評(píng)論 25 707
  • 1.什么是Activity?問的不太多婴氮,說點(diǎn)有深度的 四大組件之一,一般的,一個(gè)用戶交互界面對(duì)應(yīng)一個(gè)activit...
    JoonyLee閱讀 5,732評(píng)論 2 51
  • 玉米熬糖 年輕時(shí)斯棒,曾有過許多的理想。且不論是否能夠?qū)崿F(xiàn)主经,只要有想法并付諸于行動(dòng)荣暮,也能算是有志之士吧。 貧困的家庭環(huán)...
    龍井溝的記憶閱讀 485評(píng)論 4 1