一:广播机制简介
BroadCastReciver:广播接收者,它的作用是用来接受系统和应用发送过来的广播的。
我们先来了解什么是广播:android系统是离不开广播的,比如手机电量改变时系统就会发送一条广播提示用户;当手机开机时系统会发送一条广播,接受到广播就能实现开机启动服务的功能;当没有网络时也会发送一条广播,提示作相应的操作。
接下来我们来了解一下广播的类型, android中的广播可以分为两种类型:标准广播和有序广播
标准广播:
是一种异步执行的广播,广播发出后,所有的广播接收器几乎会在同一时刻接收到这条广播消息,因此它们没有先后顺序可言,效率比较高。但它的缺点是接收者不能将处理结果传递给下一个接受者,并且无法终止BroadCast intent的传播。
标准广播的工作图如下所示:
有序广播:是一种同步执行的广播,这种广播发出后,同一时刻只有一个广播接收器能收到广播消息,当这个广播接收器执行完了之后,广播才会往下传递,因此它是有先后顺序的,那么可以更改它的先后顺序吗?可以 通过以下两种方法更改它的执行顺序:
1:androdManifest<intent-filter.../>元素的android:priority属性中设置它的优先级:取值范围在-1000~1000,数越大优先级越高。
2:也可以调用IntentFilter对象的setPriority()进行设置。
Ordered Broadcast接收者可以终止BroadCast Intent的传播,Broadcastt Intent的传播一旦终止,后面的接收器就无法接收到BroadCast
有序广播的工作图如下所示:
二 接收系统广播
android内置了很多系统级别的广播,我们可以在应用中监听这些广播得到各种系统的状态信息。
如果想要监听广播,就需要使用广播接收器,广播接收器可以自由地对自己感兴趣的广播进行注册。注册广播的方式有两种,
1:在代码中注册
在代码中注册页叫做动态注册
下面我们就通过动态注册来写一个监听网络变化的程序
package broadcastreceiver.csdn.com.aty;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.widget.Toast;
import org.xutils.x;
import broadcastreceiver.csdn.com.R;
/**
* 动态注册广播监听器
*
* 监听网络变化
* Created by Administrator on 2017/5/30.
*/
public class DynamicAty extends Activity{
private IntentFilter intentDynamic;
private NetworkChangeBR netWorkChangeBR;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aty_dynnamic);
x.view().inject(this);
intentBR();//注册广播
}
/**
* 注册广播
*/
private void intentBR() {
/**
* 创接IntentFilter实例
*IntentFilter对象负责过滤掉组件无法响应和处理的Intent,只将自己关心的Intent接收进来进行处理
*/
intentDynamic=new IntentFilter();
/**
* 当网络发生改变时系统会发出一条android.net.conn.CONNECTIVITY_CHANGE广播
*
* 我们的广播想要监听什么就在这写什么
*/
intentDynamic.addAction("android.net.conn.CONNECTIVITY_CHANGE");
/**
* 创建NetworkChangeReceiver实例
*/
netWorkChangeBR=new NetworkChangeBR();
/**
* registerReceiver进行注册
*/
registerReceiver(netWorkChangeBR,intentDynamic);
}
/**
* 监听网络变化
*
* 建立一个内部类让它继承自BroadcastReceiver
*/
class NetworkChangeBR extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
/**
* 这是一个系统服务类,专门用来管理网络的
*/
ConnectivityManager connectivityManager= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
/**
* 得到NetworkInfo的实例
*/
NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo();
if(networkInfo!=null && networkInfo.isAvailable()){
Toast.makeText(DynamicAty.this,"网络打开了",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(DynamicAty.this,"网络关闭了",Toast.LENGTH_SHORT).show();
}
}
}
/**
* 动态注册的广播接收器一定要取消
*
* 取消广播接收器
*
*/
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(netWorkChangeBR);
}
}
这里有非常重要的一点要说明,如果程序涉及一些对用户敏感的操作,就必须在配置文件中声明权限。
android:Manifest中加入以下代码
允许用户访问GSM网络信息
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
2:AndroidManifest.xml中注册
在AndraoidManifest中注册也叫做静态注册
下面我们通过静态注册编写一个能够监听网络变化的应用吧。
右击创建应用的包->New->Other->Broadcast Receiver 会弹出下图所示的窗口
Class Name:输入广播的名字
Exported:是否允许这个广播接收器接收本程序以外的广播
Enabled属性是否启用这个广播接受器
构选这两个属性,点击Finish,广播接收器创建完成了,以下是自动生成的代码
package broadcastreceiver.csdn.com.aty;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class QuintReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
throw new UnsupportedOperationException("Not yet implemented");
}
}
android Studio注册这一步也帮我们一起完成了,下面是Android Manifest里的代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="broadcastreceiver.csdn.com">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<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=".aty.MainAty">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".aty.QuintAty" />
<activity android:name=".aty.DynamicAty" />
<receiver
android:name=".aty.QuintReceiver"
android:enabled="true"
android:exported="true"></receiver>
</application>
</manifest>
不过现在还是不能接收广播的,下面我们对代码进行修改
下面是修改后的AndroidMainifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="broadcastreceiver.csdn.com">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<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=".aty.MainAty">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".aty.QuintAty" />
<activity android:name=".aty.DynamicAty" />
<receiver
android:name=".aty.QuintReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
</application>
</manifest>
可以看到我们在两个地方修改了
1、<intent-filter>标签里加了监听系统开机广播action
2、 加了一条开机广播的权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
接下来我们在QuintReceiver加了一条Toast
package broadcastreceiver.csdn.com.aty;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/**
* 静态注册广播接收器
*/
public class QuintReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context,"开机了",Toast.LENGTH_SHORT).show();
}
}
好了静态注册也完成了
三 发送自定义广播
现在我们已经学会了通过广播接收系统广播,接下来我们来学习如何在应用程序中发送自定义广播。
1:发送标准广播
下面是发送广播的代码
MainAty.class
package broadcastreceiver.csdn.com.aty;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.xutils.view.annotation.Event;
import org.xutils.view.annotation.ViewInject;
import org.xutils.x;
import broadcastreceiver.csdn.com.R;
public class MainAty extends AppCompatActivity {
@Event(value ={R.id.bn_standard,R.id.bn_dynamic})
private void onClick(View v){
switch (v.getId()){
case R.id.bn_dynamic:
//动态注册
Intent intentDynamic=new Intent(MainAty.this,DynamicAty.class);
startActivity(intentDynamic);
break;
case R.id.bn_standard:
//发送标准广播
Intent intentStandard=new Intent("com.csdn.broadcastreceiver.MY_RECEIVER");
sendBroadcast(intentStandard);
break;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aty_main);
x.view().inject(this);//xutils3初始化
}
}
布局
aty_main.xml
<?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:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="81dp">
<Button
android:id="@+id/bn_dynamic"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="动态注册"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/bn_standard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="发送标准广播"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bn_dynamic" />
</android.support.constraint.ConstraintLayout>
接收广播的代码
package broadcastreceiver.csdn.com.Receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/**
* 接收自定义广播
*/
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"标准广播收到了",Toast.LENGTH_SHORT).show();
}
}
AndroidManifest.xml注册
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="broadcastreceiver.csdn.com">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<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=".aty.MainAty">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".aty.QuintAty" />
<activity android:name=".aty.DynamicAty" />
<receiver
android:name=".Receiver.QuintReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".Receiver.MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.csdn.broadcastreceiver.MY_RECEIVER"/>
</intent-filter>
</receiver>
</application>
</manifest>
思考一个问题,其他程序是否也可以接收该广播呢?我们新建Demo2的项目
Demo2接收广播的代码
package broadcastreceiver.csdn.com.demo2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class Demo2Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"Demo2也收到了标准广播",Toast.LENGTH_SHORT).show();
}
}
AndroidManifest.xml配置代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="broadcastreceiver.csdn.com.demo2">
<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=".MainAty">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".Demo2Receiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.csdn.broadcastreceiver.MY_RECEIVER"/>
</intent-filter>
</receiver>
</application>
</manifest>
2:发送有序广播
发送有序广播很简单
package broadcastreceiver.csdn.com.aty;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.xutils.view.annotation.Event;
import org.xutils.view.annotation.ViewInject;
import org.xutils.x;
import broadcastreceiver.csdn.com.R;
public class MainAty extends AppCompatActivity {
@Event(value ={R.id.bn_standard,R.id.bn_dynamic})
private void onClick(View v){
switch (v.getId()){
case R.id.bn_dynamic:
//动态注册
Intent intentDynamic=new Intent(MainAty.this,DynamicAty.class);
startActivity(intentDynamic);
break;
case R.id.bn_standard:
//发送标准广播
Intent intentStandard=new Intent("com.csdn.broadcastreceiver.MY_RECEIVER");
// sendBroadcast(intentStandard);//发送标准广播
sendOrderedBroadcast(intentStandard,null);//发送有序广播
break;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aty_main);
x.view().inject(this);//xutils3初始化
}
}
只要把
sendBroadcast(intentStandard);//发送标准广播
换成
sendOrderedBroadcast(intentStandard,null);//发送有序广播
接下来我们来修改它的优先级
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="broadcastreceiver.csdn.com.demo2">
<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=".MainAty">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".Demo2Receiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.csdn.broadcastreceiver.MY_RECEIVER"/>
</intent-filter>
</receiver>
</application>
</manifest>
我们通过android:priority属性给广播接收器设置了优先级,优先级高的先运行
学习了接收广播的优先级,我们再学一个方法,abortBroadcast(),截断广播,优先级高德广播截断后,优先级低的广播就无法收到了。
package broadcastreceiver.csdn.com.demo2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class Demo2Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"Demo2也收到了标准广播",Toast.LENGTH_SHORT).show();
abortBroadcast();//将这条广播截断
}
}
四 本地广播
有的时候我们发出的广播只要求本地能够接受,这个时候就要用到本地广播,本地广播的特点是发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本地应用程序发出的广播。
下面我们看看代码:
LocalityAty.class
package broadcastreceiver.csdn.com.aty;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.view.View;
import android.widget.Toast;
import org.xutils.view.annotation.Event;
import org.xutils.x;
import broadcastreceiver.csdn.com.R;
/**
* 本地广播
* Created by Administrator on 2017/5/30.
*/
public class LocalityAty extends Activity{
private String LOCALITY_BROADCAST="com.csdn.broadcastreceiver.LOCALITY_BROADCAST";
private IntentFilter intentFilter;
private LocalityReceiver localityReceiver;
/**
* 本地广播需要使用LocalBroadcastManager对广播进行管理
*/
private LocalBroadcastManager localBroadcastManager;
@Event(value = {R.id.bn_locality})
private void onClick(View v){
switch (v.getId()){
case R.id.bn_locality:
//发送本地广播
Intent intent=new Intent(LOCALITY_BROADCAST);
localBroadcastManager.sendBroadcast(intent);
break;
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aty_locality);
x.view().inject(this);
localBroadcastManager=LocalBroadcastManager.getInstance(this);//获取实例
intentFilter =new IntentFilter();
intentFilter.addAction(LOCALITY_BROADCAST);
localityReceiver=new LocalityReceiver();
localBroadcastManager.registerReceiver(localityReceiver,intentFilter);//注册本地广播监听器
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localityReceiver);
}
class LocalityReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(LocalityAty.this,"收到了本地广播",Toast.LENGTH_SHORT).show();
}
}
}
aty_locality.xml
<?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">
<Button
android:id="@+id/bn_locality"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="260dp"
android:text="本地广播"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="0.527" />
</android.support.constraint.ConstraintLayout>
下面我们来总结一下本地广播的特点吧:
1.本地广播无法通功静态注册
2.本地广播更安全,因为发出的广播不用担心被其他应用监听到,
3.本地广播比全局广播高效
还有一点要说明的是广播接收器里不能进行耗时操作,不能开启多线程