BroadCastReceiver

BroadCastReceiver组件

  1. 广播的发送与接收
  2. 自定义权限广播
  3. 特殊的广播与接收者
  4. Android系统常见的广播事件
  5. 电话拦截的实现,拓展内容,涉及反射底层源代码
  6. 广播的其他特点
01-广播的发送与接收
  1. 无序广播特点:无序广播不可以被拦截,数据不能够修改,sendBroadCast(Intent)

    普通广播是完全异步(就是不会被某个广播接收者终止)的,可以在同一时刻(逻辑上)被所有接收者接收到(其实被接收者接收到也是由顺序的,接收者配置的优先级越高,越先接收到,也就是说广播接收者的优先级对于无序广播也是有用的),消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传播。

  2. 有序广播特点:有序广播可以被拦截abortBroadcast(),数据可以被修改,sendOrderedBroadCast(Intent)

    有序广播是按照接收者声明的优先级别,被接收者依次接收广播。如:A 接收者的级别高于B,B的级别高于C,那么,广播先传给A,再传给B,最后传给C 。在传递的过程中如果有某个接收者终止(abortBroadCast)了该广播,那么后面的接收者就接收不到该广播。


public class MainActivity extends Activity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            handler = new Handler();
            Looper.loop();
        }
    }).start();
}
// 发送自定义的无序广播
public void sendNormal(View view) {
    Intent intent = new Intent();

    intent.setAction("com.itheima.mybroadcast.action");

    intent.putExtra("data", "这是我给你发送的数据");
    // 发送广播(无序)
    sendBroadcast(intent);
}
// 发送自定义的有序广播
public void sendOrder(View view) {
    Intent intent = new Intent();
    intent.setAction("com.itheima.orderedbroadcast.sendmoney");
    /*
     * intent虽然可以携带数据,但是当前我们的业务需求上,让携带的数据可以被广播接收者修改.因此数据不能放到intent里面.
     */
    // intent.putExtra("money", "1w");
    /*
     * 参数1:intent 参数2:广播接收者应该具备的权限,如果不需要写null
     * 参数3:最终广播接收者,是有序广播接收者中永远都是最后一个执行的.
     * 参数4:handler对象,决定第三个参数(BroadCastReceiver中)onReceive()方法在哪个线程中执行,
     * 如果该handler是在主线程中创建的,那么参数3中的方法就在主线程中调用,如果为null,默认在主线程中.
     * 参数5/6/7:发送有序广播时,携带的初始化数据.之所有有三个,第一个是int[类型,第二个是String,第三个是Bundle
     */
    sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String resultData = getResultData();
            Log.d("tag", "我是最终广播接收者:我收到的数据是:" + resultData);
            Log.d("tag", "ThreadName=" + Thread.currentThread().getName());
        }
    }, handler, 0, "每人发1w元.", null);
}

//无序广播的接收
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String stringExtra = intent.getStringExtra("data");
        Toast.makeText(context, "收到广播:" + stringExtra, Toast.LENGTH_SHORT).show();
    }
}
// 有序广播的多级接收:
// 优先级第一级
public class ProvinceReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 1. 先获取到初始化的数据
        String resultData = getResultData();
        Log.d("tag", "省政府收到的钱是:" + resultData);
        Log.d("tag", "省政府线程:" + Thread.currentThread().getName());
        // 拦截广播
        abortBroadcast();
        // 2. 修改数据,然后再设置出去
        // setResultData("每人5k元.");
    }
}

// 优先级第二级
public class CityReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        /*
         * 1. 先获取到初始化的数
         */
        String resultData = getResultData();
        Log.d("tag", "市政府收到的钱是:" + resultData);
        /*
         * 2. 修改数据,然后再设置出去
         */
        setResultData("每人1k元.");
    }
}
//XML文件的注册.
<receiver android:name="com.example.receivermyBroadcast.MyReceiver">
     <intent-filter >
         <action android:name="com.itheima.mybroadcast.action"/>
     </intent-filter>
 </receiver>

 <receiver android:name="com.example.receivermyBroadcast.ProvinceReceiver">
     <intent-filter android:priority="1000" >
         <action  android:name="com.itheima.orderedbroadcast.sendmoney"/>
     </intent-filter>
 </receiver>

 <receiver android:name="com.example.receivermyBroadcast.CityReceiver">
     <intent-filter android:priority="500" >
         <action  android:name="com.itheima.orderedbroadcast.sendmoney"/>
     </intent-filter>
 </receiver>

 <receiver android:name="com.example.receivermyBroadcast.CountryReceiver">
     <intent-filter android:priority="100" >
         <action  android:name="com.itheima.orderedbroadcast.sendmoney"/>
     </intent-filter>
 </receiver>
02. 权限自定义广播
//发送广播
public void send(View view){
    Intent intent = new  Intent();
    intent.setAction("com.itheima.mypermission.action");
    //参数2:自定义的权限
    sendBroadcast(intent, "com.itheima.permission.receiveMyBroadcast");
}
//XML文件实现
<!-- 声明自定义权限 -->
<permission android:name="com.itheima.permission.receiveMyBroadcast" android:protectionLevel="normal"></permission>
<!-- 还需使用自己的权限 -->
<uses-permission android:name="com.itheima.permission.receiveMyBroadcast"/>
注意这里有个权限等级,如果是危险的级别,在6.0的系统需要我们去动态去注册该权限

//接收广播
<!-- 使用别人的权限 -->
<uses-permission android:name="com.itheima.permission.receiveMyBroadcast"/>
  <!-- 注册该权限就可以了-->
<receiver android:name="com.example.receiverPermissionBroad.MyReceiver">
     <intent-filter >
           <action android:name="com.itheima.mypermission.action"></action>
      </intent-filter>
</receiver>
03. 特殊的广播与接收者
  1. 特殊的广播与接收者,频繁的操作:锁屏与解屏,电量的变化等
  2. 用动态注册方式注册,注意需要反注册,清单文件里注册无效

//锁屏与解屏,注意:在这个地方,只要界面一退出,该广播就失效可以采用在服务中注册广播的方式,
public class MainActivity extends Activity {
private ScreenReceiver screenReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    screenReceiver = new ScreenReceiver();
    IntentFilter intentFilter = new IntentFilter();
    // IntentFilter可以添加多个Action
    intentFilter.addAction(Intent.ACTION_SCREEN_ON);
    intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
    registerReceiver(screenReceiver, intentFilter);
}

class ScreenReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 如何区分到底是哪个事件
        if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            Toast.makeText(context, "高斯雷开屏了", Toast.LENGTH_SHORT).show();
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            Toast.makeText(context, "高斯雷关屏了", Toast.LENGTH_SHORT).show();
            Log.d("tag", "高斯雷关屏了");
        }
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (screenReceiver != null) {
        unregisterReceiver(screenReceiver);
        screenReceiver = null;
    }
}

public class MainActivity extends Activity {

    private ScreenReceiver screenReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        screenReceiver = new ScreenReceiver();
        IntentFilter intentFilter = new IntentFilter();
        // IntentFilter可以添加多个Action
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(screenReceiver, intentFilter);
    }

    class ScreenReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // 如何区分到底是哪个事件
            if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
                Toast.makeText(context, "高斯雷开屏了", Toast.LENGTH_SHORT).show();
            } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                Toast.makeText(context, "高斯雷关屏了", Toast.LENGTH_SHORT).show();
                Log.d("tag", "高斯雷关屏了");
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (screenReceiver != null) {
            unregisterReceiver(screenReceiver);
            screenReceiver = null;
        }
    }
}
04. Android系统常见的广播事件
  1. IP拨号器(有序广播,最终广播)
  2. 监听手机的网络状态(无序广播)
  3. 监听开机启动(无序广播)
  4. 拦截短信(有序广播)
  5. sd状态的监听
  6. 监听应用的安装与卸载(无序广播)

//1. IP拨号器(有序广播,最终广播)
public class IPBroadCastReceiver extends BroadcastReceiver {
  // 当该广播接收者接收到广播的时候,被系统回调该方法
  @Override
  public void onReceive(Context context, Intent intent) {
    // 1. 获取用户拨打的号码
    String num = getResultData();
    //String num = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
    // 2. 获取用户保存的ip号码
    SharedPreferences sharedPreferences = context.getSharedPreferences("config", Context.MODE_PRIVATE);
    String ip = sharedPreferences.getString("ipnum", "");
    // 3. 修改号码为ip+号码
    // 4. 将修改好的号码设置出去
    setResultData(ip + num);
  }
}
//注册广播接收者
<receiver android:name="com.example.ipcaller.IPBroadCastReceiver">
    <!-- 添加一个意图过滤器,不然过滤不到Intent,因为广播的发送其实就是发送的Intent -->
    <intent-filter >
        <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
    </intent-filter>
</receiver>
//定义权限该权限系统是不会提示.
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

//2. 监听手机的网络状态(无序广播)
public class MainActivity extends Activity {
  private TextView tv_state;
  private NetStateReceiver netStateReceiver;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv_state = (TextView) findViewById(R.id.tv_state);

    /*
     * 1. 注册广播
     * 参数1:广播接收者对象
     * 参数2:意图过滤器
     */
    netStateReceiver = new NetStateReceiver();
    //封装一个IntentFilter
    IntentFilter filter = new IntentFilter();
    filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    registerReceiver(netStateReceiver, filter);
  }
  @Override
  protected void onDestroy() {
    super.onDestroy();
    /*
     * 2. 取消注册的广播(反注册)
     */
    if (netStateReceiver!=null) {
      unregisterReceiver(netStateReceiver);
      netStateReceiver = null;
    }
  }
  class NetStateReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
      //通过并更新当前的网络状态
      /*
       * 对于网络状态改变的广播,我们需要重新编写代码获取状态
       */
      getState(null);
      Toast.makeText(context, "监听到网络状态改变了额", Toast.LENGTH_SHORT).show();
    }
  }
  public void getState(View view){
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

    //获取当前可用的网络,如果当前没有可用的网络,返回null
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    if (activeNetworkInfo==null) {
      tv_state.setText("当前没有可用的网络");
      tv_state.setTextColor(Color.parseColor("#ff0000"));  
    }else {
      //获取当前网络的名称"MOBILE","WIFI"
      String typeName = activeNetworkInfo.getTypeName();
      //如果是"MOBILE"类型网络,我们可以进一步获取4G(LET)或者3G
      String subtypeName = activeNetworkInfo.getSubtypeName();

      tv_state.setTextColor(Color.BLUE);
      tv_state.setText("当前网络类型:"+typeName+"/"+subtypeName);
    }

  }
}
//权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

//3. 监听开机启动(无序广播)
public class BootStartReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    Log.d("tag", "监听到开机了");
    Toast.makeText(context, "监听到开机了", Toast.LENGTH_LONG).show();
    //启动MainActivity
    Intent intent2 = new Intent(context, MainActivity.class);
    /*
     * 如果第从一个非Activity中启动其他Activity,那么在使用Intent的时候需要让其创建一个新的任务栈
     */
    intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent2);
  }
}
//权限
 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
//注册
 <receiver android:name="com.example.listenbootStart.BootStartReceiver" >
  <intent-filter >
      <action android:name="android.intent.action.BOOT_COMPLETED"/>
  </intent-filter>
</receiver>

//4. 拦截短信(有序广播)
public class BlackNumReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    /*
     * 1. 获取sp中的黑名单
     */
    SharedPreferences sharedPreferences = context.getSharedPreferences("config", Context.MODE_PRIVATE);
    String blacknum = sharedPreferences.getString("blacknum", "");
    if (TextUtils.isEmpty(blacknum)) {
      Log.d("tag", "用户不想拦截任何短信");
      return;
    }
    /*
     * 2. 获取广播中的短信
     */
    Bundle bundle = intent.getExtras();
    Object[] objs = (Object[]) bundle.get("pdus");
    for(Object obj : objs){
      byte[] pdu = (byte[]) obj;
      SmsMessage smsMessage = SmsMessage.createFromPdu(pdu);
      /*
       * 3. 判断短信的发送者是否是黑名单用户发送过来的
       */
      String address = smsMessage.getOriginatingAddress();
      //获取短信正文
      String messageBody = smsMessage.getMessageBody();
      /*
       * 4. 如果是 就拦截,否则啥够不干
       */
      if (address.equals(blacknum)) {      
        Log.d("tag", "拦截了短信:"+address+"/"+messageBody);
        //拦截广播
        abortBroadcast();    
      }else {
        Log.d("tag", "放行了一个短信:"+address+"/"+messageBody);
      }
    }
  }
}
//权限
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
//注册
 <receiver android:name="com.example.abortSms.BlackNumReceiver">
     <intent-filter android:priority="1000" >
         <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
     </intent-filter>
 </receiver>

//5. sd状态的监听
public class SdcardStateReceiver extends BroadcastReceiver {
  //当sd状态发生改变的时候执行
  @Override
  public void onReceive(Context context, Intent intent) {
    //获取到当前广播的事件类型
    String action = intent.getAction();  
    if ("android.intent.action.MEDIA_MOUNTED".equals(action)) {
      System.out.println("说明sd卡挂载了 ....");
    }else if ("android.intent.action.MEDIA_UNMOUNTED".equals(action)) {
      System.out.println("说明sd卡卸载了 ");
    }
  }
}
 <receiver android:name="com.itheima.sdcardstate.SdcardStateReceiver">
     <intent-filter >
         <action android:name="android.intent.action.MEDIA_MOUNTED"/>
         <action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
 <!--小细节  这里需要配置一个data  约束类型叫file 因为sd里面存的数据类型是file  -->
          <data android:scheme="file"/>
     </intent-filter>
 </receiver>

//6. 监听应用的安装与卸载(无序广播)
public class AppStateReceiver extends BroadcastReceiver {
  //当有新的应用被安装  了  或者有应用被卸载 了  这个方法调用 
  @Override
  public void onReceive(Context context, Intent intent) {
    //获取当前广播事件类型 
    String action = intent.getAction();
    if ("android.intent.action.PACKAGE_INSTALL".equals(action)) {
    //该事件作为保留字,不起任何作用
    }else if ("android.intent.action.PACKAGE_ADDED".equals(action)) {
      System.out.println("应用安装了22222");
    }else if("android.intent.action.PACKAGE_REMOVED".equals(action)){
      System.out.println("应用卸载了"+intent.getData());
    } 
  }
}
 <receiver android:name="com.itheima.appstate.AppStateReceiver" >
     <intent-filter>
         <action android:name="android.intent.action.PACKAGE_INSTALL" />
         <action android:name="android.intent.action.PACKAGE_ADDED" />
         <action android:name="android.intent.action.PACKAGE_REMOVED" />
         <!-- 想让action事件生效 还需要 配置一个data -->
         <data android:scheme="package" />
     </intent-filter>
 </receiver>
5. 电话拦截的实现,拓展内容,涉及反射底层源代码
  • 需求 : 拦截黑名单号码发送的短信
  • 实现 :
    • 权限 android.permission.CALL_PHONE
    • 通过TelephonyManager监听电话的状态
    • 拦截电话需要调用远程服务,通过androidxref下载对应的系统源文件
    • 如果使用原生Android.jar内的API,必须使用Android Studio编译工程.
  • 代码 :
    • 注意 : 需要拷贝对应的远程服务的AIDL文件到src目录
    • 注意权限 : CALL_PHONE / READ_PHONE_STATE

// 获取TelephonyManager,能够监听来电的手机号码,付出的号码能够监听,但是获取不到手机号码,只能用广播的形式获取
//挂断电话的逻辑
TelephonyManager  tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
PhoneListener listener = new PhoneListener();
// 监听系统的电话状态
tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

//只能够监听来电的手机号码,打出去的号码监听不到.
class PhoneListener extends PhoneStateListener {
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);
        // 判断状态是否是响铃状态
        if (state == TelephonyManager.CALL_STATE_RINGING) {
            // 判断号码是否为黑名单号码
            BlackDBDAO dao = new BlackDBDAO(InterceptService.this);
            int type = dao.getType(incomingNumber);
            if (type == BlackBean.TYPE_TEL || type == BlackBean.TYPE_ALL) {
                // 获取远程服务
                try {
                    Class<?> clazz = Class.forName("android.os.ServiceManager");
                    Method method = clazz.getDeclaredMethod("getService", String.class);
                    IBinder binder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);
                    ITelephony telephony = ITelephony.Stub.asInterface(binder);
                    // 挂断电话
                    telephony.endCall();
                } catch (Exception e) {
                    e.printStackTrace();
                    LogUtils.e("错误 : " + e.toString());
                }
            }
        }
    }
// 释放资源
 tm.listen(listener, PhoneStateListener.LISTEN_NONE);
  • 监听呼出电话,需要权限 android.permission.PROCESS_OUTGOING_CALLS
    * 监听呼入的号码, 需要使用TelephonyManager,监听不到来电的号码
    * 监听呼出的号码, 需要使用系统广播

//监听号码的逻辑
public class LocationService extends Service {
    private TelephonyManager tm;
    private PhnStateLisner listener;
    private OutgoingReceiver receiver;

    @Override
    public void onCreate() {
        super.onCreate();
        // 获取TelephonyManager
        tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        listener = new PhnStateLisner();
        // 监听电话状态
        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
        // 注册监听呼出状态的广播
        receiver = new OutgoingReceiver();
        IntentFilter filter = new IntentFilter();
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
        registerReceiver(receiver, filter);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 取消监听
        tm.listen(listener, PhoneStateListener.LISTEN_NONE);
        unregisterReceiver(receiver);
    }

    // 自己实现的PhoneStateListener
    class PhnStateLisner extends PhoneStateListener {
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);
            // 如果当前电话状态是响铃状态,查询号码归属地,并显示
            if (state == TelephonyManager.CALL_STATE_RINGING) {
                String location = QueryLocationDAO.queryLocation(LocationService.this, incomingNumber);
                Toast.makeText(LocationService.this, location, Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 自己实现的监听电话呼出状态的广播
    class OutgoingReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String num = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            Toast.makeText(LocationService.this, num, Toast.LENGTH_SHORT).show();
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
6. 广播的其他特点
  1. 特殊的广播接收者只能够使用动态注册
  2. 在4.0后,第一次安装应用的时候必须得有界面,这样子广播接收者才生效
  3. 如果用户点击了设置 页面的强行停止按钮,那么广播接收也是不生效的.
  4. 跨进程传递数据
  5. 应用之间的相互唤醒
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值