Android Broadcast详解

在 Android 中使用 Activity, Service, Broadcast, BroadcastReceiver
   活动(Activity) - 用于表现功能
   服务(Service) - 相当于后台运行的 Activity
   广播(Broadcast) - 用于发送广播
   广播接收器(BroadcastReceiver) - 用于接收广播
   Intent - 用于连接以上各个组件,并在其间传递消息

BroadcastReceiver是一种全局的监听器,用于监听全局广播消息,可以方便实现不同组件之间的通信。其用于接收广播通知信息,并做出对应处理的组件。很多广播是源自于系统代码的 ── 比如,通知时区改变、电池电量低、拍摄一张照片或者用户改变了语言选项。应用程序也可以进行广播 ── 比如说,通知其它应用程序一些数据下载完成并处于可用状态。 应用程序可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。 广播接收器没有用户界面。然而,其可以启动一个Activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,如闪动背灯、震动、播放声音等等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。


Broadcast是一种广泛运用的在应用程序之间传输信息的机制, 而BroadcastReceiver是对发送出来的 Broadcast进行过滤接受并响应的一类组件。
广播接收者(BroadcastReceiver)用于接收广播Intent,广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收,这个特性跟JMS中的Topic消息接收者类似,要实现一个广播接收者方法如下:

(1)继承 BroadcastReceiver类,并重写onReceive()方法
public class IncomingSMSReceiver extends BroadcastReceiver { 
    @Override public void onReceive(Context context, Intent intent) {

       
    } 
}


(2)注册广播的方法有两种

    1>使用代码进行订阅
  IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED"); 
  IncomingSMSReceiver receiver = new IncomingSMSReceiver(); 
  registerReceiver(receiver, filter);
注:一般在onStart中注册,onStop中取消unregisterReceiver 


    2>在AndroidManifest.xml文件中的<application>节点里进行订阅: 

<receiver android:name=".IncomingSMSReceiver">

    <intent-filter>

         <action android:name="android.provider.Telephony.SMS_RECEIVED"/>

    </intent-filter>

</receiver>


滤接收的过程: 
    首先在需要发送信息的地方,把要发送的信息和用于过滤的信息(如Action、Category)装入一个Intent对象,然后通过调用 Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把 Intent对象以广播方式发送出去
    当Intent发送以后,所有已经注册的BroadcastReceiver会检查注册时的IntentFilter是否与发送的Intent相匹配,若 匹配则就会调用BroadcastReceiver的onReceive()方法。所以当我们定义一个BroadcastReceiver的时候,都需要 实现onReceive()方法

广播被分为两种不同的类型

  (1)普通广播(Normal broadcasts):完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播
  (2)粘性广播(Sticky broadcast):该种发送并经过AMS(ActivityManagerService)分发给对应的receiver后,这个broadcast并不会被丢弃,而是保存在AMS中,当有新的需要动态注册的receiver请求AMS注册时,如果这个receiver能够接收这个broadcast,那么AMS会将在receiver注册成功之后,马上向receiver发送这个broadcast。使用sendStickyBroadcast()来发送该种属性广播,使用这个api需要权限 android.Manifest.permission.BROADCAST_STICKY,粘性广播的特点是Intent会一直保留到广播事件结束,而这种广播也没有所谓的10秒限制,10秒限制是指普通的广播如果onReceive方法执行时间太长,超过10秒的时候系统会将这个广播置为可以干掉的candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行
   (3)有序广播(Ordered broadcasts):按照接收者声明的优先级别,被接收者依次接收广播。如:A的级别高于B,B的级别高于C,那么,广播先传给A,再传给B,最后传给C 。优先级别声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000,优先级别也可以调用IntentFilter对象的setPriority()进行设置。有序广播的接收者可以终止广播Intent的传播,广播Intent的传播一旦终止,后面的接收者就无法接收到广播。前面的接收者可以对广播的内容进行修改,修改的结果被后面接收者接收,优先级高的接收者还可以结束这个广播
 
注:有序广播的接收者可以将数据传递给下一个接收者,如:A得到广播后,可以往它的结果对象中存入数据,当广播传给B时,B可以从A的结果对象中得到A存入的数据 


发送广播消息:extends Service
    指定广播目标Action:Intent Intent = new Intent(action-String) 
    指定了此action的receiver会接收此广播 
    需传递参数(可选) putExtra(); 
    发送:sendBroadcast(Intent);


(1)Context.sendBroadcast():发送的是普通广播,所有订阅者都有机会获得并进行处理
(2)Context.sendOrderedBroadcast():发送的是有序广播,系统会根据接收者声明的优先级别按顺序逐个执行接收者

(3) BroadcastReceiver.abortBroadcast():可用于终止有序广播,如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。过
(4) setResultExtras(Bundle bundle):用于存放数据,对于有序广播,前面的接收者可以将数据通该方法存放进结果对象,然后传给下一个接收者,下一个接收者通过代码: Bundle bundle = getResultExtras(true))可以获取上一个接收者存入在结果对象中的数据
注:1> Intent setPackage(String packageName):设置接收广播App的包名
       2> abstract void sendBroadcast(Intent intent, String receiverPermission):设置保护权限,可为一下四个值之一
                           normal:默认值。低风险权限,只要申请了就可以使用,安装时不需要用户确认
                           dangerous:像WRITE_SETTING和SEND_SMS等权限是有风险的,因为这些权限能够用来重新配置设备或者导致话费。使用此protectionLevel来标识用户可能关注的一些权限。Android将会在安装程序时,警示用户关于这些权限的需求,具体的行为可能依据Android版本或者所安装的移动设备而有所变化
                           signature:这些权限仅授予那些和本程序应用了相同密钥来签名的程序
                           signatureOrSystem:与signature类似,除了一点,系统中的程序也需要有资格来访问。这样允许定制Android系统应用也能获得权限,这种保护等级有助于集成系统编译过程
        设置这两个可用于保护传递信息的安全性

广播接收器的分类
1> 私有广播接收器:只接收app自身发出的广播
2> 公共广播接收器:能接收所有app发出的广播
3> 内部广播接收器:只接收内部app发出的广播
intent-filter节点与exported属性设置组合建议

(1)私有广播接收器设置exported='false',并且不配置intent-filter。(私有广播接收器依然能接收到同UID的广播)
<receiver android:name=".PrivateReceiver" android:exported="false" />
(2)对接收来的广播进行验证
(3)内部app之间的广播使用protectionLevel='signature'验证其是否真是内部app
(4)返回结果时需注意接收app是否会泄露信息
(5)发送的广播包含敏感信息时需指定广播接收器,使用显示意图或者  setPackage(String packageName)
(6)sticky broadcast粘性广播中不应包含敏感信息
(7)Ordered Broadcast建议设置接收权限receiverPermission,避免恶意应用设置高优先级抢收此广播后并执行abortBroadcast()方法
注:使用 LocalBroadcastManager.sendBroadcast() 发出的广播只能被app自身广播接收器接收
           Intent intent = new Intent("my-sensitive-event");
           intent.putExtra("event", "this is a test event");
           LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

系统收到短信,发出的广播属于有序广播。如果想阻止用户收到短信,可以通过设置优先级,让你们自定义的接收者先获取到广播,然后终止广播,这样用户就接收不到短信了

如:

public class IncomingSMSReceiver extends BroadcastReceiver { 

private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";

@Override 

public void onReceive(Context context, Intent intent) {

if (intent.getAction().equals(SMS_RECEIVED)) {

    SmsManager sms = SmsManager.getDefault();

    Bundle bundle = intent.getExtras();

    if (bundle != null) {

    Object[] pdus = (Object[]) bundle.get("pdus");

    SmsMessage[] messages = new SmsMessage[pdus.length];

    for(int i = 0; i < pdus.length; i++) 

         messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);

    for(SmsMessage message : messages){

         String msg = message.getMessageBody();

         String to = message.getOriginatingAddress();

         sms.sendTextMessage(to, null, msg, null, null);

    }

    }

}

}

}


在AndroidManifest.xml文件中的<application>节点里对接收到短信的广播Intent进行订阅: 
<receiver android:name=".IncomingSMSReceiver"> 

<intent-filter>

    <action android:name="android.provider.Telephony.SMS_RECEIVED"/>

</intent-filter>

</receiver>
在AndroidManifest.xml文件中添加以下权限: 
<uses-permission android:name="android.permission.RECEIVE_SMS"/><!-- 接收短信权限 --> 
<uses-permission android:name="android.permission.SEND_SMS"/><!-- 发送短信权限 -->

 

广播接收者的响应

在Android中,每次广播消息到来时都会创建BroadcastReceiver实例并执行onReceive() 方法, onReceive() 方法执行完后,BroadcastReceiver 的实例就会被销毁。

注:当  onReceive()  方法在10秒内没有执行完毕,Android会认为该程序无响应。所以在  BroadcastReceiver  里不能做一些比较耗时的操作,否侧会弹出ANR(Application No Response)的对话框。如果需要完成一项比较耗时的工作,应该通过发送  Intent  给  Service ,由  Service  来完成。这里不能使用子线程来解决,因为  BroadcastReceiver  的生命周期很短,子线程可能还没有结束  BroadcastReceiver  就先结束了。 BroadcastReceiver  一旦结束,此时  BroadcastReceiver  的所在进程很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。如果它的宿主进程被杀死,那么正在工作的子线程也会被杀死。所以采用子线程来解决是不可靠的

如:
public class IncomingSMSReceiver extends BroadcastReceiver { 
    @Override 

    public void onReceive(Context context, Intent intent) { 
            //发送Intent启动服务,由服务来完成比较耗时的操作
            Intent service = new Intent(context, XxxService.class); 
            context.startService(service); 
    }
}


广播接收者

除了短信到来广播Intent,Android还有很多广播Intent,如:开机启动、电池电量变化、时间已经改变等广播Intent。 
(1)接收电池电量变化广播Intent

在 AndroidManifest.xml 文件中的<application>节点里订阅此Intent
<receiver android:name=".IncomingSMSReceiver"> 
    <intent-filter> 
         <action android:name="android.intent.action.BATTERY_CHANGED"/> 
    </intent-filter> 
</receiver> 

(2)接收开机启动广播Intent

在 AndroidManifest.xml 文件中的<application>节点里订阅此Intent
<receiver android:name=".IncomingSMSReceiver"> 
    <intent-filter> 
         <action android:name="android.intent.action.BOOT_COMPLETED"/> 
    </intent-filter> 
</receiver> 
并且要进行权限声明: 
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> 


(3)接收短信广播Intent

在 AndroidManifest.xml 文件中的<application>节点里订阅此Intent

<receiver android:name=".IncomingSMSReceiver"> 

<intent-filter>

    <action android:name="android.provider.Telephony.SMS_RECEIVED"/>

</intent-filter>

</receiver> 
在 AndroidManifest.xml 文件中添加以下权限
<uses-permission android:name="android.permission.RECEIVE_SMS"/><!-- 接收短信权限 --> 
<uses-permission android:name="android.permission.SEND_SMS"/><!-- 发送短信权限 --> 


无序广播的发送和接收实例
 1>创建广播接收函数需要继承于BroadcastReceiver 

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.util.Log;

import android.widget.Toast;

 

public class MyReceiver extends BroadcastReceiver {

 @Override

 public void onReceive(Context context, Intent intent) {

  Log.i("MyReceiver.onReceive""进入广播处理函数! Action = " + intent.getAction());

  if("HEHE.HAHA".equals(intent.getAction())) {

   Toast.makeText(context, "收到的Action为:" + intent.getAction() + "\n收的到内容是:" 

       + intent.getStringExtra("msg"), Toast.LENGTH_SHORT) .show();

   Log.i("MyReceiver.onReceive""进入广播处理函数的里面!");

  }

 }

}

 

2>在<application>中注册BroadcastReceiver

<!-- 注册BroadcastReciver -->

<receiver android:name="com.example.mybroadcast.MyReceiver" >

<intent-filter>

<action android:name="HEHE.HAHA" />

</intent-filter>

</receiver>


3>发送广播

import android.os.Bundle;

import android.app.Activity;

import android.content.Intent;

import android.util.Log;

import android.view.Menu;

import android.view.View;

import android.widget.Button;

 

public class MainActivity extends Activity {

private Button button;

 @Override

 protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_main);

 }

//按钮处理方法

public void onReceive(View view) {

  Log.i("MainActivity.onReceive""进入onReceive");

  Intent intent = new Intent();

  intent.setAction("HEHE.HAHA");

  intent.putExtra("msg""简单的消息!");

  // 发送广播

  sendBroadcast(intent);

}

}  
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值