什么是广播

广播是Android SDK的四大组件中唯一需要别动接收数据的组件。也就是说对于Activity、ContentProvider和Service都可以主动调用,并获取返回数据。而负责接收Broadcast数据的接收器却永远不知道什么时候可以接收到广播。从这种表现形式上看,很像面向对象中的事件(Event),对于事件(onClick、onKeydown)来说,从来不会预知用户什么时候触发他们,只能默默的等待不可预知的事件发生。因此,广播也可以被成为全局事件。


接收系统广播

短信拦截(静态注册)

1 编写广播接收器类,继承自android.content.BroadcastReceiver类

ShortMessageReceiver.java

package com.turing.base.activity.broadcastDemo;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.SmsMessage;
import android.widget.Toast;import com.apkfuns.logutils.LogUtils;import java.util.Set;public class ShortMessageReceiver extends BroadcastReceiver {private Handler handler ;public ShortMessageReceiver() {}public ShortMessageReceiver(Handler handler) {this.handler = handler ;}@Overridepublic void onReceive(Context context, Intent intent) {Bundle bundle = intent.getExtras();if (bundle != null) {Set<String> keys = bundle.keySet();//查看收的广播包含哪些数据for (String key : keys) {LogUtils.e("bundele中的数据" + key);}// 获取收到的短信Object[] objArray = (Object[]) bundle.get("pdus");String message = parseMessageFromRawData(objArray);Toast.makeText(context, message, Toast.LENGTH_SHORT).show();Message msg = new Message();msg.what = 1 ;msg.obj = message;handler.sendMessage(msg);/**// 定义封装短信内容的SmsMessage对象数组SmsMessage[] message = new SmsMessage[objArray.length];// 循环处理收到的所有短信for (int i = 0; i < objArray.length; i++) {// 将每条短信数据转换成SendMessage对象message[i] = SmsMessage.createFromPdu((byte[]) objArray[i]);// 获取发送短信的电话号码和短信内容String messageInfo = "手机号:" + message[i].getOriginatingAddress() + "\n";messageInfo += "短信内容:" + message[i].getDisplayMessageBody();//做个简单的展示Toast.makeText(context, messageInfo, Toast.LENGTH_SHORT).show();}**/}}public  String  parseMessageFromRawData(Object[] pdus) {if (pdus == null) return null;try {StringBuilder message = new StringBuilder();for (Object pdu : pdus) {SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu);if (smsMessage == null) continue;message.append("源号码:" + smsMessage.getOriginatingAddress() + ",内容:" +smsMessage.getDisplayMessageBody());}return message.toString();} catch (Exception e) {LogUtils.e( "SMSBroadcastReceiver read sms failed", e);} catch (OutOfMemoryError oom) {LogUtils.e( "SMSBroadcastReceiver caused OOM =_=!", oom);//为了避免后续操作出现问题,gc一下System.gc();System.gc();}return null;}
}

清单文件配置Receiver

 <receiver
            android:name=".activity.broadcastDemo.ShortMessageReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="android.provider.Telephony.SMS_RECEIVED" /></intent-filter></receiver>

清单文件配置权限

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

注意事项

  • 如果不知道广播中包含哪些数据,可以从Bundle.keySet()方法中获取这些数据的key,将其输出到Logcat中查看,如上述代码所示
  • 由于接受的短信内容是以字节数组的形式保存的,为了方便使用这些数据,需要使用SmsMessage.createFromPdu方法将这些字节数据组成的数据转换为SmsMessage对象
  • SmsMessage建议使用android.telephony.SmsMessage中的。
  • 由于接收器可能接收多条短信,因此通过pdus返回了一个短信数组。
  • 必须要指定<action android:name="android.provider.Telephony.SMS_RECEIVED" /> 我们编写的短信接收器才可以接收系统的短信广播,切记
  • 配置权限android.permission.RECEIVE_SMS
  • 即使注册广播接收器的程序关闭,接收器仍然会接收到广播,除非从模拟器或者手机中卸载程序或者注销接收器,否则无法阻止接收器接收广播

用代码注册广播接收器

如果在清单文件中配置广播接收器,程序安装后就会自动注册广播接收器,如果想在适当的时候注册广播接收器,在使用完成之后将其注销就需要使用Java代码来操作了。

注册和取消方法

注册广播接收器的方法是 registerReceiver,注销的方法是unregisterReceiver,定义如下:

 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)public void unregisterReceiver(BroadcastReceiver receiver) 

其中receiver表示广播接收器对象,
filter参数相当于设置intent-filter标签中的内容。

Code

package com.turing.base.activity.broadcastDemo;import android.app.Activity;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;import com.turing.base.R;public class SmsMessageAct extends Activity implements View.OnClickListener {private EditText et_phone, et_msg;private Button btn_registerReceiver, btn_unRegisterReceiver;private ShortMessageReceiver shortMessageReceiver;private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 1:String message = (String) msg.obj;String pre = "源号码:";String suf = ",内容:";et_phone.setText(message.substring((message.indexOf(pre) + pre.length()), message.indexOf(suf)));et_msg.setText(message.substring(message.indexOf(suf) + suf.length(), message.length()));break;default:break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_sms_message);initView();initEvents();// 创建广播接收器的对象shortMessageReceiver = new ShortMessageReceiver(handler);}private void initEvents() {btn_registerReceiver.setOnClickListener(this);btn_unRegisterReceiver.setOnClickListener(this);}private void initView() {et_phone = (EditText) findViewById(R.id.id_et_phone);et_msg = (EditText) findViewById(R.id.id_et_msg);btn_registerReceiver = (Button) findViewById(R.id.id_btn_registerReceiver);btn_unRegisterReceiver = (Button) findViewById(R.id.id_btn_unRegisterReceiver);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.id_btn_registerReceiver:registerReceiver(shortMessageReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));Toast.makeText(this, "动态注册广播接收器成功", Toast.LENGTH_SHORT).show();break;case R.id.id_btn_unRegisterReceiver:unregisterReceiver(shortMessageReceiver);Toast.makeText(this, "动态注销短信广播接收器over", Toast.LENGTH_SHORT).show();break;default:break;}}@Overrideprotected void onPause() {super.onPause();if (shortMessageReceiver != null) {unregisterReceiver(shortMessageReceiver);Toast.makeText(this, "Activity onPause ,注销短信广播接收器over", Toast.LENGTH_SHORT).show();}}
}

广播的优先级

android:priority

通过intent-filter标签的android:priority属性可以设置接收器的调用优先级,该属性值属于一个整数,数值越大,优先级越高。

Code

 <receiver android:name=".activity.service.StartupReceiver"><intent-filter android:priority="100"><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver>
  • 如果不设置优先级别,对于同一个应用程序中的广播接收器会按照在Manifest清单文件中定义的顺序调用。

    • 广播的优先级只是对同步处理方式起作用,如果在接收器中使用了异步处理方式,则调用的顺序除了和优先级有关,还和Android系统的线程调用有关。

来去电拦截

广播动作

监听电话状态以用于拦截来去电,来电(监听电话状态)和去电的广播动作如下:

  • 来电:android.intent.action.PHONE_STATE
  • 去电:android.intent.action.NEW_OUTGOING_CALL

来电可以分解为3个状态:未接电话时的响铃,接听电话 和挂断电话(可能是对方挂断,也可能是自己挂断)

监听这三个状态的代码如下(使用静态方式注册的广播):

CallInReceiver:

package com.turing.base.activity.broadcastDemo;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;import com.turing.base.R;public class CallInReceiver extends BroadcastReceiver {private static Object obj;public CallInReceiver() {}@Overridepublic void onReceive(Context context, Intent intent) {// 获取电话管理服务,以便获取电话的状态TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);switch (telephonyManager.getCallState()) {case TelephonyManager.CALL_STATE_RINGING: // 响铃String incomingNumber = intent.getStringExtra("incoming_number");Toast.makeText(context, "电话响铃中......", Toast.LENGTH_SHORT).show();//showPopupWindowToast(context,incomingNumber);break;case TelephonyManager.CALL_STATE_OFFHOOK: //接听电话Toast.makeText(context, "电话已接通......", Toast.LENGTH_SHORT).show();break;case TelephonyManager.CALL_STATE_IDLE:// 挂断电话Toast.makeText(context, "挂断电话......", Toast.LENGTH_SHORT).show();//closeToast();default:break;}}/*** 使用反射,此Toast不会关闭** @param context* @param msg*/
//    public static void showToast(Context context, String msg) {//        Toast toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
//        toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0);
//        try {//            Field field = toast.getClass().getDeclaredField("mTN");
//            field.setAccessible(true);
//            obj = field.get(toast);
//            Method method = obj.getClass().getDeclaredMethod("show", null);
//            method.invoke(obj, null);
//        } catch (Exception e) {//        }
//
//    }/*** 通过此方法关闭那个不可关闭的Toast*/
//    public static void closeToast() {//        if (obj != null) {//            try {//                Method method = obj.getClass().getDeclaredMethod("hide", null);
//                method.invoke(obj, null);
//            } catch (Exception e) {//            }
//
//        }
//    }public static void showPopupWindowToast(Context context, String incomingNumber) {LayoutInflater inflater  = LayoutInflater.from(context);View view = inflater.inflate(R.layout.activity_popupwd_toast, null);TextView textView = (TextView)view.findViewById(R.id.tvMsg);textView.setText("电话号码:" + incomingNumber);final PopupWindow popupWindow = new PopupWindow(view,500 ,100);popupWindow.setTouchable(false);popupWindow.showAtLocation(view, Gravity.CENTER_HORIZONTAL,20 ,0);// 设置定时器,5秒后自动关闭android.os.Handler handler = new android.os.Handler();handler.postDelayed(new Runnable() {@Overridepublic void run() {popupWindow.dismiss();}} , 5*1000);}}

CallOutReceiver

package com.turing.base.activity.broadcastDemo;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;public class CallOutReceiver extends BroadcastReceiver {public CallOutReceiver() {}@Overridepublic void onReceive(Context context, Intent intent) {// 获取去电号码String outComingNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);// showToastToast.makeText(context, "去电......" + outComingNumber, Toast.LENGTH_SHORT).show();//CallInReceiver.showPopupWindowToast(context, outComingNumber);}
}
 <!-- 来电 --><receiver
            android:name=".activity.broadcastDemo.CallInReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="android.intent.action.PHONE_STATE" /></intent-filter></receiver><!-- 去电 --><receiver
            android:name=".activity.broadcastDemo.CallOutReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="android.intent.action.NEW_OUTGOING_CALL" /></intent-filter></receiver>

设置权限

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

截获屏幕休眠与唤醒

按手机上的挂断按钮后,手机会进入休眠状态(屏幕变黑),当再此按下手机的任意键后,屏幕会唤醒(屏幕变量)。这两个动作可以通过如下两个动作连接

广播动作

  • 休眠状态 Intent.ACTION_SCREEN_OFF
  • 唤醒状态 Intent.ACTION_SCREEN_ON
 private void screenOnOff() {ScreenOnOffReceiver screenOnOffReceiver = new ScreenOnOffReceiver();IntentFilter intentFilter = new IntentFilter();// 设置屏幕唤醒广播的动作intentFilter.addAction(Intent.ACTION_SCREEN_ON);// 设置屏幕休眠广播的动作intentFilter.addAction(Intent.ACTION_SCREEN_OFF);// 注册registerReceiver(screenOnOffReceiver,intentFilter);}
package com.turing.base.activity.broadcastDemo;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;public class ScreenOnOffReceiver extends BroadcastReceiver {public ScreenOnOffReceiver() {}@Overridepublic void onReceive(Context context, Intent intent) {// 接收屏幕唤醒状态的广播if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {Log.d("screen", "ok");} else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {Log.d("screen", "off");}}}

注意事项:

屏幕唤醒和休眠的广播,只能通过代码的以动态的方式注册,如果在清单文件中配置,则不起作用。


开机自动运行

广播动作

android.intent.action.BOOT_COMPLETED

Code

package com.turing.base.activity.service;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;import com.apkfuns.logutils.LogUtils;/*** 只要完成两项工作: 启动服务 和 显示一个Activity提示服务启动成功(主题设置为Dialog的形式)*/
public class StartupReceiver extends BroadcastReceiver {public StartupReceiver() {}@Overridepublic void onReceive(Context context, Intent intent) {LogUtils.e("StartupReceiver  onReceive");// 如果是开机启动的Actionif(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){// 启动ActivityIntent activityIntent = new Intent(context,BootCompletedMessageAct.class);// 想要在Service中启动Activity,必须设置如下标志activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(activityIntent);//启动服务Intent serviceIntent = new Intent(context,StartupService.class);context.startService(serviceIntent);}}
}
  <!-- 开机广播 --><receiver android:name=".activity.service.StartupReceiver"><intent-filter ><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver>

显示手机电池的当前电量

查看电池的电量也需要接收一个系统广播,本demo是通过registerReceiver方法进行注册的。

广播动作

Intent.ACTION_BATTERY_CHANGED

Code

package com.turing.base.activity.broadcastDemo;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;import com.turing.base.R;public class BatteryInfoAct extends AppCompatActivity {private TextView tv_batteryInfo;private BroadcastReceiver batteryChangedReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {//level标识当前电量的值int level = intent.getIntExtra("level", 0);// scale标识电量的总刻度int scale = intent.getIntExtra("scale", 100);// 将当前电量换算成百分比的形式tv_batteryInfo.setText("电池用量:" + (level * 100 / scale) + "%");}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_battery_info);tv_batteryInfo = (TextView) findViewById(R.id.id_tv_battery);// 注册广播registerReceiver(batteryChangedReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));}@Overrideprotected void onPause() {super.onPause();if (batteryChangedReceiver != null) {unregisterReceiver(batteryChangedReceiver);}}
}

发送广播

sendBoradcast

可以通过sendBoradcast的方式发送广播
方法定义如下:

public void sendBroadcast(Intent intent)

下面的代码发送了一个广播,并添加了广播数据和category

// 指定广播动作
Intent brdcstIntent= new Intent("com.turing.demo.sendbrdcst.MYBROADCAST");
// 添加category
brdcstIntent.addCategory("xxx.xxx.xxx");
// 设置广播数据
brdcstIntent.putExtra("name","XXXXX");
// 发送广播
sendBoradcast(brdcstIntent);

Code

两个工程,proj_send_broadcast, proj_custom_receiver

proj_send_broadcast

public class Main extends Activity
{/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);}public void onClick_Send_Broadcast(View view){Intent broadcastIntent = new Intent("mobile.android.ch10.MYBROADCAST");broadcastIntent.addCategory("mobile.android.ch10.mycategory");broadcastIntent.putExtra("name", "broadcast_data");sendBroadcast(broadcastIntent);Toast.makeText(this, "广播发送成功.", Toast.LENGTH_LONG).show();}
}

proj_custom_receiver

CustomReceiver

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;public class CustomReceiver extends BroadcastReceiver
{@Overridepublic void onReceive(Context context, Intent intent){if ("mobile.android.ch10.MYBROADCAST".equals(intent.getAction())){String name = intent.getStringExtra("name");Toast.makeText(context, name, Toast.LENGTH_LONG).show();}}}

清单文件

    <receiver android:name=".CustomReceiver"><intent-filter><action android:name="mobile.android.ch10.MYBROADCAST" /><category android:name="mobile.android.ch10.mycategory" /></intent-filter></receiver>

验证广播接收器是否注册

 private void validateReceiver(String actionName) {// 获取PackageManagerPackageManager packageManager = getPackageManager();// 指定要查询广播的动作Intent intent = new Intent(actionName);// 返回已查到的广播接收器集合,如果没有符合条件的广播,List长度为0List<ResolveInfo> resolveInfos = packageManager.queryBroadcastReceivers(intent,PackageManager.GET_INTENT_FILTERS);// 显示查询到的广播的数量Toast.makeText(this,"已发现" + resolveInfos.size() + "个接收去电广播的接收器" ,Toast.LENGTH_SHORT).show();StringBuilder sb = new StringBuilder();if(resolveInfos.size() > 0 ){for (ResolveInfo resolveInfo : resolveInfos){sb.append(resolveInfo.toString());}}Toast.makeText(this,sb.toString() ,Toast.LENGTH_SHORT).show();}

全局事件-广播(Broadcast)相关推荐

  1. 理解微信小程序Wepy框架的三个事件交互$broadcast,$emit,$invoke

    $broadcast: $broadcast事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消.事件广播的顺序为广度优先搜索顺序,如上图,如果页面Page_Index发起一个$bro ...

  2. 微信小程序Wepy框架的三个事件交互($broadcast,$emit,$invoke)

    $broadcast: broadcast事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消.事件广播的顺序为广度优先搜索顺序,如上图,如果页面PageIndex发起一个broadc ...

  3. Android 广播(Broadcast)

    Android 广播Broadcast 1.小声嘀咕 2.广播机制简介 3.接收系统广播 3.1动态注册 3.2静态注册 4.发送自定义广播 4.1标准广播 4.2有序广播 5.使用本地广播 1.小声 ...

  4. vue php聊天室,实时聊天室:基于Laravel+Pusher+Vue通过事件广播实现

    之前有说过要整理出一篇事件广播的教程,今天终于有时间把这篇文章给写了出来,本次的教程是基于Laravel+Pusher+Vue,以事件广播作为核心技术,让你可以快速搭建起一个实时聊天室应用,话不多说, ...

  5. pusher 创建新应用_基于 Laravel + Pusher + Vue 通过事件广播构建实时聊天室应用

    基于 Laravel + Pusher + Vue 通过事件广播构建实时聊天室应用 由 学院君 创建于2年前, 最后更新于 3个月前 版本号 #3 前言:学院君之前有说过要整理出一篇事件广播手把手教程 ...

  6. jquery之ajax——全局事件引用方式以及各个事件(全局/局部)执行顺序

    jquery中各个事件执行顺序如下: 1.ajaxStart(全局事件) 2.beforeSend(局部事件) 3.ajaxSend(全局事件) 4.success(局部事件) 5.ajaxSucce ...

  7. 「后端小伙伴来学前端了」Vue中全局事件总线(GlobalEventBus)原理及探究过程

    前言 上一篇文章写了 Vue 中的自定义事件,自定义事件是全局事件总线基础.我在上一篇文章中埋下了一个小小的伏笔.如下图: 我说过,在Vue中如果我们用(@orv-on)给组件绑定上一个自定义事件,其 ...

  8. 「后端小伙伴来学前端了」Vue中利用全局事件总线实现组件之间通信

    月亮啊月亮 你能照见南边,也能照见北边 照见她,你跟她说一声,就说我想她了. 前言 前一篇文章写了 vue 中利用 Props 实现组件之间的通信,那种方式是最简单也是最基础的组件之间的通信方式.父组 ...

  9. B08_NumPy 广播(Broadcast)

    NumPy 广播(Broadcast) 广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的算术运算通常在相应的元素上进行. 如果两个数组 a 和 b ...

最新文章

  1. Introduce Intelligence to Your Security Operations
  2. 抓住\留住用户的引导页长什么样?
  3. Spring cloud Gateway介绍
  4. LuoguP4012 深海机器人问题(费用流)
  5. 数据库系统概论第五版(第 1 章 绪论)习题答案
  6. Charles青花瓷抓包
  7. IDEA jsp中文乱码
  8. 报修管理系统微信小程序源码
  9. nginx 499错误原因及解决
  10. HR面试程序员,一般第一个问题会问什么
  11. MFC ShowWindow参数
  12. sun java 考试_Sun Java认证考试科目
  13. 超大容量充电宝哪个好,大容量移动充电宝哪个品牌好?
  14. linux r语言内存查看,R语言统计与分布的相关知识
  15. 史上最详细的Vmware安装教程(一)-创建Linux虚拟机
  16. LOJ 6485 LJJ学多项式
  17. 7时过2小时是几时_餐后2小时血糖正常值是多少?
  18. IIS7应用程序池停止后,如何自动启动
  19. 锐捷RG-S3760交换机配置Telnet、SSH、DHCP示例
  20. 学习meshlab(1)——基本的edit工具学习

热门文章

  1. sql array 数组基本用法(四)
  2. linux 更改文件所有者
  3. vuex模块化 怎么引用state_[Vuex系列] - 细说state的几种用法
  4. 76. Leetcode 295. 数据流的中位数 (堆-技巧一-固定堆)
  5. 机器学习笔记:反向传播
  6. 职业大揭秘,算法攻城狮在日常工作中都干了些啥?
  7. Python中时间戳与时间字符串相互转换
  8. python线程同步锁_Python实现的多线程同步与互斥锁功能示例
  9. Python--切片学习记录
  10. Java中导入错误的jar所引发的问题