本文主要是介绍广播接受者(Broadcast Receiver),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
监听设备的重启
设备重启后,那些持续运行的应用通常也需要重启。通过监听具有BOOT_COMPLETED操作的broadcast intent,可得知设备是否已完成启动。
创建Broadcast Receiver
package com.example.photogallery;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;public class StartupReceiver extends BroadcastReceiver {private static final String TAG = "StartupReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Log.i(TAG, intent.getAction());}}
broadcast receiver须在manifest配置文件中登记(也可在代码中登记),类似于activity或service。使用receiver标签并包含相应的intent-filter在其中。StartupReceiver 将会监听BOOT_COMPLETED操作,该操作还需配置RECEIVE_BOOT_COMPLETED使用权限。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/><receiver android:name="com.example.photogallery.StartupReceiver" ><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver>
与activity和service不同,在配置文件中声明的的broadcast receiver几乎总是需要声明intent-filter。broadcast intent就是为发送信息给多个监听者而生的,但显示intent只有一个receiver。因此显示broadcast intent很少见。
在配置文件中完成声明后,即使应用当前并未运行,只要有匹配的broadcast intent的发来,broadcast receiver就会接收。一收到intent,broadcast receiver的onReceive(Context, Intent)方法即开始运行,然后broadcast receiver就会被销毁。
如何使用Broadcast Receiver
由于onReceive(Context, Intent)方法运行在住线程上,因此不能再该方法内做一些耗时的重度任务,如连接网络或数据的永久存储等。
receiver需要知道定时器的启停状态。在PollService类中添加一个Preferences常量,用于存储状态信息。
public static void setServiceAlarm(Context context, boolean isOn) {....
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(PREF_IS_ALARM_ON, isOn).commit();}
设备重启后启动定时器
@Overridepublic void onReceive(Context context, Intent intent) {Log.i(TAG, intent.getAction());boolean isOn = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PollService.PREF_IS_ALARM_ON, false);PollService.setServiceAlarm(context, isOn);}
发送与接受自己的广播
通过发送与接受自己的广播,避免在打开应用后,我们仍能接收都通知信息。
发送广播
sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));
动态broadcast receiver
我们可以编写一个类似StartupReceiver,并在manifest配置文件中登记的broadcast receiver来接收intent。但这里该方法行不通。我们须在PhotoGalleryFragment存在的时候接收intent。而在manifest配置文件中声明的独立receiver则很难做到这一点。因为该receiver在不断接收intent的同时,还需要另一种方法来知晓PhotoGalleryFragment的存在状态。
使用动态broadcast receiver可解决该问题。动态broadcast receiver是在代码中,而不是在配置文件中完成登记声明的。
创建一个隐藏前台通知的通用fragment
package com.example.photogallery;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.app.Fragment;
import android.widget.Toast;public abstract class VisibleFragment extends Fragment {private static final String TAG = "VisibleFragment";private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(getActivity(),"get a broadcast: " + intent.getAction(),Toast.LENGTH_SHORT).show();}};@Overridepublic void onResume() {super.onResume();/*** 这里创建的IntentFilter与在配置文件中定义的intent-filter是一样的。任何使用XML定义的IntentFilter,均可以* 代码的方式完成定义。要配置以代码方式创建的IntentFilter,直接调用addCategory(String)、addAction(String)和* addDataPath(String)等方法。*/IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);getActivity().registerReceiver(mOnShowNotification, filter);}@Overridepublic void onPause() {super.onPause();/*** 使用完后,动态登记的broadcast receiver必须能够自我清除。通常,如果在启动生命周期方法中登记了receiver,* 则需在相应的停止方法中调用Context.unregisterReceiver(BroadcastReceiver)方法。因此,这里,我们* 在onResume()方法里登记,而在onPause()方法里撤销登记。同样的,如果在onActivityCreated()方法里登记,* 则应在onActivityDestoryed(...)方法里撤销登记。* 顺便要说的是,我们应注意保留fragment中的onCreate(...)和onDestory()方法的应用。设备发生旋转时,* onCreate(...)和onDestory()方法中的getActivity()方法会返回不同的值。因此,想在Fragment.onCreate(...)* 和Fragment.onDestory()方法中实现登记或撤销登记,应使用getActivity().getApplicationContext()方法。* */getActivity().unregisterReceiver(mOnShowNotification);}}
修改PhotoGalleryFragment,调整其父类为VisibleFragment。
使用私有权限
使用动态broadcast receiver存在一个问题,即系统中的任何应用均可监听并触发我们的receiver。
不要担心,有多种方式可用于阻止未授权的应用闯入我们的私人领域。如果receiver声明在manifest配置文件里,且权限应用内部使用,则可在receiver标签上添加一个android:exported=”false”属性。这样系统中的其他应用就再也无法接触到该receiver。另外,也可创建自己的权限。这通常通过在manifest配置文件添加一个permission标签来完成。
<permission android:name="com.example.photogallery.PRIVATE"android:protectionLevel="signature"/><uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="com.example.photogallery.PRIVATE"/>
发送带有权限的broadcast
public static final String PERM_PRIVATE="com.example.photogallery.PRIVATE";sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION), PERM_PRIVATE);
以上代码中,使用protectionLevel签名,我们定义了自己的定制权限。如同intent操作、类别以及系统权限,权限本身只是一行简单的字符串。即使是自定义的权限,也必须在使用前获取它,这是规则。三处出现的字符串“com.example.photogallery.PRIVATE”必须保证完全一致。
要使用权限,必将其作为参数传入sendBroadcast(…)方法里,指定了接收权限,任何应用必须使用同样的权限才能接收我们发送的intent。
要怎么保护我们的broadcast receiver呢?其他应用可通过创建自己的broadcast receiver来触发broadcast receiver。同样,在registerReceiver(…)方法中传入自定义权限即可解决该问题。
public void onResume() {super.onResume();IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);getActivity().registerReceiver(mOnShowNotification, filter, PollService.PERM_PRIVATE, null);}
深入学习protectionLevel
自定义权限必须指定android:protectionLevel属性值。Android根据protectionLevel属性值确定自定义权限的使用方法。
protectionLevel的可选值
使用ordered broadcast接收结果
从概念上讲,常规broadcast intent可同时被其他应用所接收。而现在,onReceive(…)方法是在主线程上调用的,所以实际上,receiver并没有同步并发运行。因而,指望它们会按照某种顺序依次运行,或知道它们什么时候全部结束运行也是不可能的。结果,这给broadcast receiver之间的通讯,或intent发送者接收receiver的信息都带来了麻烦。
为解决问题,可使用有序broadcast intent实现双向通讯。有序broadcast允许多个broadcast receiver依次处理broadcast intent。另外,通过传入一个名为result receiver的特别broadcast receiver,有序broadcast还可以实现让broadcast的发送者接收broadcast接受者发送的返回结果。
从接收方来看,这看上去与常规broadcast没有什么不同。然而,这里我们获得了一个特别工具:一套改变接受者返回值的方法。这里我们需要取消通知信息。可通过一个简单的整数结果码,将此需要告知信息发送者。使用setResultCode(int)方法,设置结果码为Activity.RESULT_CANCELED。
修改VisibleFragment类,将取消通知的信息发送给SHOW_NOTIFICATION的发送者。
private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {setResultCode(Activity.RESULT_CANCELED);}};
既然此处只需发送YES或NO的标志,因此使用int结果码即可。如需返回更多复杂数据,可使用setResultData(String)或setResultExtras(Bundle)方法。如需设置所有三个参数,可调用setResult(int, String, Bundle)方法。设定返回值后,每个后续接受者均可看到或修改返回值。
为让以上方法发挥作用,broadcast必须有序。在PollService类中,编写一个可发送有序broadcast的新方法。该方法打包一个Notification调用,然后作为一个broadcast发出。只要通知信息还没有被撤销,可指定一个result receiver发出打包的Notification。
void showBackgroundNotification(int requestCode, Notification notification) {Intent intent = new Intent(ACTION_SHOW_NOTIFICATION);intent.putExtra("RESULT_CODE", requestCode);intent.putExtra("NOTIFICATION", notification);/*** 除了sendBroadcast(Intent, String)方法中使用的参数外,sendOrderedBroadcast(Intent,* String, BroadcastReceiver, Handler, int, String, Bundle)方法还有另外5个参数,依次为:* 一个result receiver、一个支持result receiver运行的Handler、结果代码初始化值、结果数据以及有序* broadcast的结果附加内容。*/sendOrderedBroadcast(intent, PERM_PRIVATE, null, null,Activity.RESULT_OK, null, null);}
result receiver比较特殊,只有在所有有序broadcast intent的接受者结束运行后,它才开始运行。虽然有时可使用result receiver接收broadcast和发送通知对象,但此处该方法行不通。目标broadcast intent通常是在PollService对象消亡前发出的,也就是说broadcast receiver可能也被销毁了。
因此,最终的broadcast receiver需要保持独立运行。新建一个NotificationReceiver类。
package com.example.photogallery;import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;public class NotificationReceiver extends BroadcastReceiver {private static final String TAG = "NotificationReceiver";@Overridepublic void onReceive(Context context, Intent intent) {if (getResultCode() != Activity.RESULT_OK)return;int requestCode = intent.getIntExtra("RESULT_CODE", 0);Notification notification = (Notification) intent.getParcelableExtra("NOTIFICATION");NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);/*** 传入的整数参数是通知消息的标识的标识符,在整个应用中该值是唯一的。如使用同一ID发送两条消息,则第二条消息会替换掉第一条消息。* 这也是进度条或其他动态视觉效果的实现方式。*/notificationManager.notify(requestCode, notification);}}
登记新建的receiver。既然该receiver负责发送通知消息,并接收其他接受者返回的结果码,它的运行应该在最后。这就需要将receiver的优先级设置为最低。为保证receiver最后运行,设置其优先级值为-999(-1000及以下值属系统保留值)。
另外,既然NotificationReceiver仅限于应用内部使用,我们还需设置属性值为android:exported=”false”,以保证其对外部应用不可见。
<receiver
android:name="com.example.photogallery.NotificationReceiver"android:exported="false" ><intent-filter android:priority="-999" ><action android:name="com.example.photogallery.SHOW_NOTIFICATION" /></intent-filter></receiver>
receiver 与长时运行任务
如不想受限于主线程的时间限制,并希望broadcast intent可触发一个长时运行任务,该怎么做呢?
有两种方式可以选择:
1、将任务交给服务去处理,然后通过broadcast receiver启动服务。这也是推荐的方式。服务可以运行很久,直到完成需要处理的任务。同时服务可将请求放在队列中,然后依次处理,或按其自认为合适的方式管理全部任务请求。
2、使用BroadcastReceiver.goAsync()方法。该方法返回一个BroadcastReceiver.PendingResult对象,随后,我们可使用该对象提供结果。因此,可将PendingResult交给AsyncTask去执行长时运行的任务,然后再调用PendingResult的方法响应broadcast。
BroadcastReceiver.goAsync()方法有两处弊端。首先它不支持旧设备。其次,它不够灵活:我们仍需快速响应broadcast,并且与使用服务相比,没什么架构模式好选择。
当然,goAsync()方法并非一无是处:可通过该方法的调用,完成有序broadcast的结果设置。如果真的要使用它,应注意不要耗时过长。
代码地址
这篇关于广播接受者(Broadcast Receiver)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!