Android四大组件Broadcast Receiver详解

2024-04-29 01:48

本文主要是介绍Android四大组件Broadcast Receiver详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章来源:http://www.itnose.net/detail/6030479.html

更多文章:http://www.itnose.net/type/85.html

本文主要讲述了:

一、BroadcastReceiver概述:

二、BroadcastReceiver事件分类

三、BroadcastReceiver事件的编程流程

四、两类BroadcastReceiver

五、普通广播和有序广播

六、Service与BroadcastReceiver如何交互?

七、开机自动运行service

八、BroadcastReceiver的生命周期

 

一、BroadcastReceiver概述:

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


二、BroadcastReceiver事件分类 

1、系统广播事件,比如:ACTION_BOOT_COMPLETED(系统启动完成后触发),ACTION_TIME_CHANGED(系统时间改变时触发),ACTION_BATTERY_LOW(电量低时触发)等等。 

2、用户自定义的广播事件。 


三、BroadcastReceiver事件的编程流程 

1、注册广播事件:注册方式有两种,

一种是静态注册,就是在 AndroidManifest.xml文件中定义,注册的广播接收器必须要继承BroadcastReceiver类;

在AndroidManifest.xml中用标签生命注册,并在标签内用标签设置过滤器。

<receiver android:name="myRecevice">    //继承BroadcastReceiver,重写onReceiver方法<intent-filter>    <action android:name="com.dragon.net"></action> //使用过滤器,接收指定action广播</intent-filter></receiver>

另一种是动态注册,是在程序中使用 Context.registerReceiver注册,注册的广播接收器相当于一个匿名类。两种方式都需要IntentFIlter。

 

IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(String);   //为BroadcastReceiver指定action,使之用于接收同action的广播registerReceiver(BroadcastReceiver,intentFilter);

  一般:在onStart中注册,onStop中取消unregisterReceiver 

  指定广播目标Action:Intent intent = new Intent(actionString); 

  并且可通过Intent携带消息 :intent.putExtra("msg", "hello,我通过广播发送消息了"); 

  发送广播消息:Context.sendBroadcast(intent )

 

2、发送广播事件:通过Context.sendBroadcast来发送,由Intent来传递注册时用到的Action。 

3、 接收广播事件:当发送的广播被接收器监听到后,会调用它的onReceive()方法,并将包含消息的Intent对象传给它。onReceive中代码的执行时间不要超过5s,否则Android会弹出超时dialog。

四、两类BroadcastReceiver

1、正常广播 Normal broadcasts(用 Context.sendBroadcast()发送)是完全异步的。它们都运行在一个未定义的顺序,通常是在同一时间。这样会更有效,但意味着receiver不能包含所要使用的结果或中止的API。  
2、有序广播 Ordered broadcasts(用 Context.sendOrderedBroadcast()发送)每次被发送到一个receiver。所谓有序,就是每个receiver执行后可以传播到下一个receiver,也可以完全中止传播??不传播给其他receiver。 而receiver运行的顺序可以通过matched intent-filter 里面的android:priority来控制,当priority优先级相同的时候,Receiver以任意的顺序运行。

 

PS:

下面举例说明了4种情况的广播事件:静态注册的系统广播事件、静态注册的用户自定义广播事件、动态注册的系统广播事件和动态注册的用户自定义广播事件。

1、创建广播接受者

 

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;public class MyReceiver extends BroadcastReceiver {private static final String TAG = "MyReceiver";@Overridepublic void onReceive(Context context, Intent intent) {String msg = intent.getStringExtra("msg");Log.i(TAG, msg);}}
2、广播注册 

 

1)静态注册
静态注册是在AndroidManifest.xml文件中配置的,我们就来为MyReceiver注册一个广播地址:

<receiver android:name=".MyReceiver"><intent-filter><action android:name="android.intent.action.MY_BROADCAST"/><category android:name="android.intent.category.DEFAULT" /></intent-filter></receiver>
配置了以上信息之后,只要是android.intent.action.MY_BROADCAST这个地址的广播,MyReceiver都能够接收的到。


2)动态注册
动态注册需要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播,下面我们就来看一下注册的代码:

 

MyReceiver receiver = new MyReceiver();IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.MY_BROADCAST");registerReceiver(receiver, filter);

3)发送广播

 

 

    public void send(View view) {Intent intent = new Intent("android.intent.action.MY_BROADCAST");intent.putExtra("msg", "hello receiver.");sendBroadcast(intent);}

PS:

 

下面简单介绍下系统广播

下面是android系统中定义了很多标准的Broadcast Action来响应系统的广播事件(只列出一部分)

①ACTION_TIME_CHANGED(时间改变时触发)
②ACTION_BOOT_COMPLETED(系统启动完成后触发)--比如有些程序开机后启动就是用这种方式来实现的
③ACTION_PACKAGE_ADDED(添加包时触发)
④ACTION_BATTERY_CHANGED(电量低时触发)

 

/*** 系统静态注册广播消息接收器* * @author zuolongsnail* */
public class SystemReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {Log.e("SystemReceiver", "电量低提示");Toast.makeText(context, "您的手机电量偏低,请及时充电", Toast.LENGTH_SHORT).show();}}
}

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.byread" android:versionCode="1" android:versionName="1.0"><application android:icon="@drawable/icon" android:label="@string/app_name"><activity android:name=".MainActivity" android:label="@string/app_name"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><!-- 注册系统静态广播接收器 --><receiver android:name=".SystemReceiver"><intent-filter><action android:name="android.intent.action.BATTERY_LOW" /></intent-filter></receiver></application>
</manifest>

 

五、普通广播和有序广播

上面的例子只是一个接收者来接收广播,如果有多个接收者都注册了相同的广播地址,又会是什么情况呢,能同时接收到同一条广播吗,相互之间会不会有干扰呢?

这就涉及到普通广播和有序广播的概念了。

1、Normal Broadcast(普通广播):Normal Broadcast是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高。但缺点是接受者不能将处理结果传递给下一个接收者,并且无法终止Broadcast Intent的广播。
 
 2、Ordered Broadcast(有序广播):Ordered Broadcast的接收者将按预先声明的优先级依次接受Broadcast。如:A的级别高于B、B的级别高于C,那么Broadcast先传给A,再传给B,最后传给C。优先级别声明在<intent-filter.../>元素的android:priority属性中,数越大优先级别越高,取值范围为-1000-1000,优先级别也可以调用IntentFilter对象的setPriority()进行设置。OrderedBroadcast接收者可以终止Broadcast Intent的传播,BroadcastIntent的传播一旦终止,后面的接收者就无法接收到Broadcast。另外,OrderedBroadcast的接收者可以将数据传递给下一个接收者。如:A得到Broadcast后,可以往它的结果对象中存入数据,当Broadcast传给B时,B可以从A的结果对象中得到A存入的数据。

3、context提供的如下两个方法用于发送广播:
  sendBroadcast():发送Normal Broadcast
    sendOrderedBroadcast():发送OrderedBroadcast。
 
4、对于OrderedBroadcast而言,系统会根据接收者生命的优先级别顺序逐个执行接收者,优先接收到Broadcast的接收者可以终止Broadcast,调用BroadcastReceiver的abortBroadcast()方法即可终止Broadcast。如果Broadcast被前面的接收者终止,后面的接收者就再也无法获取到Broadcast。
 
5、不仅如此,对于OrderBroadcast而言,优先接收到Broadcast的接收者可以通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,然后传给下一个接收者,下一个接收者通过代码:  Bundle bundle=getResultExtras(true)可以获取上一个接收者存入的数据。

实例:点击按钮,两个Receiver接收同一条广播,在logcat中打印出数据(按照Receiver的优先顺序,Receiver2先,Receiver1后)

Manifest:      

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.song"android:versionCode="1"android:versionName="1.0" ><uses-sdk android:minSdkVersion="8" /><applicationandroid:icon="@drawable/ic_launcher"android:label="@string/app_name" ><activityandroid:label="@string/app_name"android:name=".C48_BroadcastActivity" ><intent-filter ><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter><!--优先级的设定 MyReceiver2大于MyReceiver1,优先级的范围-1000~1000 --></activity><receiver android:name=".MyReceiver1"><intent-filter android:priority="200"><action android:name="com.song.123"/></intent-filter></receiver><receiver android:name=".MyReceiver2"><intent-filter android:priority="1000"><action android:name="com.song.123"/></intent-filter></receiver></application></manifest>

activity代码:

 

 

//发送广播,bundle绑上key为a的数据
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;public class C48_BroadcastActivity extends Activity {/** Called when the activity is first created. */Button button;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);button=(Button)findViewById(R.id.button);button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubIntent intent=new Intent("com.song.123");Bundle bundle=new Bundle();bundle.putString("a", "aaa");intent.putExtras(bundle);//有序广播sendOrderedBroadcast(intent, null);}});}
}
Receiver2

 

 

package com.song;
//优先接到广播,bundle绑上key为b的数据
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;public class MyReceiver2 extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubSystem.out.println("receiver2");
//		context.getSystemService(name);Bundle bundle=intent.getExtras();bundle.putString("b", "bbb");System.out.println("a="+bundle.get("a"));setResultExtras(bundle);//切断广播
//		abortBroadcast();     }}

Receiver1

 

 

package com.song;
//接收从receiver2传来的广播,包含key为a和b的数据
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;public class MyReceiver1 extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubSystem.out.println("receiver1");//要不要接受上一个广播接收器receiver2传来的的数据Bundle bundle=getResultExtras(true);System.out.println("a="+bundle.getString("a")+",b="+bundle.getString("b"));}}

根据上面的配置可以看出,该程序包括两个receiver,其中 Receiver2高,Receiver1低。如果Receiver2中的Receiver2zhabortBroadcast()注释了,那么程序Receiver1中将可以看到完整的信息了。

 

 

六、Service与BroadcastReceiver如何交互?

我们之前都是先启动了一个Activity,然后在Activity中启动服务。如果是这样,在启动服务时必须要先启动一个Activity。在很多时候这样做有些多余,我们现在可以利用Broadcast Receiver在Android系统启动时运行一个Activity。也许我们会从中得到一些启发,既然可以在Broadcast Receiver中启动Activity,为什么不能启动Service呢?说做就做,现在让我们来验证一下这个想法。

先编写一个服务类,这个服务类没什么特别的,仍然使用前面两节编写的MyService类即可。在AndroidManifest.xml文件中配置MyService类的代码也相同。

下面来完成最关键的一步,就是建立一个BroadcastReceiver,代码如下:

 

import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  public class StartupReceiver extends BroadcastReceiver  
{  @Override  public void onReceive(Context context, Intent intent)  {  //  启动一个Service  Intent serviceIntent = new Intent(context, MyService.class);          context.startService(serviceIntent);          Intent activityIntent = new Intent(context, MessageActivity.class);  //  要想在Service中启动Activity,必须设置如下标志  activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  context.startActivity(activityIntent);  }  
} 

在StartupReceiver类的onReceive方法中完成了两项工作:启动服务和显示一个Activity来提示服务启动成功。 

 

AndroidManifest.xml文件的完整代码。

 

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.blogjava.mobile.startupservice"android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" 
android:label="@string/app_name"> <activity android:name=".MessageActivity" 
android:theme="@android:style/Theme.Dialog"> <intent-filter>                  <category android:name="android.
intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name="StartupReceiver"> <intent-filter> <action android:name="android.
intent.action.BOOT_COMPLETED" /> <category android:name="android.
intent.category.LAUNCHER" /> </intent-filter> </receiver> <service android:enabled="true" android:name=".MyService" /> </application> <uses-sdk android:minSdkVersion="3" /> <uses-permission android:name="android.
permission.RECEIVE_BOOT_COMPLETED" /> 
</manifest> 

 

 

PS:

开机自动运行service

我们经常会有这样的应用场合,比如消息推送服务,需要实现开机启动的功能。要实现这个功能,我们就可以订阅系统“启动完成(BOOT_COMPLETED)”这条广播,接收到这条广播后我们就可以启动自己的服务了;

上面实例其实和开机启动服务差不多了,下面我们在说说开机启动服务。
Receiver :

 

public class BootCompleteReceiver extends BroadcastReceiver {private static final String TAG = "BootCompleteReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Intent service = new Intent(context, MsgPushService.class);context.startService(service);Log.i(TAG, "Boot Complete. Starting MsgPushService...");}}

 

 

Service :

public class MsgPushService extends Service {private static final String TAG = "MsgPushService";@Overridepublic void onCreate() {super.onCreate();Log.i(TAG, "onCreate called.");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i(TAG, "onStartCommand called.");return super.onStartCommand(intent, flags, startId);}@Overridepublic IBinder onBind(Intent arg0) {return null;}
}
AndroidManifest 
        <!-- 开机广播接受者 --><receiver android:name=".BootCompleteReceiver"><intent-filter><!-- 注册开机广播地址--><action android:name="android.intent.action.BOOT_COMPLETED"/><category android:name="android.intent.category.DEFAULT" /></intent-filter></receiver><!-- 消息推送服务 --><service android:name=".MsgPushService"/>

PS:

最后在说说Broadcast的生命周期

  1、一个BroadcastReceiver 对象只有在被调用onReceive(Context, Intent)的才有效的,当从该函数返回后,该对象就无效的了,结束生命周期。

  因此从这个特征可以看出,在所调用的onReceive(Context, Intent)函数里,不能有过于耗时的操作,不能使用线程来执行。对于耗时的操作,请start service来完成。因为当得到其他异步操作所返回的结果时,BroadcastReceiver 可能已经无效了。

2、一个Broadcast receiver只有一个回调方法:

void onReceive(Context curContext, Intent broadcastMsg)

当Broadcast receiver接收到一条广播信息,android会调用它的onReceive()方法,并传递给它一个包含广播信息的intent对象。当Broadcast receiver在执行这个方法时可以认为它是活动的,onReceive()方法返回时,它便终止了。

一个包含活动Broadcast receiver的进程会被系统保护以避免被终止。但是如果进程不包含任何活动组件,那么当它占用的内存要用于其它进程时,系统任何时候都可以终止运行该进程。

当应答一条广播信息十分耗时,而另一个线程必须执行某个任务时会出现一个问题。试想一下,如果onReceive()方法产生一个线程然后返回,那么整个进程,包括新的线程会被认为是不活动的(除非在进程中还有其它活动的组件),该进程就有被系统终止运行的危险。解决这个问题的办法就是在onReceive()方法中启动一个服务,让这个服务做那样的工作,这样系统就会知道进程中有活动的组件而不会停止进程。

 

这篇关于Android四大组件Broadcast Receiver详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/944818

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存