【攻克Android (33)】四大组件之 BroadcastReceiver

[b][size=large]本文围绕以下三个部分展开:[/size][/b]

[b][size=large]一、广播[/size][/b]
[b][size=large]案例一:系统广播(属于普通广播):手机电池电量小于15%时进行提醒[/size][/b]
[b][size=large]案例二:自定义普通广播[/size][/b]


[b][size=large]一、广播[/size][/b]

[size=medium][b]1. 概念:[/b][/size]

[size=medium]广播(broadcasting)是多点投递的最普遍的形式,它向每一个目的站投递一个分组的拷贝。它可以通过多个单次分组的投递完成,也可以通过单独的连接传递分组的拷贝,直到每个接收方均收到一个拷贝为止。[/size]

[size=medium]每个广播电台播放的内容都不相同。接受广播时广播(发送方)并不在意我们(接收方)接收到广播时如何处理。好比我们收听交通电台的广播,电台中告诉我们现在在交通状况如何,但它并不关心我们接收到广播时做如何做出处理,这不是广播应该关心的问题。[/size]

[size=medium][b]2. Android 广播的优势:[/b][/size]

[size=medium](1)扩展了Android中的事件模型,提高了应用程序的可扩展性。[/size]

[size=medium](2)方便了不同应用程序共享数据。[/size]

[size=medium](3)提高了应用程序的运行效率。[/size]

[size=medium][b]3. BroadcastReceiver:[/b][/size]

[size=medium]BroadcastReceiver是Android整个系统中通用的发布/订阅机制(更确切地说,Observer模式)的实现。意思是接收者(Receiver)订阅一些事件,在事件发生时做出一定响应。[/size]

[size=medium]系统自身时时刻刻广播着一些事件。比如收到短信、来了一个电话、电量不足或者系统启动等事件发生的时候,它们都是通过广播传递给每个接收者。[/size]

[size=medium]广播可以在不同的应用之间传递;也可以在同一应用的不同Activity之间传递;还可以在应用与服务之间传递。[/size]

[size=medium]Broadcast Receriver并无任何可见的界面(不同于Activity),也并非常驻于内存中执行(不同于Service)。它只会在事件发生时执行一段代码,做些启动一个Activity/Service之类的操作。[/size]

[size=medium]每次广播消息到来时都会创建BroadcastReceiver实例并执行onReceive() 方法。[/size]

[size=medium]广播接收者(BroadcastReceiver)用于接收广播Intent,通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收。[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0111/0171/ed250723-c553-35b1-a4fa-a82de2fd11b8.png[/img][/align]

[size=medium][b]4. 广播的分类。[/b][/size]

[size=medium][b](1)[/b]系统广播、自定义广播。[/size]

[size=medium][b]A.[/b] 系统内部已经定义了很多广播消息类型,例如电池电量低、屏幕开启或者关闭、系统引导完成等。[/size]

[size=medium]系统内部广播这些消息使用的是 sendBroadcast()。[/size]

[size=medium]当系统发送这些广播后,同样经过 Intent 匹配找到相应的 Receiver 对象并启动。这与 Activity 或者 Service 一样。[/size]

[size=medium]Receiver对象接收的消息本质上是Intent。[/size]

[size=medium]多数应用中,Receiver接收的是系统发出的消息。[/size]

[size=medium][b]附:系统广播可以捕捉系统发出的行为有:[/b][/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0111/0328/6efd352c-a7ef-32ed-b1b9-a68318723a10.png[/img][/align]

[size=medium][b]B.[/b] 自定义广播包括自定义普通广播、自定义有序广播。[/size]

[size=medium]自定义广播无非是给Intent对象的Action字段赋予自定义的值而已,不能与系统内部的消息名称重复,并在Receiver对象的intent-filter中使用相同的Action值进行匹配。一般自定义广播命名时,可以使用本程序包名作为前缀,以免与其它程序定义的广播发生命名冲突。[/size]

[size=medium][b](2)[/b]普通广播(Normal broadcasts)、有序广播(Ordered broadcasts)。[/size]

[size=medium][b]A. [/b]前者是完全异步的,所有接收者(逻辑上)都在同一时刻运行,对消息传递的效率而言这是很好的做法。[/size]
[size=medium]但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播。[/size]
[size=medium]然而后者是逐个执行接收者:系统会按照接收者声明的优先级别
(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置),按顺序逐次执行。[/size]

[size=medium][b]B.[/b] 发送广播:[/size]

[size=medium][b]Context.sendBroadcast()[/b]:[/size]

[size=medium]发送的是普通广播,所有订阅者都有机会获得并进行处理。[/size]

[size=medium][b]Context.sendOrderedBroadcast()[/b]:[/size]

[size=medium]发送的是有序广播,系统会根据接收者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast())。如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将处理结果存放进广播Intent,然后传给下一个接收者。[/size]

[size=medium][b](3)[/b]静态广播、动态广播。[/size]

[size=medium]静态广播:使用静态注册方式注册的广播。[/size]

[size=medium]动态广播:使用动态注册方式注册的广播。[/size]

[size=medium][b]5. 广播的实现。[/b][/size]

[size=medium][b](1)继承BroadcastReceiver,并重写onReceive()方法。[/b][/size]

[size=medium][b](2)注册BroadcastReceiver。[/b][/size]

[size=medium]注册的方法有两种:[/size]

[size=medium][b]1)[/b]静态注册(在功能清单文件中的<application>节点里进行注册)。[/size]

<receiver android:name=".IncomingSMSReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>


[size=medium][b]2)[/b]动态注册(使用代码进行进行注册)。[/size]

IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
IncomingSMSReceiver receiver = new IncomingSMSReceiver();
registerReceiver(receiver, filter);


[size=medium][b]3)[/b]两种注册方式的比较:[/size]

[size=medium][b]A. [/b]动态注册的广播 永远要快于 静态注册的广播。不管静态注册的优先级设置的多高,不管动态注册的优先级有多低。[/size]

[size=medium][b]B. [/b]动态注册广播不是 常驻型广播 ,也就是说广播跟随activity的生命周期。注意: 在activity结束前,移除广播接收器。[/size]
[size=medium]静态注册是常驻型 ,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。[/size]

[size=medium][b]C. [/b]在同一个优先级下,谁先启动的快,谁将先接收到广播。[/size]

[size=medium][b]6. 处理耗时任务。[/b][/size]

[size=medium]在Android中,程序的响应(Responsive)被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视。当BroadcastReceiver在10秒内没有执行完毕,Android会认为该程序无响应。所以在BroadcastReceiver里不能做一些比较耗时的操作,否侧会弹出ANR(Application No Response)的对话框。[/size]

[size=medium]如果需要完成一项比较耗时的工作,应该通过发送Intent给Service,由Service来完成,而不是使用子线程的方法来解决。因为BroadcastReceiver的生命周期很短(在onReceive() 执行后BroadcastReceiver 的实例就会被销毁),子线程可能还没有结束它就先结束了。当然如果BroadcastReceiver结束了,它的宿主进程还在运行,子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。[/size]

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



[b][size=large]案例一:系统广播(属于普通广播):手机电池电量小于15%时进行提醒[/size][/b]

[align=center][img]http://dl2.iteye.com/upload/attachment/0111/0190/6ed09170-768e-325a-b596-58719909f008.png[/img][/align]

[align=center][img]http://dl2.iteye.com/upload/attachment/0111/0192/281e3b82-0045-32a2-85cd-1cd5ac3d5d0a.png[/img][/align]

[align=center][img]http://dl2.iteye.com/upload/attachment/0111/0256/c88a0007-7258-3873-9ff4-bf2552100c59.png[/img][/align]

[size=medium][b]1. activity_main.xml。写一个 TextView,用于显示当前电池电量。[/b][/size]

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/tvBatteryChanged"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textSize="30sp" />

</RelativeLayout>


[size=medium][b]2. MainActivity。声明和在onCreate()中初始化 TextView。[/b][/size]

    // 1.1 声明变量(显示电池电量的文本)
private TextView tvBatteryChanged;


        // 1.2 初始化变量(显示电池电量的文本)
tvBatteryChanged = (TextView) findViewById(R.id.tvBatteryChanged);


[size=medium][b]3. MainActivity。【一、定义广播】定义匿名的广播接收者(接收手机电量不足的通知)。[/b][/size]

    // 2. 定义匿名的广播接收者(接收手机电量不足的通知)
private BroadcastReceiver batteryChangedReceiver = new BroadcastReceiver() {
/**
* 在 BroadcastReceiver 接收到与之匹配的广播消息后,onReceive()方法会被调用
*
* onReceive()方法必须要在5秒钟执行完毕,
* 否则 Android 系统会认为该组件失去响应,并提示用户强行关闭该组件
*
* @param context 设置BroadcastReceiver实例
* @param intent Receiver对象接收的消息
*/
@Override
public void onReceive(Context context, Intent intent) {

}
};


[size=medium][b]4. MainActivity。【二、注册广播】在onCreate()中注册电量不足的广播接收者。[/b][/size]

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 1.2 初始化变量(显示电池电量的文本)
tvBatteryChanged = (TextView) findViewById(R.id.tvBatteryChanged);

// 3. 注册电量不足的广播接收者
// 参数一:指定广播接收者
// 参数二:新建意图过滤器,在过滤器中写入 广播的 action(动作)
// Receiver对象接收的消息本质上是Intent
this.registerReceiver(batteryChangedReceiver,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}


[size=medium][b]5. MainActivity。【三、处理操作】在onReceive() 方法中。[/b][/size]

// 4. 处理操作

// 经过 Intent 匹配找到相应的 Receiver 对象并启动。
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
/**
* getIntExtra (String name, int defaultValue)
* Retrieve(再次获得,取回) extended data from the intent.
* 获得指定参数的值,若值为 null,则返回默认值 0
*
*/

// level:当前电池电量
int level = intent.getIntExtra("level", 0);
// 电池总电量
int scale = intent.getIntExtra("scale", 100);
// 电量文本
String text = "电池电量:" + (level * 100 / scale) + " %";
// 在 TextView 中显示出当前电池电量
tvBatteryChanged.setText(text);

// 判断:当电量小于等于15%时触发:通过 Toast 输出通知
if (level <= 15) {
// 因为这是在内部类中,而 Toast 输出是在 MainActivity 中,
// 因此写为 MainActivity.this
Toast.makeText(MainActivity.this, "当前电量已小于15%",
Toast.LENGTH_LONG).show();
}
}



[size=medium][b]代码补充:[/b][/size]

[size=medium][b]MainActivity。[/b][/size]

package com.android.mybroadcast;

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.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

/*
系统内部定义的广播消息,可以查阅官方文档:
C:\android\android-sdk-windows\docs\reference\android\content\Intent.html
*/
public class MainActivity extends Activity {
// 1.1 声明变量(显示电池电量的文本)
private TextView tvBatteryChanged;

// 2. 定义匿名的广播接收者(接收手机电量不足的通知)
private BroadcastReceiver batteryChangedReceiver = new BroadcastReceiver() {
/**
* 在 BroadcastReceiver 接收到与之匹配的广播消息后,onReceive()方法会被调用
*
* onReceive()方法必须要在5秒钟执行完毕,
* 否则 Android 系统会认为该组件失去响应,并提示用户强行关闭该组件
*
* @param context 设置BroadcastReceiver实例
* @param intent Receiver对象接收的消息
*/
@Override
public void onReceive(Context context, Intent intent) {
// 4. 处理操作

// 经过 Intent 匹配找到相应的 Receiver 对象并启动。
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
/**
* getIntExtra (String name, int defaultValue)
* Retrieve(再次获得,取回) extended data from the intent.
* 获得指定参数的值,若值为 null,则返回默认值 0
*
*/

// level:当前电池电量
int level = intent.getIntExtra("level", 0);
// 电池总电量
int scale = intent.getIntExtra("scale", 100);
// 电量文本
String text = "电池电量:" + (level * 100 / scale) + " %";
// 在 TextView 中显示出当前电池电量
tvBatteryChanged.setText(text);

// 判断:当电量小于等于15%时触发:通过 Toast 输出通知
if (level <= 15) {
// 因为这是在内部类中,而 Toast 输出是在 MainActivity 中,
// 因此写为 MainActivity.this
Toast.makeText(MainActivity.this, "当前电量已小于15%",
Toast.LENGTH_LONG).show();
}
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 1.2 初始化变量(显示电池电量的文本)
tvBatteryChanged = (TextView) findViewById(R.id.tvBatteryChanged);

// 3. 注册电量不足的广播接收者
// 参数一:指定广播接收者
// 参数二:新建意图过滤器,在过滤器中写入 广播的 action(动作)
// Receiver对象接收的消息本质上是Intent
this.registerReceiver(batteryChangedReceiver,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}

// -------------------------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}



[b][size=large]案例二:自定义普通广播[/size][/b]

[align=center][img]http://dl2.iteye.com/upload/attachment/0111/0258/45e139d8-b73a-3835-a006-5ea226021eab.png[/img][/align]

[align=center][img]http://dl2.iteye.com/upload/attachment/0111/0260/79f5134b-6601-3854-b6e9-7aa6b3c03233.png[/img][/align]

[align=center][img]http://dl2.iteye.com/upload/attachment/0111/0262/fe104a52-9ec8-3b92-af20-77dde5b33279.png[/img][/align]

[size=medium][b]1. 创建 BroadcastReceiver:NewsReceiver。【一、创建 BroadcastReceiver】[/b][/size]

[size=medium][b]2. 在功能清单中注册自定义 BroadcastReceiver。(创建的时候,自动注册好了)【二、注册自定义 BroadcastReceiver】[/b][/size]

[size=medium][b]3. activity_main.xml。写一个按钮。[/b][/size]

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<Button
android:id="@+id/btnSendBroadcast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/sendBroadcast" />

</RelativeLayout>


[size=medium][b]4. MainActivity。在按钮点击事件中发送广播。(要有 intent意图、附件信息 等)[/b][/size]

    /**
* 按钮点击事件
* @param view
*/
public void onClick(View view){
// (4)发送普通广播

// (4.1)新建 intent
Intent intent = new Intent();
// (4.2)设置 intent 的 action 动作 (设置发送的广播频道)
intent.setAction("android.intent.action.NEWS");
// (4.3)在功能清单文件中,注册 意图过滤器(广播接收者 这一端 设置 接收的广播频道)
// (4.4)附加信息
intent.putExtra("msg", "今天大部分地区将迎来强降雨天气");
// (4.5)发送 广播
sendBroadcast(intent);
}


[size=medium]其中,(4.3)如下:[/size]

        <!-- (2) 注册自定义 BroadcastReceiver (在功能清单中) -->
<receiver android:name=".NewsReceiver" >
<!--
(4.3)注册 意图过滤器(广播接收者 这一端 设置 接收的广播频道)
-->
<intent-filter>
<action android:name="android.intent.action.NEWS" />
</intent-filter>
</receiver>


[size=medium]如果 注册中的 intent 和 发送的广播中的 intent 是一样的(相当于观众把频道调到了电台广播频道),就可以接收到广播。[/size]

[size=medium][b]5. NewsReceiver。接收广播。[/b][/size]

package com.android.mybroadcast;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

/**
* 新闻联播的观众
*/
public class NewsReceiver extends BroadcastReceiver {
public NewsReceiver() {
}

/**
* 重写 onReceive() 方法
* @param context
* @param intent
*/
@Override
public void onReceive(Context context, Intent intent) {
// (5)获得 Intent意图 中的附加信息
String msg = intent.getStringExtra("msg");
// (6)Toast显示广播
Toast.makeText(context, "收到新闻联播的广播:" + msg,
Toast.LENGTH_LONG).show();
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值