当前位置: 代码迷 >> 综合 >> Andriod 四大组件之Broadcast Receiver
  详细解决方案

Andriod 四大组件之Broadcast Receiver

热度:62   发布时间:2023-12-18 22:34:13.0

广播的功能和特征

  • 广播的生命周期很短,经过调用对象-->实现onReceive-->结束,整个过程就结束了。从实现的复杂度和代码量来看,广播无疑是最迷你的Android 组件,实现往往只需几行代码。广播对象被构造出来后通常只执行BroadcastReceiver.onReceive方法,便结束了其生命周期。所以有的时候我们可以把它当做函数看也未必不可。
  • Android中的四大组件是 Activity、Service、Broadcast和Content Provider。而Intent是一个对动作和行为的抽象描述,负责组件之间程序之间进行消息传递。那么Broadcast Receiver组件就提供了一种把Intent作为一个消息广播出去,由所有对其感兴趣的程序对其作出反应的机制。
  • 和所有组件一样,广播对象也是在应用进程的主线程中被构造,所以广播对象的执行必须是要同步且快速的。也不推荐在里面开子线程,因为往往线程还未结束,广播对象就已经执行完毕被系统销毁。如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成。
  • 每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应。

 

接收系统广播:

广播接收器可以自由地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能收到该广播,并在内部处理相应的逻辑。注册广播的方式有两种,在代码中注册和在清单文件中注册,前者称为动态注册,后者称为静态注册。

动态注册监听网络变化:

新建工程文件,首先在MainActivity中定义一个内部类netWorkChangeReceiver,并重写父类的onReceive()方法,这样每当网络状态发生变化时,onReceive()方法就会得到执行,这里使用Toast提示一段文本信息。紧接着在onCreate方法中进行动态注册,然后在onDestroy方法中进行取消注册。最后要记得,动态注册的广播接收器一定要取消注册才行。

另外,查询系统的网络状态是需要申明权限的,打开清单文件,添加如下权限:

public class MainActivity extends Activity {private IntentFilter intentFilter;private netWorkChangeReceiver netWorkChangeReceiver;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 动态注册:创建一个IntentFilter的实例,添加网络变化的广播intentFilter = new IntentFilter();intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");// 创建NetWorkChangeReceiver的实例,并调用registerReceiver()方法进行注册netWorkChangeReceiver = new netWorkChangeReceiver();registerReceiver(netWorkChangeReceiver, intentFilter);}// 取消注册,一定要记得,不然系统会报错@Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(netWorkChangeReceiver);}class netWorkChangeReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {//通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();if(networkInfo != null && networkInfo.isAvailable()){Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();}else{Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();}}}
}

静态注册实现开机启动:

动态注册的方式比较灵活,但缺点是:必须在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。为了让程序在未启动的情况下就能接收到广播,这里就需要使用到静态注册。

这里我们准备让程序接收一条开机广播,当收到这条广播时,就可以在onReceive()方法中执行相应的逻辑,从而实现开机启动的功能。

新建一个类:BootCompleteReceiver,让他继承BroadcastReceiver,在onReceive()方法中简单地Toast一下,代码如下:

public class BootCompleteReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();}
}

可以看到,这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在清单文件AndroidManifest.xml中将这个广播接收器的类名注册进去。

然后修改清单文件AndroidManifest.xml,代码如下:

<uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="16" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/><applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name="com.example.m05_broadcastreceiver01.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=".BootCompleteReceiver"><intent-filter ><action android:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver></application>

发送自定义广播

发送标准广播 

新建工程文件。在发广播之前,我们先定义一个广播接收器来接收此广播才行。因此,新建一个类:MyBroadcastReceiver,让他继承BroadcastReceiver,代码如下:

public class MyBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();}
}

这里,当MyBroadcastReceiver 收到自定义的广播时,就会执行onReceive()方法中的逻辑,弹出一个Toast。

紧接着,要在清单文件AndroidManifest.xml中对这个广播接收器进行注册:

<applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name="com.example.m05_broadcastreceiver02.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=".MyBroadcastReceiver"><intent-filter ><action android:name="com.example.m05_broadcastreceiver02.MY_BROADCAST"/></intent-filter></receiver></application>

然后,修改MainActivity.java中的代码,添加Button的监听事件: 点击按钮时,发送广播 

   Button button1=(Button)findViewById(R.id.button1);button1.setOnClickListener(new OnClickListener() {            @Overridepublic void onClick(View v) {Intent intent =new Intent("com.example.m05_broadcastreceiver02.MY_BROADCAST");sendBroadcast(intent);}});

总结:可以看到,点击按钮时,发送com.example.m05_broadcastreceiver02.MY_BROADCAST这条广播,这样, 所有能够监听com.example.m05_broadcastreceiver02.MY_BROADCAST这条广播的广播接收器就都会同时收到消息,此时发出去的就是一条标准广播,即无序广播。所以接下来就需要讲到有序广播。

发送有序广播:

有序广播的接收者则将按预先声明的优先级依次接收Broadcast.如A高于B,了高于C,那么执行顺序为A到B到C.优先级声明在<intent-filter.../>元素的Android:priority属性中,数字越大优先级越高.取值范围为-1000~1000,先可以在代码中调用IntentFilter对象的setPriority()进行设置.

发送有序广播的方法为sendOrderedBroadcast()

优先接收到Broadcast的接收者可以通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,然后传给下一个接收者,

下一个接收者通过Bundle bundle = new getResultExtras(true)可以获取上一个接收者存入的数据

下面是一个例子,共定义了两个receiver

public class MyReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {  Toast.makeText(  context,  "接收到的Intent的Action为:" + intent.getAction() + "\n消息内容是:"  + intent.getStringExtra("msg"), 5000).show();  //创建一个Bundle对象,并存入数据  Bundle bundle = new Bundle();  bundle.putString("first", "第一个BroadcastReceiver存入的消息");  //将bundle放入结果中  setResultExtras(bundle);  //取消Broadcast的继续发送  abortBroadcast();  }  
}  
public class MyReceiver2 extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {  Bundle bundle = getResultExtras(true);  //解析前一个BroadcastReceiver所存入的key为first的消息  String first = bundle.getString("first");  Toast.makeText(context,"第一个Broadcast存入的消息为:" + first, 5000).show();  }  
}  
配置两个receiver的优先级

<receiver android:name="MyReceiver" >  <intent-filter android:priority="20">  <action android:name="WangLi.Test.OrderBroadcast" />  </intent-filter>  
</receiver>  
<receiver android:name="MyReceiver2" >  <intent-filter android:priority="0">  <action android:name="WangLi.Test.OrderBroadcast" />  </intent-filter>  
</receiver>  

一个是20,一个是0,显然是先执行优先级为20的那个接收者

注意MyReceiver里的取消Broadcast继续发送的代码

abortBroadcast();  

如果注释它,那么MyReceiver2 也会得到执行


各种各样的广播: 

在android中有很多系统自带的intent.action,通过监听这些事件我们可以完成很多功能。

    1. 开机: String BOOT_COMPLETED_ACTION 广播:在系统启动后。这个动作被广播一次(只有一次)。监听: “android.intent.action.BOOT_COMPLETED”
    2. 电话拨入: String ANSWER_ACTION 动作:处理拨入的电话。监听: “android.intent.action.ANSWER”
    3. 电量变化: String BATTERY_CHANGED_ACTION 广播:充电状态,或者电池的电量发生变化。监听: “android.intent.action.BATTERY_CHANGED”
    4. 日期改变: String DATE_CHANGED_ACTION 广播:日期被改变。 监听:“android.intent.action.DATE_CHANGED”
    5. 取消更新下载: String FOTA_CANCEL_ACTION 广播:取消所有被挂起的 (pending) 更新下载。 监听:“android.server.checkin.FOTA_CANCEL”
    6. 更新开始安装: String FOTA_READY_ACTION 广播:更新已经被下载 可以开始安装。监听 “android.server.checkin.FOTA_READY”
    7. 主屏幕: String HOME_CATEGORY 类别:主屏幕 (activity)。设备启动后显示的第一个 activity。 监听:"android.intent.category.HOME”
    8. 新应用: String PACKAGE_ADDED_ACTION 广播:设备上新安装了一个应用程序包。监听: “android.intent.action.PACKAGE_ADDED”
    9. 删除应用: String PACKAGE_REMOVED_ACTION 广播:设备上删除了一个应用程序包。监听: “android.intent.action.PACKAGE_REMOVED”
    10. 屏幕关闭: String SCREEN_OFF_ACTION 广播:屏幕被关闭。监听: “android.intent.action.SCREEN_OFF”
    11. 屏幕开启: String SCREEN_ON_ACTION 广播:屏幕已经被打开。 监听:“android.intent.action.SCREEN_ON”
    12. 时区改变: String TIMEZONE_CHANGED_ACTION 广播:时区已经改变。监听: “android.intent.action.TIMEZONE_CHANGED”
    13. 时间改变: String TIME_CHANGED_ACTION 广播:时间已经改变(重新设置)。 “android.intent.action.TIME_SET”
    14. 时间流逝: String TIME_TICK_ACTION 广播:当前时间已经变化(正常的时间流逝)。 “android.intent.action.TIME_TICK”
    15. 进入大容量存储模式: String UMS_CONNECTED_ACTION 广播:设备进入 USB 大容量存储模式。 “android.intent.action.UMS_CONNECTED”
    16. 退出大容量存储模式: String UMS_DISCONNECTED_ACTION 广播:设备从 USB 大容量存储模式退出。 “android.intent.action.UMS_DISCONNECTED”
    17. 壁纸改变: String WALLPAPER_CHANGED_ACTION 广播:系统的墙纸已经改变。 “android.intent.action.WALLPAPER_CHANGED”
    18. web搜索: String WEB_SEARCH_ACTION 动作:执行 web 搜索。 “android.intent.action.WEB_SEARCH”
    19. 网络变化: String CONNECTIVITY_CHANGE_ACTION 动作:网络变化。“android.intent.action.CONNECTIVITY_CHANGE_ACTION”

实例:使用动态注册,监听手机的电量变化。   

<LinearLayout 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" ><TextViewandroid:id="@+id/textView1"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="30dp"android:gravity="center"/></LinearLayout>
package com.example.m05_broadcastreceiver02;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.widget.TextView;public class MainActivity extends Activity {private BatteryBroadcastReceiver batteryBroadcastReceiver;private TextView textView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView=(TextView)findViewById(R.id.textView1);//动态注册监听电量的广播接收器IntentFilter intentFilter = new IntentFilter();intentFilter.addAction("android.intent.action.BATTERY_CHANGED");batteryBroadcastReceiver = new BatteryBroadcastReceiver();registerReceiver(batteryBroadcastReceiver, intentFilter);       }//取消注册监听电量的广播接收器@Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(batteryBroadcastReceiver);}//新建一个广播接收器,监听电量的变化public class BatteryBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {//获取当前电量int level = intent.getIntExtra("level", 0);//电量的总刻度int scale = intent.getIntExtra("scale", 100);textView.setText("电池电量为"+((level*100) / scale)+"%");//当电量低时,可以进行一些操作,例如弹出通知等
/*                if(level<15){do something}*/}}}}

紧接着,在清单文件中进行权限声明:

<uses-permission android:name="android.permission.BATTERY_STATS"/>




  相关解决方案