【0047】Android基础-34-广播接收者BroadCastReceiver相关
【注意】
【1】在sd卡的广播和安装包apk的事件广播时,除了配置action时,还需要配置data数据;详见4.2和7.1;
【2】动态注册广播和静态注册广播的区别和好处;
【3】有序广播和无序广播:无序广播不可以被终止 数据不可以被修改
【4】红色日志的信息并非是错误;
【1】为什么需要广播接收者?
① Android系统内部相当于已经有一个电台,定义了好多的广播事件
比如外拨电话 短信到来 sd卡状态 电池电量变化....
②谷歌工程师给我们定义了一个组件专门用来接收这些事件的
③谷歌工程师为什么要设计这样一个组件 目的就是为了方便开发者进行开发
【2】IP电话拨号器实例

【2.1】新建类起名称的时候以Receiver结尾,表明是一个广播接收者;

【2.2】同样定义一个广播接收者需要在清单文件中配置:配置一个action;
当此动作发生的时候,就会调用override的receiver方法;


可以配置多个action,这几个action中的一个触发,则都可以使得被复写OnReceive方法被调用;

【2.3】需要增加打电话的权限


【2.4】当有外拨电话的时候该onReceiver方法被执行;

【2.5】取到拨出去的电话号:getResultData()方法会拿到之前在配置清单中配置的action发生的receiver的数据;



【2.6】增加要拨打的号:增加了判断,如果是以“0”开头的就加拨17951,否则不加拨;



【2.7】将加拨的号码可以通过用户输入完成
【2.7.1】页面布局


【2.7.2】业务逻辑,将用户输入的号码保存到sharedPreference中;

【注意】实际在MainActivity中获取到的sharedPreference实例是通过this(此对象拿到的),即MainActivity的上下文,因为MainActivity继承于context;

但是在OutGoingCallReceiver类中无法拿到sharedPreference实例;但是在onReceiver方法中的参数已经存在context上下文;
通过上下文就可以拿到sharedPreference实例;


【2.7.3】效果演示



【2.8】完整源码
【activity_main.xml】源码
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 tools:context=".MainActivity" > 7 8 <EditText 9 android:id="@+id/et_number" 10 android:layout_width="match_parent" 11 android:layout_height="wrap_content" 12 android:hint="请输入要保存的ip号码" /> 13 14 <Button 15 android:onClick="click" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:text="保存" /> 19 20 </LinearLayout>
【MainActivity.java】源码
1 package com.itheima.ipdail; 2 3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.content.SharedPreferences; 6 import android.view.Menu; 7 import android.view.View; 8 import android.widget.EditText; 9 import android.widget.Toast; 10 11 public class MainActivity extends Activity { 12 13 private EditText et_number; 14 15 @Override 16 protected void onCreate(Bundle savedInstanceState) { 17 super.onCreate(savedInstanceState); 18 setContentView(R.layout.activity_main); 19 et_number = (EditText) findViewById(R.id.et_number); 20 } 21 22 // 点击按钮 获取用户输入的iP号码 进行保存 23 public void click(View v) { 24 // [1]获取到用户输入的number 25 String number = et_number.getText().toString().trim(); 26 // [2]使用sp 保存起来 27 SharedPreferences sp = getSharedPreferences("config", 0); 28 // [3]存数据 29 sp.edit().putString("ipnumber", number).commit(); 30 Toast.makeText(getApplicationContext(), "保存成功", 0).show(); 31 32 } 33 34 }
【OutGoingCallReceiver】源码
1 package com.itheima.ipdail; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.content.SharedPreferences; 7 8 /** 9 * 定义一个广播接收者 就要在清单文件里面配置一下 10 * 11 * @author jhon 12 * 13 */ 14 public class OutGoingCallReceiver extends BroadcastReceiver { 15 16 // 当接收到外拨电话的事件的时候回执行这个方法 17 @Override 18 public void onReceive(Context context, Intent intent) { 19 20 // [0]获取到我们保存的ip号码 21 SharedPreferences sp = context.getSharedPreferences("config", 0); 22 // [0.1]获取我们保存的ip号码 23 String ipnumber = sp.getString("ipnumber", ""); 24 25 // [1]获取当前我们要拨打的电话号码 26 String currentNumber = getResultData(); 27 28 // [1.1]判断拨打的电话是否是长途 29 if (currentNumber.startsWith("0")) { 30 // [2]在当前的号码前面加上一个17951 31 setResultData(ipnumber + currentNumber); 32 } 33 34 } 35 36 }
【AndroidManifest.xml】源码
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.itheima.ipdail" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="8" 9 android:targetSdkVersion="17" /> 10 <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/> 11 12 <application 13 android:allowBackup="true" 14 android:icon="@drawable/ic_launcher" 15 android:label="@string/app_name" 16 android:theme="@style/AppTheme" > 17 <activity 18 android:name="com.itheima.ipdail.MainActivity" 19 android:label="@string/app_name" > 20 <intent-filter> 21 <action android:name="android.intent.action.MAIN" /> 22 23 <category android:name="android.intent.category.LAUNCHER" /> 24 </intent-filter> 25 </activity> 26 <!--配置广播接收者 --> 27 28 <receiver android:name="com.itheima.ipdail.OutGoingCallReceiver"> 29 <intent-filter > 30 <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> 31 </intent-filter> 32 </receiver> 33 34 </application> 35 36 </manifest>
【3】总结BroadCastReceiver的使用方法
【3.1】(1)定义一个类继承BroadCastReceiver

【3.2】(2)在清单文件里面配置一下

【3.3】当我们配置的action 的事件发生了 onReceive方法就会执行
【4】sd卡状态的监听
【4.1】案例效果

【4.2】【清单文件的配置】必须配置data中的sheme项:在常用的操作中配置data选项的地方只有两个,此处算一处;

【4.3】源码
【SdcardReceiver.java】源码
1 package com.itheima.sdcardstate; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 7 public class SdcardReceiver extends BroadcastReceiver { 8 9 // 当sd卡被卸载 或者被挂载上来的时候 会执行 10 @Override 11 public void onReceive(Context context, Intent intent) { 12 13 // [1]获取到当前广播的事件类型 14 String action = intent.getAction(); 15 // [2]对action做一个判断 16 if ("android.intent.action.MEDIA_UNMOUNTED".equals(action)) { 17 System.out.println("说明sd卡 卸载了"); 18 19 } else if ("android.intent.action.MEDIA_MOUNTED".equals(action)) { 20 21 System.out.println("说明sd卡挂载了"); 22 } 23 24 } 25 26 }
【配置清单】源码
1 <receiver android:name="com.itheima.sdcardstate.SdcardReceiver"> 2 <intent-filter > 3 <action android:name="android.intent.action.MEDIA_UNMOUNTED"/> 4 <action android:name="android.intent.action.MEDIA_MOUNTED"/> 5 <action android:name="android.intent.action."/> 6 <!--想让上面的这2个事件生效 必须的加上这样的一个data --> 7 <data android:scheme="file"/> 8 </intent-filter> 9 </receiver>
【5】广播接收者的工作原理
【说明】实际的监听程序在后台仍然可以进行监听事件的发生;(sd卡监听程序在后台运行,当sd卡发生变化时,仍可以判断状态的变化)
其实:杀死本监听程序之后,仍然可以接收到事件发生时传来的状态;
这样做的好处利于开发者的开发,当然也有不好的地方,下面的短信监听器就是案例;


【6】短监听案例
【6.1】配置权限-此处的权限配置在列表中没有,但是可以复制粘贴使用;

1 <!--配置拦截短信的action--> 2 <receiver android:name=".SMSListenerReceiver"> 3 <intent-filter> 4 <action android:name="android.provider.Telephony.SMS_RECEIVED"/> 5 </intent-filter> 6 </receiver>
【6.2】配置权限


【6.3】在接收到短信的OnReceive方法确实被调用了;


【6.4】获取短信的发送联系人和短信的数据
【pdu】一条短信信息的封装结果就是一个pdu;




1 package com.example.administrator.a10smslistener; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.telephony.SmsManager; 7 import android.telephony.SmsMessage; 8 9 /** 10 * Created by Administrator on 2017-10-29. 11 */ 12 13 public class SMSListenerReceiver extends BroadcastReceiver { 14 @Override 15 public void onReceive(Context context, Intent intent) { 16 System.out.println("haha"); 17 18 //1.获取短信的发送的号码和内容 19 Object[] objects = (Object[]) intent.getExtras().get("pdus"); 20 for (Object pdu : objects) { 21 //2.获取smsMessage实例 22 SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu); 23 //3.获取短信的内容 24 String messageBody = message.getMessageBody(); 25 //4.获取发送者 26 String originatingAddress = message.getOriginatingAddress(); 27 System.out.println("body" + messageBody + "========" + originatingAddress); 28 } 29 30 } 31 }

【6.5】删除此配置选项则不会出现app登陆的界面;
在Android4.0之后,如果在第一次程序安装的时候没有此登陆界面,则该应用无法接受到广播;

【6.6】在Android4.0之后,点击了forceStop则应用的广播事件则无法生效

【7】广播接收者案例_卸载安装
这样做的意义:可以收集安装包的包名,然后可以收集大数据;
【7.1】配置data【清单文件的配置】必须配置data中的sheme项:在常用的操作中配置data选项的地方只有两个,此处算第2处处;

【7.2】程序安装和卸载的判断


【7.3】getData

【8】手机重启案例
【应用场景】在某些特殊的行业开发的APP,在设备上开机之后,只是用当前公司开发的APP;
方法:开机之后监测
【8.1】清单配置

【8.2】权限配置

【8.3】bug:从BroadCastReceiver中跳转到到Activity中需要配置flags;
【出现问题的现象】

【原因】需要intent的flag;一般在进行跳转的时候都是在一个Activity中跳转到另外一个Activity;
此处是在BroadCastReceiver中跳转到到Activity中;需要配置flags;


【说明】在android7.1系统中该选项改为了警告:

不添加也是可以的,但是最好还是添加上;
【解决办法】

【8.4】通过隐式意图可以做一些开机之后就进入的事情:比如屏蔽掉导航按键、通知栏的功能的等等;

【9】自定义广播:有序广播和无序广播
【9.1】无序广播:只要发送广播就行,不用管是否有人接收;
【9.1.1】 无序广播:类似新闻联播
【源码】6_发送一条无序广播/src/com/itheima/sendbroadcast/MainActivity.java
1 package com.itheima.sendbroadcast; 2 3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.content.Intent; 6 import android.view.Menu; 7 import android.view.View; 8 9 public class MainActivity extends Activity { 10 11 @Override 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.activity_main); 15 } 16 17 //点击按钮发送一条无序广播 18 public void click(View v) { 19 20 Intent intent = new Intent(); 21 //设置action 22 intent.setAction("com.itheima.custombroadcast"); 23 24 intent.putExtra("name", "新闻联播每天晚上7点准时开整!!!"); 25 //发送一条广播 发送无序广播 26 sendBroadcast(intent); 27 28 } 29 }

【注】在发送无序广播的时候需要设置action;否则在接收广播的程序中无法配置清单文件中的action选项;
【9.1.2】无序广播不可以被终止 数据不可以被修改



【红色日志的信息】报告说:在无序广播中无需终止;并非是错误;
【9.2】接受无序广播
【/7_接收自定义广播/src/com/itheima/receivebroadcast/ReceiveCustomReceiver.java】源码
1 package com.itheima.receivebroadcast; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.widget.Toast; 7 8 public class ReceiveCustomReceiver extends BroadcastReceiver { 9 10 //当我发送自定义广播的时候 这个方法就会接收到 11 @Override 12 public void onReceive(Context context, Intent intent) { 13 14 //[0]终止广播 15 // abortBroadcast(); 16 17 //[1]取出我们发送广播携带的数据 18 String content = intent.getStringExtra("name"); 19 //[2]把获取到的数据展示到toast上 20 Toast.makeText(context, content, 0).show(); 21 22 } 23 24 }
【7_接收自定义广播/AndroidManifest.xml】源码
1 <receiver android:name="com.itheima.receivebroadcast.ReceiveCustomReceiver"> 2 <intent-filter > 3 <action android:name="com.itheima.custombroadcast"/> 4 </intent-filter> 5 </receiver>

【9.3】有序广播:类似中央发送的红头文件 按照优先级一级一级的接收 有序广播可以被终止 数据可以被修改
【9.3.1】发有序广播


【配置优先级】可以层层递进,每层的优先级不同:1000到-1000;
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.itheima.receiverice" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="8" 9 android:targetSdkVersion="17" /> 10 11 <application 12 android:allowBackup="true" 13 android:icon="@drawable/ic_launcher" 14 android:label="@string/app_name" 15 android:theme="@style/AppTheme" > 16 <activity 17 android:name="com.itheima.receiverice.MainActivity" 18 android:label="@string/app_name" > 19 <intent-filter> 20 <action android:name="android.intent.action.MAIN" /> 21 22 <category android:name="android.intent.category.LAUNCHER" /> 23 </intent-filter> 24 </activity> 25 26 <!--配置广播接收者 --> 27 <receiver android:name="com.itheima.receiverice.ProvienceReceiver"> 28 <intent-filter 29 android:priority="1000" 30 > 31 <action android:name="com.itheima.rice"/> 32 </intent-filter> 33 </receiver> 34 35 <receiver android:name="com.itheima.receiverice.CityReceiver"> 36 <intent-filter 37 android:priority="800" 38 > 39 <action android:name="com.itheima.rice"/> 40 </intent-filter> 41 </receiver> 42 43 <receiver android:name="com.itheima.receiverice.CountryReceiver"> 44 <intent-filter 45 android:priority="100" 46 > 47 <action android:name="com.itheima.rice"/> 48 </intent-filter> 49 </receiver> 50 <receiver android:name="com.itheima.receiverice.NongMinReceiver"> 51 <intent-filter 52 android:priority="10" 53 > 54 <action android:name="com.itheima.rice"/> 55 </intent-filter> 56 </receiver> 57 58 </application> 59 60 </manifest>
【/9_接收大米/src/com/itheima/receiverice/ProvienceReceiver.java】源码
1 package com.itheima.receiverice; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.widget.Toast; 7 8 public class ProvienceReceiver extends BroadcastReceiver { 9 10 @Override 11 public void onReceive(Context context, Intent intent) { 12 13 //[1]获取到发送广播携带的数据 14 String content = getResultData(); 15 16 //[2]展示到Toast上 17 Toast.makeText(context, "省:"+content, 1).show(); 18 19 //[2.1]终止广播 20 // abortBroadcast(); 21 22 //[3]修改数据 (扣留大米) 23 setResultData("给每个村民发了500斤大米"); 24 25 26 } 27 28 }
【/9_接收大米/src/com/itheima/receiverice/CityReceiver.java】源码
1 package com.itheima.receiverice; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.widget.Toast; 7 8 public class CityReceiver extends BroadcastReceiver { 9 10 @Override 11 public void onReceive(Context context, Intent intent) { 12 13 //[1]获取到发送广播携带的数据 14 String content = getResultData(); 15 16 //[2]展示到Toast上 17 Toast.makeText(context, "市:"+content, 1).show(); 18 19 //[3]修改数据 (扣留大米) 20 setResultData("给每个村民发了200斤大米"); 21 22 } 23 24 }
【/9_接收大米/src/com/itheima/receiverice/NongMinReceiver.java】源码
1 package com.itheima.receiverice; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.widget.Toast; 7 8 public class NongMinReceiver extends BroadcastReceiver { 9 10 @Override 11 public void onReceive(Context context, Intent intent) { 12 13 //[1]获取到发送广播携带的数据 14 String content = getResultData(); 15 16 //[2]展示到Toast上 17 Toast.makeText(context, "农民:"+content, 1).show(); 18 } 19 20 }
【9.3.2】终止有序广播-广播终止与某一级

【9.3.3】定义广播的最终接受者:



【9.4】判断有序广播和无序广播的方法:
添加终止语句,如果接收不到信息则为有序广播,否则为无序广播;
【10】 特殊广播接收者: 比如操作特别频繁的广播事件 屏幕的锁屏和解锁 电池电量的变化 这样的广播接收者在清单文件里面注册无效
【10.1】【实例】特殊的事件接受者:在清单文件中注册无效


【10.2】动态注册

【/10_特殊的广播接收者/src/com/itheima/screen/MainActivity.java】源码
1 package com.itheima.screen; 2 3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.content.IntentFilter; 6 import android.view.Menu; 7 8 public class MainActivity extends Activity { 9 10 private ScreenReceiver screenReceiver; 11 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_main); 16 17 /* 18 * <receiver android:name="com.itheima.screen.ScreenReceiver"> 19 * <intent-filter > <action 20 * android:name="android.intent.action.SCREEN_OFF"/> <action 21 * android:name="android.intent.action.SCREEN_ON"/> </intent-filter> 22 * </receiver> 23 */ 24 25 // [1]动态的去注册屏幕解锁和锁屏的广播 26 screenReceiver = new ScreenReceiver(); 27 // [2]创建intent-filter对象 28 IntentFilter filter = new IntentFilter(); 29 // [3]添加要注册的action 30 filter.addAction("android.intent.action.SCREEN_OFF"); 31 filter.addAction("android.intent.action.SCREEN_ON"); 32 // [4]注册广播接收者 33 this.registerReceiver(screenReceiver, filter); 34 35 } 36 37 @Override 38 protected void onDestroy() { 39 // 当activity销毁的时候 取消注册广播接收者 40 unregisterReceiver(screenReceiver); 41 42 super.onDestroy(); 43 } 44 45 }
【/10_特殊的广播接收者/src/com/itheima/screen/ScreenReceiver.java】源码
1 package com.itheima.screen; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 7 public class ScreenReceiver extends BroadcastReceiver { 8 9 @Override 10 public void onReceive(Context context, Intent intent) { 11 12 // [1]获取到当前广播的事件类型 13 String action = intent.getAction(); 14 // [2]对当前广播事件类型做一个判断 15 16 if ("android.intent.action.SCREEN_OFF".equals(action)) { 17 18 System.out.println("屏幕锁屏了"); 19 } else if ("android.intent.action.SCREEN_ON".equals(action)) { 20 21 System.out.println("说明屏幕解锁了~~~"); 22 } 23 24 } 25 26 }
【10.3】这种程序的特点:在程序退出之后,广播发生时,广播接受者将无法接受到广播;
【原因】这种做的好处是节省设备的内存;
静态注册的程序在广播在发生的时候,会直接唤醒应用程序,占用内存;
如果是动态注册的程序仍然唤醒应用程序,则一天会频繁的占用内存,对内存的占用空间较大;
如果同时100个应用同时是动态注册,则系统可能会出现崩溃;
浙公网安备 33010602011771号