为了容易理解,可以将广播代入到事件模型中,发送广播消息看做是触发event,BroadcastReceiver是处理事件的回调逻辑。

广播这种模型中涉及到两个角色,就是广播的发送者和接收者,所以会涉及到如何发送和如何接收广播。

同时因为系统中可能会有很多的广播,为了不被乱七八糟的东西混淆视听,每个广播给它一个action,这样广播接收器就可以使用action来过滤出自己感兴趣的广播,也可以将action看做是一个频道,每个广播都有一个自己的频道,广播接收器为了不串台就只收听自己感兴趣的一个或多个频道。

广播的分类

按作用域划分

广播按照作用域可以分为全局广播和本地广播。

全局广播的作用域超出此应用程序,发出的全局广播可以被所有应用程序接收,也可以接收其它应用程序发出的全局广播。

 

本地广播就是作用域限定在本应用中,发出的广播只能在应用内部传递,同样也只接收应用内部的本地广播。

这样是因为我们在自己的应用内传递关键信息,如果不限制作用域的话就有可能被其它应用收到,这样很容易引起安全性问题。

按接收顺序划分

按接收顺序分为普通广播(无序广播)和有序广播。

普通广播:使用Context#sendBroadcast(Intent intent)发送,普通广播是异步的(所以又叫无序广播),广播接收者的顺序无法确定,因为是异步的,所以不能够被停止掉,这种方式保证每个广播接收器都能够接收到广播,并且收到的就是原始的广播信息(因为从发送者直接到接收者,中间没有经过其它人)。

 

有序广播:使用Context#sendOrderedBroadcast方法发送,所有要接收此条广播的接收器要排队接收,类似于一条处理链,链上的每个接收器都可以选择从这里终止不再向下传递,所以有序广播是可以被终止的,不保证每个接收器都一定能够接收到广播,同样的,因为链式向后面传递,那么前面的接收者也可以对广播修改后再往下传递,所以此方式除链上的第一个节点外其他接收器收到的数据都有可能被篡改过。另外既然有序广播接收的时候需要排队,那么排队的依据是什么呢,就是在注册的时候intent-filter的android:priority来决定。

粘性广播:使用Context#sendStickyBroadcast发送,粘性广播被发送后,最后一个粘性广播将被粘在系统上,在一段时间内如果有新的广播接收器注册的话那么它将能够接收到这个被粘住的广播,尽管在这个广播被发送的时候它还没有注册,但就是粘了一下收到了。

发送广播

发送全局广播

sendBroadcast()方法第一个参数接收一个Intent,第二个参数是与权限相关的字符串。

发送全局无序广播: Context#sendBroadcast

发送全局有序广播: Context#sendOrderedBroadcast

Intent intent = new Intent("foo.BAR");
sendOrderedBroadcast(intent, null);

发送本地广播

本地广播使用LocalBroadcastManager来管理。

发送本地有序广播:LocalBroadcastManager.getInstance(this).sendBroadcast

发送本地无序广播:LocalBroadcastManager.getInstance(this).sendBroadcastSync

接收广播

接收广播的套路

1. 要接收广播需要创建一个类继承android.content.BroadcastReceiver,并在其onReceive方法中实现对广播事件的处理逻辑

2. 然后将创建的广播接收器注册,注册的方式有静态注册(AndroidManifest.xml)和动态注册(Java代码)两种,如果接收广播需要权限的话还要声明使用相应权限。

3. 然后当有符合条件的广播到来的时候会自动调用广播接收器的onReceive方法

静态注册和动态注册的区别

静态注册:在应用程序关闭后,当有广播来临时仍然能够接收到被调用,应用场景是需要时刻监听广播(即使在应用程序退出后)。

动态注册:短命鬼,广播接收器的生命周期跟随组件在变,应用场景是只在某段时间才需要监听广播。

拦截广播

要在有序广播接收器的处理链上拦截广播,在onReceive方法中调用aboryBroadcast即可拦截广播不再向后传递而是从此处停止。

@Override
public void onReceive(Context context, Intent intent) {
boolean foo = intent.getBooleanExtra("foo", false);
if (foo) {
abortBroadcast();
}
}

本地广播的注册

本地广播只能通过动态注册的方式。因为静态广播主要是为了让应用程序在不启动的时候也能够接收到广播,而本地广播因为都是在应用程序内传递的,所以本地广播都是在应用启动时才有的,所以本地广播不能使用静态注册的方式。

注册本地广播接收器: LocalBroadcastManager.getInstance(this).registerReceiver();

取消注册本地广播接收器:LocalBroadcastManager.getInstance(this).unregisterReceiver();

onReceive的耗时操作

onReceive的执行时间最多只有10秒钟,当超过10秒的时候将会报错,所以不应该在其中执行耗时的方法,正确的方式是启动一个Service执行耗时操作。

静态注册

在AndroidManifest.xml文件中注册广播接收器:

<!-- 静态注册广播接收器 -->
<receiver
android:name=".FooBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="cc11001100.foo" />
</intent-filter>
</receiver>

name: 继承了BroadcastReceiver的广播接收器

enable: 是否启动此接收器

exported:是否允许接收此应用以外的广播,如果为false表示只接收此应用内的广播,即本地广播接收器,否则为全局广播接收器。

intent-filter:增加action过滤广播,其属性priority用于设置此广播接收器的优先级,范围是[-1000, 1000]

action ,系统中会有很多乱七八糟的广播,这个是用来过滤只接收自己需要的广播,intent-filter下可以有多个action

对于静态注册,如果使用的是Android Studio的话,可以通过:

创建的类会继承BroadcastReceiver并且自动在AndroidManifest.xml文件中静态注册。

下面是一个静态注册的例子:

广播接收器:

package cc11001100.androidstudy_005;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast; /**
* @author CC11001100
*/
public class FooBroadcastReceiver extends BroadcastReceiver { @Override
public void onReceive(Context context, Intent intent) {
int value = intent.getIntExtra("foo", 0);
Toast.makeText(context, Integer.toString(value), Toast.LENGTH_LONG).show();
Log.i("FooBroadcastReceiver", "onReceive: " + value);
} }

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cc11001100.androidstudy_005.MainActivity"> <Button
android:id="@+id/sendBroadcastBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Broadcast"
tools:ignore="MissingConstraints"
android:onClick="sendBroadcastBtn"/> </android.support.constraint.ConstraintLayout>

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cc11001100.androidstudy_005"> <application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> <!-- 静态注册广播接收器 -->
<receiver
android:name=".FooBroadcastReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="cc11001100.foo" />
</intent-filter>
</receiver> </application> </manifest>

MainActivity:

package cc11001100.androidstudy_005;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View; import java.util.Random; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getName(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} /**
* 绑定的按钮事件,发送广播事件供接收
*/
public void sendBroadcastBtn(View view) {
Intent intent = new Intent();
intent.setAction("cc11001100.foo");
intent.putExtra("foo", new Random().nextInt());
sendBroadcast(intent);
Log.i(TAG, "sendBroadcastBtn: ");
}
}

前面提到过静态注册的广播接收器即使在应用退出后仍然可以接收广播,那么有没有办法停掉它呢?

PackageManager

动态注册

在程序运行的时候使用Java代码注册,称为动态注册,动态注册要记得在组件的onDestroy中unregisterReceiver广播接收器。

动态注册的步骤:

1. 定义广播接收器类

2. 创建IntentFilter,通过setAction设置所要接收的广播

3. 使用Context#registerReceiver(BroadcastReceiver receiver, IntentFilter filter)方法注册接收器

下面是一个动态注册的例子,广播接收器,对接收到的广播做处理:

package cc11001100.androidstudy_005;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast; /**
* @author CC11001100
*/
public class FooBroadcastReceiver extends BroadcastReceiver { @Override
public void onReceive(Context context, Intent intent) {
int value = intent.getIntExtra("foo", 0);
Toast.makeText(context, Integer.toString(value), Toast.LENGTH_LONG).show();
Log.i("FooBroadcastReceiver", "onReceive: " + value);
} }

布局文件,放一个按钮,每单击一次就发送一个广播供广播接收器接收:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cc11001100.androidstudy_005.MainActivity"> <Button
android:id="@+id/sendBroadcastBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Broadcast"
tools:ignore="MissingConstraints"
android:onClick="sendBroadcastBtn"/> </android.support.constraint.ConstraintLayout>

MainActivity:

package cc11001100.androidstudy_005;

import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View; import java.util.Random; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getName();
private FooBroadcastReceiver fooBroadcastReceiver; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 动态注册广播接收器
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("cc11001100.foo");
fooBroadcastReceiver = new FooBroadcastReceiver();
registerReceiver(fooBroadcastReceiver, intentFilter);
} @Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(fooBroadcastReceiver);
} /**
* 绑定的按钮事件,发送广播事件供接收
*/
public void sendBroadcastBtn(View view) {
Intent intent = new Intent();
intent.setAction("cc11001100.foo");
intent.putExtra("foo", new Random().nextInt());
sendBroadcast(intent);
Log.i(TAG, "sendBroadcastBtn: ");
}
}

.

Android笔记之广播的更多相关文章

  1. Android学习笔记(广播机制)

    1.Android的广播机制介绍 收听收音机也是一种广播,在收音机中有很多个广播电台,每个广播电台播放的内容都不相同.接受广播时广播(发送方)并不在意我们(接收方)接收到广播时如何处理.好比我们收听交 ...

  2. Android学习笔记_19_广播接收者 BroadcastReceiver及其应用_窃听短信_拦截外拨电话

    一.广播接收者类型: 广播被分为两种不同的类型:“普通广播(Normal broadcasts)”和“有序广播(Ordered broadcasts)”. 普通广播是完全异步的,可以在同一时刻(逻辑上 ...

  3. android菜鸟学习笔记26----Android广播消息及BroadcastReceiver

    1.广播类型: Android中的广播有两种类型:标准广播和有序广播.其中,标准广播是完全异步发送的广播,发出之后,几乎所有的广播接收者都会在同一时刻收到这条广播消息,因而,这种类型的广播消息是不可拦 ...

  4. Android笔记(二十六) Android中的广播——BroadcastReceiver

    为了方便进行系统级别的消息通知,Android有一套类似广播的消息机制,每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收自己所关心的广播内容,这些广播可能是来自于系统,也可能是来自于 ...

  5. 我的Android笔记--我对安卓系统的一些了解

    敲了这么长时间代码,记录一下我对Android的一些概念,下面大部分内容来源自网络资料和官方给的文档.     1,Android操作系统的核心属于Linux的一个分支,具有典型的Linux调度和功能 ...

  6. Android开发之广播

    广播是Android开发中的一个重要的功能,在Android里面有各式各样的广播,比如:电池的状态变化.信号的强弱状态.电话的接听和短信的接收等等,现在给大家简单介绍一下系统发送.监听这些广播的机制. ...

  7. android之自定义广播

    布局文件 点击按钮发送广播 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmln ...

  8. Android之旅---广播(BroadCast)

    什么是广播 在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制.我们拿广播电台来做个比方.我们平常使用收音机收音是这样的:许许多多不同的广播电台通过特定的频率来发送他们 ...

  9. Android笔记——Android中数据的存储方式(二)

    我们在实际开发中,有的时候需要储存或者备份比较复杂的数据.这些数据的特点是,内容多.结构大,比如短信备份等.我们知道SharedPreferences和Files(文本文件)储存这种数据会非常的没有效 ...

随机推荐

  1. ThreadLocal 定义,以及是否可能引起的内存泄露(threadlocalMap的Key是弱引用,用线程池有可能泄露)

    ThreadLocal 也可以跟踪一个请求,从接收请求,处理请求,到返回请求,只要线程不销毁,就可以在线程的任何地方,调用这个参数,这是百度二面的题目,参考: Threadlocal 传递参数(百度二 ...

  2. 计算机网络【3】—— IP地址分类与子网划分

    一.IP地址分类

  3. 查看MySQL最近执行的语句

    首先登入MySQL. Reading table information for completion of table and column names You can turn off this ...

  4. VSS2005 上传pdf 空白

    加补丁 VS80-KB943847-X86-INTL.exe

  5. BZOJ3829 [Poi2014]FarmCraft 【树形dp】

    题目链接 BZOJ3829 题解 设\(f[i]\)为从\(i\)父亲进入\(i\)之前开始计时,\(i\)的子树中最晚装好的时间 同时记\(siz[i]\)为节点\(i\)子树大小的两倍,即为从父亲 ...

  6. 解题:USACO18FEB Taming the Herd

    题面 从零开始的DP学习系列之贰(我的DP真的就这么烂TAT) 设DP状态的另一个技巧,考虑题目中有关答案的各种信息 然后这种和结尾有关系的$dp$可以考虑向前找结尾来转移 设$dp[i][j]$表示 ...

  7. 解题:POI 2007 Driving Exam

    题面 有点意思的题 从一个位置$i$出发可以到达每一个位置即是从$1,n$出发可以到达$i$.然后有了一个做法:把图上下反转后建反图,这样就可以求从一个点$i$到达左右两侧的花费$dp[i][0/1] ...

  8. fzyjojP2963 -- [校内训练20161227]疫情控制问题

    (题干中的废话已经划去) dp显而易见 收益为负数的可以直接扔掉不管.不要一定更优 子串问题,考虑SAM 建立广义SAM 尝试匹配,匹配到的位置的parent树祖先如果有完整的串,那么可以从这个串转移 ...

  9. Backbone前端开发流程及规范

    定好View 首先,根据页面切分View,切分View的规则是将重复利用的视图或者功能相近的视图归于一个View,对于Backbone,每一个model都要对应一个View.父层View负责布局,并将 ...

  10. Python之旅:并发编程之多线程理论部分

    一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合 ...