BroadCastReceiver组件
- 广播的发送与接收
- 自定义权限广播
- 特殊的广播与接收者
- Android系统常见的广播事件
- 电话拦截的实现,拓展内容,涉及反射底层源代码
- 广播的其他特点
01-广播的发送与接收
无序广播特点:无序广播不可以被拦截,数据不能够修改,sendBroadCast(Intent)
普通广播是完全异步(就是不会被某个广播接收者终止)的,可以在同一时刻(逻辑上)被所有接收者接收到(其实被接收者接收到也是由顺序的,接收者配置的优先级越高,越先接收到,也就是说广播接收者的优先级对于无序广播也是有用的),消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传播。
有序广播特点:有序广播可以被拦截
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. 特殊的广播与接收者
- 特殊的广播与接收者,频繁的操作:锁屏与解屏,电量的变化等
- 用动态注册方式注册,注意需要反注册,清单文件里注册无效
//锁屏与解屏,注意:在这个地方,只要界面一退出,该广播就失效可以采用在服务中注册广播的方式,
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系统常见的广播事件
- IP拨号器(有序广播,最终广播)
- 监听手机的网络状态(无序广播)
- 监听开机启动(无序广播)
- 拦截短信(有序广播)
- sd状态的监听
- 监听应用的安装与卸载(无序广播)
//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. 广播的其他特点
- 特殊的广播接收者只能够使用动态注册
- 在4.0后,第一次安装应用的时候必须得有界面,这样子广播接收者才生效
- 如果用户点击了设置 页面的强行停止按钮,那么广播接收也是不生效的.
- 跨进程传递数据
- 应用之间的相互唤醒