广播机制概述
Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者。广播作为Android组件间的通信方式,可以使用的场景如下:
- 1.同一app内部的同一组件内的消息通信(单个或多个线程之间);
- 2.同一app内部的不同组件之间的消息通信(单个进程);
- 3.同一app具有多个进程的不同组件之间的消息通信;
- 4.不同app之间的组件之间消息通信;
- 5.Android系统在特定情况下与App之间的消息通信。
- 1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
- 2.广播发送者通过binder机制向AMS发送广播;
- 3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息队列中;
- 4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
- 第一种情形:实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题,若适用广播机制,显然有些“杀鸡牛刀”的感觉,会显太“重”;
- 第二种情形:对于此类需求,在有些较复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对统一进程,用于处理此类需求非常适合,且轻松解耦。
- 第三四五种情形:由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。
APP退出后静态Receiver能否接收到广播的问题
我们可能一直有一个观点:静态注册的广播接收者即使app已经退出,只要有相应的广播发出,此广播接收者依然可以接收到广播。 但此种描述自Android 3.1开始对于系统广播不再成立了! Android 3.1开始,系统在Intent中增加了与广播相关的flag参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。 FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(即包所在的进程已经退出) FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包 主要原因是: 自Android3.1开始,系统本身增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收者,对于其所在进程已经退出的app,同样无法接收到广播。 因此,对于系统广播,由于是系统内部直接发出的,无法更改此flag值,所以,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到系统广播。 但是对于自定义的广播,由于我们可以手动设置此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即使所在App进程已经退出,也能能接收到广播,并会启动应用进程。 在3.1以前,相信不少app可能通过静态注册方式监听各种系统广播,以此进行一些业务上的处理。但3.1以后,静态注册接受广播方式的改变,将直接导致此类方案不再可行。于是,通过将Service与App本身设置成不同的进程已经成为实现此类需求的可行的替代方案。
MainActivity
public class MainActivity extends ListActivity { private TelephonyManager tm; private MyBRReceiver myReceiver; public static final String MY_BROADCAST_ACTION_UNORDERED = "com.bqt.broadcast.songwennuan_unordered"; public static final String MY_BROADCAST_ACTION_ORDERED = "com.bqt.broadcast.songwennuan_ordered"; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String[] array = { "动态注册广播,监听网络状态变化", "取消注册广播", "发送自定义的无序广播", "发送自定义的有序广播" }; ListAdapter mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))); setListAdapter(mAdapter); tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); tm.listen(new MyPhoneStateListener(this), PhoneStateListener.LISTEN_CALL_STATE);//权限 READ_PHONE_STATE //动态注册。需要程序启动才可以接收广播。一般在onCreate中注册,在onDestroy中取消注册。 myReceiver = new MyBRReceiver(); registerReceiver(myReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));//权限:ACCESS_NETWORK_STATE } @Override protected void onListItemClick(ListView l, View v, int position, long id) { switch (position) { case 0://可以重复注册,不会有什么异常 registerReceiver(myReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));//权限:ACCESS_NETWORK_STATE Toast.makeText(this, "已注册", Toast.LENGTH_SHORT).show(); break; case 1: try { unregisterReceiver(myReceiver);//不管注册多少次,只能取消注册一次,若多次取消或在没有注册时取消会报IllegalArgumentException Toast.makeText(this, "已取消注册", Toast.LENGTH_SHORT).show(); } catch (Exception e) { Toast.makeText(this, "异常啦!", Toast.LENGTH_SHORT).show(); } break; case 2: sendUnOrderedBroadcast(); break; case 3: sendOrderedBroadcast(); break; } } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(myReceiver);//如果已取消注册或没有注册,会报错:Unable to destroy activity… : IllegalArgumentException: Receiver not registered… } //******************************************************************************************************************************** //发送无序广播。不可被拦截,不可终止。 public void sendUnOrderedBroadcast() { Intent intent = new Intent(MY_BROADCAST_ACTION_UNORDERED);//自定义广播动作。广播一般用于【应用程序之间】消息的传递,使用隐式意图。 intent.putExtra("msg", "======发1万块======"); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);//包含已经停止的包。设置此Flag后,即使APP已退出,能匹配此Action的广播接收者依然可以接收到此广播 //但是对于系统广播,由于Intent是系统内部直接发出的,无法更改此flag值,所以,3.1以后,如果App进程已经退出,广播接收者将不能接收到系统广播 sendBroadcast(intent); } //发送有序广播。可被拦截,可终止,可以修改数据。 public void sendOrderedBroadcast() { Intent intent = new Intent(MY_BROADCAST_ACTION_ORDERED); sendOrderedBroadcast(intent, null, null, null, Activity.RESULT_OK, "-----------给农民兄弟发10000块钱----------------", null); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); //String receiverPermission 只有指定权限的接受者才能接收到广播;BroadcastReceiver 一定会收到广播的接收者,不可以被拦截,但接收到的数据可被改变 sendOrderedBroadcast(intent, null);//发送一条空广播 } private class MyBRReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //广播中不允许开辟线程, onReceiver()运行超过10秒会ANR。广播更多的时候扮演的是一个"启动者"的角色,比如收到广播后启动Service,Notification,Activity等 NetWorkEnum netEnum = NetUtils.getNetWorkState(MainActivity.this); Toast.makeText(context, "网络状态:" + netEnum, Toast.LENGTH_SHORT).show(); } } }
来电、去电、短信的BroadCastReceiver
/**监听来电、去电、短信到来*/ public class PhoneAndSmsReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //This method is called when the BroadcastReceiver is receiving an Intent broadcast。 String resultData = getResultData(); //Retrieve the current result data, as set by the previous receiver. Often this is null. if (intent != null) { if (Intent.ACTION_NEW_OUTGOING_CALL.equalsIgnoreCase(intent.getAction())) {//去电。权限:CALL_PHONE doWhenTo(context, resultData); } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equalsIgnoreCase(intent.getAction())) {//电话状态改变,和state一样,有三种状态 if (TelephonyManager.EXTRA_STATE_RINGING.equalsIgnoreCase(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {//来电状态 doWhenFrom(context, resultData); } } else if (android.provider.Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equalsIgnoreCase(intent.getAction())) {//沃日,这个字段隐藏的好深啊 doWhenGetSms(context, intent); } } } /**当去电时,发送一个通知。这里可以直接获取到去电号码*/ private void doWhenTo(Context context, String resultData) { Intent callIntent = new Intent(Intent.ACTION_CALL); callIntent.setData(Uri.parse("tel:" + resultData));//接听到的内容就是电话号码,如186******** PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, callIntent, 0);//注意,这里要用getActivity,因为PendingIntent的【目的】是打开拨号界面 Notification notification = new Notification.Builder(context).setSmallIcon(R.drawable.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher))// .setTicker("TickerText:" + "监听到去电!").setAutoCancel(true).setContentTitle("包青天提醒你").setContentText("去电号码为" + resultData)// .setContentIntent(pendingIntent).setNumber(7).setWhen(System.currentTimeMillis()).build();//这个方法API 16 及之后才可使用 ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(110, notification);//使用同一个id以替换旧的通知 } /**当来电时,弹一个土司。注意,这里并不能获取到来电号码*/ private void doWhenFrom(Context context, String resultData) { Toast.makeText(context, "监听到来电,resultData是否为null:" + (resultData == null), Toast.LENGTH_LONG).show();//这里获取到的resultData为null } /**当收到短信时,弹一个土司 */ private void doWhenGetSms(Context context, Intent intent) { Object[] objs = (Object[]) intent.getExtras().get("pdus"); for (Object obj : objs) { SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) obj); String sender = smsMessage.getOriginatingAddress(); String body = smsMessage.getMessageBody(); Toast.makeText(context, "收到消息-" + sender + "--" + body, Toast.LENGTH_LONG).show(); } abortBroadcast();//根本干不过系统短信:①没启动时收不到此广播(这个很正常) ②启动时能收到但不能终止广播(估计优先级不够) } }
电话状态监听PhoneStateListener
/**使用TelephonyManager监听电话状态。权限:READ_PHONE_STATE*/ public class MyPhoneStateListener extends PhoneStateListener { private Context context; public MyPhoneStateListener(Context context) { super(); this.context = context; } public void onCallStateChanged(int state, String incomingNumber) { super.onCallStateChanged(state, incomingNumber); switch (state) { case TelephonyManager.CALL_STATE_IDLE: break; case TelephonyManager.CALL_STATE_OFFHOOK: break; case TelephonyManager.CALL_STATE_RINGING: Toast.makeText(context, "监听到来电,号码为:" + incomingNumber, Toast.LENGTH_LONG).show(); break; } } }
开机启动广播接收者
/** Android 4.3以上允许将应用安装在SD卡上,系统开机间隔一小段时间后才装载SD卡,为能获取开机广播,我们需要既监听开机广播又监听SD卡挂载广播 */ public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "开机啦,赶快干坏事~", Toast.LENGTH_LONG).show();//基本上都会被手机屏蔽掉 Intent mIntent = new Intent(context, MainActivity.class); mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//在广播中启动Activity需要添加FLAG_ACTIVITY_NEW_TASK,因为需要一个栈来存放Activity context.startActivity(mIntent); } }
网络工具类
public class NetUtils { /** * 检查是否联网 */ public static boolean checkNetwork(Context context) { ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo network = manager.getActiveNetworkInfo(); if (network == null) { return false; } return network.isAvailable();//或使用 return network.isConnectedOrConnecting(); } /** * 判断当前网络连接是否为wifi */ public static boolean isWifiConnection(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); if (activeNetInfo != null && activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) { return true; } return false; } /** * 网络类型 */ public static enum NetWorkEnum { NETWORK_NONE, NETWORK_WIFI, NETWORK_2G, NETWORK_3G, NETWORK_4G } /** * 判断当前使用的网络状态 * 返回值: 0: 没网络 1:WIFI 2:2G 3: 3G 4: 4G */ public static NetWorkEnum getNetWorkState(Context context) { ConnectivityManager connectivityMag = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivityMag != null) { // 获取WIFI网络连接状态 State wifiState = connectivityMag.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); if (State.CONNECTED == wifiState || State.CONNECTING == wifiState) return NetWorkEnum.NETWORK_WIFI; // 检查是否有网络连接 NetworkInfo netWorkInfo = connectivityMag.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); if (netWorkInfo == null) return NetWorkEnum.NETWORK_NONE; else { State tempState = netWorkInfo.getState(); if (State.CONNECTED == tempState || State.CONNECTING == tempState) return getNetworkClass(netWorkInfo.getSubtype()); else return NetWorkEnum.NETWORK_NONE; } } return NetWorkEnum.NETWORK_NONE; } /** * 判断网络类型 */ private static NetWorkEnum getNetworkClass(int networkType) { switch (networkType) { case TelephonyManager.NETWORK_TYPE_GPRS: case TelephonyManager.NETWORK_TYPE_EDGE: case TelephonyManager.NETWORK_TYPE_CDMA: case TelephonyManager.NETWORK_TYPE_1xRTT: case TelephonyManager.NETWORK_TYPE_IDEN: return NetWorkEnum.NETWORK_2G; case TelephonyManager.NETWORK_TYPE_LTE: return NetWorkEnum.NETWORK_4G; case TelephonyManager.NETWORK_TYPE_UMTS: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_EVDO_A: case TelephonyManager.NETWORK_TYPE_HSDPA: case TelephonyManager.NETWORK_TYPE_HSUPA: case TelephonyManager.NETWORK_TYPE_HSPA: case TelephonyManager.NETWORK_TYPE_EVDO_B: case TelephonyManager.NETWORK_TYPE_EHRPD: case TelephonyManager.NETWORK_TYPE_HSPAP: default: return NetWorkEnum.NETWORK_3G; } } }
清单文件
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.bqt.broadcastreceiver" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="21" /> <!-- 获取网络信息状态 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 访问电话状态,使用TelephonyManager时需要 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <!-- 监视、修改、放弃播出电话 --> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> <uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <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" > <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> <!-- —————————————————————-系统广播————————————————————— --> <!-- android:permission:如果设置,具有此权限的【广播发送者】发送的广播才能被此【广播接收者】所接收 --> <!-- android:process:指定运行时所处的进程。默认为APP的进程,四大组件都可以通过此属性指定自己的独立进程 --> <!-- android:exported:能否接收其他App(并非以进程为界)的发出的广播。同activity、service一样,如果有intent-filter默认值为true,否则为false --> <receiver android:name=".PhoneAndSmsReceiver" > <intent-filter> <!-- 静态注册的广播接收器即使app已经退出,只要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立 --> <action android:name="android.intent.action.NEW_OUTGOING_CALL" /> <action android:name="android.intent.action.PHONE_STATE" /> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> <!-- Android 4.3以上允许将应用安装在SD卡上,系统开机间隔一小段时间后才装载SD卡,所以我们需要既监听开机广播又监听SD卡挂载广播! --> <receiver android:name=".BootCompleteReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> <intent-filter> <action android:name="ANDROID.INTENT.ACTION.MEDIA_MOUNTED" /> <action android:name="ANDROID.INTENT.ACTION.MEDIA_UNMOUNTED" /> <data android:scheme="file" /> </intent-filter> </receiver> </application> </manifest>
附件列表
时间: 2024-12-07 19:57:48