四大组件之Broadcast简介
2017-03-22 16:25
302 查看
广播机制简介
Android 中的广播主要可以分为两种类型,标准广播(无序广播)和有序广播。广播是一种可以跨进程的通信方式,通过intent传递信息,没有构造函数标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。标准广播的工作流程如图所示。
有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。有序广播的工作流程如图所示。
有序广播通过 Context.sendOrderedBroadcast() 方法来发送,所有广播接收器按照优先级依次执行
通过 receiver的 intent-filter
中的 android:priority 属性给广播接收器设置了优先级,数值越大,优先级越高
在 onReceive() 方法中调用了 abortBroadcast() 方法,就表示将这条广播截断
当广播接收器收到广播后,可以使用 setResult()函数将结果传递给下一个广播接收器,然后通过
getResult() 函数来取得上一个广播接收器返回的结果。
然后修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity { …… @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.example.broadcasttest. MY_BROADCAST"); sendOrderedBroadcast(intent, null); } }); …… } …… }
即将 sendBroadcast() 方法改成 sendOrderedBroadcast() 方法。sendOrderedBroadcast() 方法接收两个参数,第一个参数仍然是 Intent,第二个参数是一个与权限相关的字符串,这里传入null就行了。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest" android:versionCode="1" android:versionName="1.0" > …… <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > …… <receiver android:name=".MyBroadcastReceiver"> <intent-filter android:priority="100" > <action android:name="com.example.broadcasttest.MY_BROADCAST"/> </intent-filter> </receiver> </application> </manifest>
通过 android:priority 属性给广播接收器设置了优先级,优先级比较高的广播接收器就可以先收到广播。这里将 MyBroadcastReceiver 的优先级设成了100,以保证它一定会在 AnotherBroadcastReceiver 之前收到广播。
既然已经获得了接收广播的优先权,那么 MyBroadcastReceiver 就可以选择是否允许广播继续传递了。修改 MyBroadcastReceiver 中的代码,如下所示:
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "received in MyBroadcastReceive", Toast.LENGTH_SHORT).show(); abortBroadcast(); } }
如果在 onReceive() 方法中调用了 abortBroadcast() 方法,就表示将这条广播截断,后面的广播接收器将无法再接收到这条广播。
本地广播(为了能够简单地解决广播的安全性问题,实现限于应用内的)
应用场景:比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。
Android引入了一套本地广播机制(Support v4 包中新增的),使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。
主要就是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。
public class MainActivity extends Activity { private IntentFilter intentFilter; private LocalReceiver localReceiver; private LocalBroadcastManager localBroadcastManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例 Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.example.broadcasttest. LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent); // 发送本地广播 } }); intentFilter = new IntentFilter(); intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST"); localReceiver = new LocalReceiver(); localBroadcastManager.registerReceiver(localReceiver, intentFilter); // 注册本地广播监听器 } @Override protected void onDestroy() { super.onDestroy(); localBroadcastManager.unregisterReceiver(localReceiver); } class LocalReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show(); } } }
首先是通过 LocalBroadcastManager 的g etInstance() 方法得到了它的一个实例,然后在注册广播接收器的时候调用的是 LocalBroadcastManager 的 registerReceiver() 方法,在发送广播的时候调用的是 LocalBroadcastManager 的 sendBroadcast() 方法,仅此而已。
本地广播是无法通过静态注册的方式来接收的。因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。
使用本地广播的几点优势:
1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄漏的问题。
2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。
3.发送本地广播比起发送系统全局广播将会更加高效。
sticky 广播通过 Context.sendStickyBroadcast() 函数来发送,用此函数发送的广播会一直滞留,当有匹配此广播的广播接收器被注册后,该广播接收器就会收到此条广播。使用此函数发送广播时,需要获得 BROADCAST_STICKY 权限:
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
sendStickyBroadcast
只保留最后一条广播,并且一直保留下去,这样即使已经有广播接收器处理了该广播,当再有匹配的广播接收器被注册时,此广播仍会被接收,如果你只想处理一遍该广播时,可以通过 removeStickyBroadcast() 函数实现。
注册广播的方式一般有两种,在代码中注册(动态注册)和在AndroidManifest.xml中注册(静态注册).
动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,动态注册的广播接收器即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。
静态注册的方式,可以让程序在未启动的情况下就能接收到广播。
动态注册监听网络变化:
创建一个广播接收器其实只需要新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。public class MainActivity extends Activity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver = new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(networkChangeReceiver); } class NetworkChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show(); } } }
然后观察onCreate()方法,首先我们创建了一个IntentFilter的实例(调频),并给它添加了一个值为android.net.conn.CONNECTIVITY_CHANGE的action(频道),接下来创建了一个NetworkChangeReceiver的实例,然后调用registerReceiver()方法进行注册,将NetworkChangeReceiver的实例和IntentFilter的实例都传了进去,这样NetworkChangeReceiver就会收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广播,也就实现了监听网络变化的功能。
动态注册的广播接收器一定都要取消注册才行,这里我们是在onDestroy()方法中通过调用unregisterReceiver()方法来实现的。
最好是能准确地告诉用户当前是有网络还是没有网络,因此我们还需要对上面的代码进行进一步的优化。修改MainActivity中的代码,如下所示:
public class MainActivity extends Activity {
……
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
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();
}
}
}
}
在onReceive()方法中,首先通过getSystemService()方法得到了ConnectivityManager的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它的getActiveNetworkInfo()方法可以得到NetworkInfo的实例,接着调用NetworkInfo的isAvailable()方法,就可以判断出当前是否有网络了,最后我们还是通过Toast的方式对用户进行提示。
另外,这里有非常重要的一点需要说明,Android系统为了保证应用程序的安全性做了规定,如果程序需要访问一些系统的关键性信息,必须在配置文件中声明权限才可以,否则程序将会直接崩溃,比如这里查询系统的网络状态就是需要声明权限的。
静态注册实现开机启动:
新建一个BootCompleteReceiver继承自BroadcastReceiver,代码如下所示:
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
}
}
可以看到,这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在AndroidManifest.xml中将这个广播接收器的类名注册进去。在onReceive()方法中,还是简单地使用Toast弹出一段提示信息。
然后修改AndroidManifest.xml文件,代码如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest" android:versionCode="1" android:versionName="1.0" > …… <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > …… <receiver android:name=".BootCompleteReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> </manifest>
需要注意的是,不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等,Android
中广播的生命周期不像 Activity 一样复杂,他的生命周期只有十秒左右,如果在
onReceive() 内做超过十秒内的事情,就会报错 。
例子:利用BroadcastReceiver实现开机“自”启动
实现原理:当Android启动时,会发出一个系统广播,内容为ACTION_BOOT_COMPLETED,它的字符串常量表示为android.intent.action.BOOT_COMPLETED。
(1)配置文件“AndroidManifest.xml”
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="lulicheng.android.onboot" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" > </uses-permission> <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".StartOnBootActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".StartOnBootService" > </service> <receiver android:name=".BootBroadcastReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> </manifest>在该配置文件中,配置了各个组件的基本参数,在使用权限中需要加入
“<uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED" >”权限,另外还有一点比较重要的就是在BootBroadcastReceiver中添加intent-filter,如此一来BootBroadcastReceiver的onReceiver方法才能被触发。
广播监听类BootBroadcastReceiver
public class BootBroadcastReceiver extends BroadcastReceiver { // 系统启动完成 static final String ACTION = "android.intent.action.BOOT_COMPLETED"; @Override public void onReceive(Context context, Intent intent) { // 当收听到的事件是“BOOT_COMPLETED”时,就创建并启动相应的Activity和Service if (intent.getAction().equals(ACTION)) { // 开机启动的Activity Intent activityIntent = new Intent(context, StartOnBootActivity.class); activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//一定要加,不然会失败 /*一定要加,出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。*/ // 启动Activity context.startActivity(activityIntent); // 开机启动的Service Intent serviceIntent = new Intent(context, StartOnBootService.class); // 启动Service context.startService(serviceIntent); } } } (3)“StartOnBootActivity” package lulicheng.android.onboot; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class StartOnBootActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 设置Activity的显示内容为一个文本域 TextView aTextView = new TextView(this); aTextView.setText("Wow!I started after cellphone boot."); setContentView(aTextView); } } (4)“StartOnBootService” package lulicheng.android.onboot; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.widget.Toast; public class StartOnBootService extends Service { @Override public IBinder onBind(Intent intent) { return null; } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); // Service被启动时,将会有弹出消息提示[MyService onStart] Toast.makeText(this, "[MyService onStart]", Toast.LENGTH_LONG).show(); } }
常见的系统广播:
Intent.ACTION_BOOT_COMPLETED //开机启动 Intent.ACTION_AIRPLANE_MODE_CHANGED; //关闭或打开飞行模式时的广播 Intent.ACTION_BATTERY_CHANGED; //充电状态,或者电池的电量发生变化 //电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册 Intent.ACTION_BATTERY_LOW; //表示电池电量低 Intent.ACTION_BATTERY_OKAY; //表示电池电量充足,即从电池电量低变化到饱满时会发出广播 Intent.ACTION_BOOT_COMPLETED; //在系统启动完成后,这个动作被广播一次(只有一次)。 Intent.ACTION_CAMERA_BUTTON; //按下照相时的拍照按键(硬件按键)时发出的广播 Intent.ACTION_CLOSE_SYSTEM_DIALOGS; //当屏幕超时进行锁屏时,当用户按下电源按钮,长按或短按(不管有没跳出话框),进行锁屏时,Android系统都会广播此Action消息 Intent.ACTION_CONFIGURATION_CHANGED; //设备当前设置被改变时发出的广播(包括的改变:界面语言,设备方向,等,请参考Configuration.Java) Intent.ACTION_DATE_CHANGED; //设备日期发生改变时会发出此广播 Intent.ACTION_DEVICE_STORAGE_LOW; //设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用? Intent.ACTION_DEVICE_STORAGE_OK; //设备内存从不足到充足时发出的广播,此广播只能由系统使用,其它APP不可用? Intent.ACTION_DOCK_EVENT; //发出此广播的地方frameworks\base\services\java\com\android\server\DockObserver.java Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE; ////移动APP完成之后,发出的广播(移动是指:APP2SD) Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; //正在移动APP时,发出的广播(移动是指:APP2SD) Intent.ACTION_GTALK_SERVICE_CONNECTED; //Gtalk已建立连接时发出的广播 Intent.ACTION_GTALK_SERVICE_DISCONNECTED; //Gtalk已断开连接时发出的广播 Intent.ACTION_HEADSET_PLUG; //在耳机口上插入耳机时发出的广播 Intent.ACTION_INPUT_METHOD_CHANGED; //改变输入法时发出的广播 Intent.ACTION_LOCALE_CHANGED; //设备当前区域设置已更改时发出的广播 Intent.ACTION_MEDIA_BAD_REMOVAL; //未正确移除SD卡(正确移除SD卡的方法:设置--SD卡和设备内存--卸载SD卡),但已把SD卡取出来时发出的广播 //广播:扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount) Intent.ACTION_MEDIA_BUTTON; //按下"Media Button" 按键时发出的广播,假如有"Media Button"按键的话(硬件按键) Intent.ACTION_MEDIA_CHECKING; //插入外部储存装置,比如SD卡时,系统会检验SD卡,此时发出的广播? Intent.ACTION_MEDIA_EJECT; //已拔掉外部大容量储存设备发出的广播(比如SD卡,或移动硬盘),不管有没有正确卸载都会发出此广播? //广播:用户想要移除扩展介质(拔掉扩展卡)。 Intent.ACTION_MEDIA_MOUNTED; //插入SD卡并且已正确安装(识别)时发出的广播 //广播:扩展介质被插入,而且已经被挂载。 Intent.ACTION_MEDIA_REMOVED; //外部储存设备已被移除,不管有没正确卸载,都会发出此广播? // 广播:扩展介质被移除。 Intent.ACTION_MEDIA_SCANNER_FINISHED; //广播:已经扫描完介质的一个目录 Intent.ACTION_MEDIA_SCANNER_STARTED; //广播:开始扫描介质的一个目录 Intent.ACTION_MEDIA_SHARED; // 广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。 Intent.ACTION_MEDIA_UNMOUNTED // 广播:扩展介质存在,但是还没有被挂载 (mount)。 Intent.ACTION_PACKAGE_ADDED; //成功的安装APK之后 //广播:设备上新安装了一个应用程序包。 //一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播) Intent.ACTION_PACKAGE_CHANGED; //一个已存在的应用程序包已经改变,包括包名 Intent.ACTION_PACKAGE_DATA_CLEARED; //清除一个应用程序的数据时发出的广播(在设置--应用管理--选中某个应用,之后点清除数据时?) //用户已经清除一个包的数据,包括包名(清除包程序不能接收到这个广播) Intent.ACTION_PACKAGE_INSTALL; //触发一个下载并且完成安装时发出的广播,比如在电子市场里下载应用? Intent.ACTION_PACKAGE_REMOVED; //成功的删除某个APK之后发出的广播 //一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播) Intent.ACTION_PACKAGE_REPLACED; //替换一个现有的安装包时发出的广播(不管现在安装的APP比之前的新还是旧,都会发出此广播?) Intent.ACTION_PACKAGE_RESTARTED; //用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播) Intent.ACTION_POWER_CONNECTED; //插上外部电源时发出的广播 Intent.ACTION_POWER_DISCONNECTED; //已断开外部电源连接时发出的广播 Intent.ACTION_REBOOT; //重启设备时的广播 Intent.ACTION_SCREEN_OFF; //屏幕被关闭之后的广播 Intent.ACTION_SCREEN_ON; //屏幕被打开之后的广播 Intent.ACTION_SHUTDOWN; //关闭系统时发出的广播 Intent.ACTION_TIMEZONE_CHANGED; //时区发生改变时发出的广播 Intent.ACTION_TIME_CHANGED; //时间被设置时发出的广播 Intent.ACTION_TIME_TICK; //广播:当前时间已经变化(正常的时间流逝)。 //当前时间改变,每分钟都发送,不能通过组件声明来接收,只有通过Context.registerReceiver()方法来注册 Intent.ACTION_UID_REMOVED; //一个用户ID已经从系统中移除发出的广播 // Intent.ACTION_UMS_CONNECTED; //设备已进入USB大容量储存状态时发出的广播? Intent.ACTION_UMS_DISCONNECTED; //设备已从USB大容量储存状态转为正常状态时发出的广播? Intent.ACTION_WALLPAPER_CHANGED; //设备墙纸已改变时发出的广播
相关文章推荐
- 15_Android四大组件:Activity 、Service、Broadcast receiver 、Content provider
- 四大组件之BroadCast
- Android四大组件 Activity BroadCast ContentProvider Service 的理解
- 四大组件之BroadcastReceiver
- android四大组件之BroadcastReceiver
- android四大组件之contentprovider与service简介
- Android四大组件之BroadcastReceiver(2)
- Android四大组件之Broadcast Receiver
- android 四大组件之BroadCastReceiver
- Android四大组件之 BroadCastReceive 使用总结
- 四大组件之BroadcastReceiver
- Android四大组件之Broadcast
- android四大组件之BroadcastReceiver
- Android四大组件之BroadCast
- Android面试常客--四大组件之BroadcastReceiver
- Android 四大组件之BroadcastReceiver特征
- android四大组件之BroadcastReceiver ip拨号功能
- Android四大组件之BroadcastReceiver必备知识点
- 四大组件Activity、Service、BroadcastReceiver、ContentProvider总结
- 四大组件之一Broadcast应用实例