BroadcastReceiver
使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。
广播分为普通广播(无序广播)和有序广播。而广播的注册方式又有两种,分静态注册和动态注册,xml中注册的(静态广播)优先级高于动态注册广播
- 普通广播:异步的,逻辑上讲是可以在同一时刻被所有接收者接收到,消息传递效率比较高。
但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传递。
通过Context.sendBroadcast()发送普通广播。
- 有序广播:按照接收者声明的优先级别(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置)按顺序接受。
前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast())。
前面的接收者可以将处理结果通过setResultExtras(Bundle)方法存放进结果对象,然后传给下一个接收者,通过代码:Bundle bundle =getResultExtras(true))可以获取上一个接收者存入在结果对象中的数据。
系统收到短信,发出的广播属于有序广播。如果想阻止用户收到短信,可以通过设置优先级,让你们自定义的接收者先获取到广播,然后终止广播,这样用户就接收不到短信了。
通过Context.sendOrderedBroadcast()发送有序广播。
- 粘性广播:
静态广播注册步骤:
- 自定义一个类继承BroadcastReceiver
- 重写onReceive方法
- 在manifest.xml中注册
示例:
1 2 3 4 5 6 7 8 9 10 11
| public class MyBroadcastReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) { Log.i("fuck","intent-action : " + intent.getAction()); if(intent.getAction().equals("test")){ Toast.makeText(context,"fuck",Toast.LENGTH_LONG).show(); } }
}
|
注册:
1 2 3 4 5 6 7 8 9
| <receiver android:name=".broadcast.MyBroadcastReceiver">
<intent-filter> <action android:name="android.intent.action.ACTION_POWER_CONNECTED" /> <action android:name="test"/> </intent-filter>
</receiver>
|
注:BroadcastReceiver生命周期很短,如果需要在onReceiver完成一些耗时操作,应该考虑在Service中开启一个新线程处理耗时操作,不应该在BroadcastReceiver中开启一个新的线程,因为BroadcastReceiver生命周期很短,在执行完onReceiver以后就结束,如果开启一个新的线程,可能出现BroadcastRecevier退出以后线程还在,而如果BroadcastReceiver所在的进程结束了,该线程就会被标记为一个空线程,根据Android的内存管理策略,在系统内存紧张的时候,会按照优先级,结束优先级低的线程,而空线程无异是优先级最低的,这样就可能导致BroadcastReceiver启动的子线程不能执行完成。
在 Android 中,程序的响应( Responsive )被活动管理器( Activity Manager )和窗口管理器( Window Manager )这两个系统服务所监视。当 BroadcastReceiver 在 10 秒内没有执行完毕,Android 会认为该程序无响应。所以在 BroadcastReceiver 里不能做一些比较耗时的操作,否侧会弹出ANR ( Application No Response )的对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent 给 Service ,由 Service 来完成。而不是使用子线程的方法来解决,因为 BroadcastReceiver 的生命周期很短(在 onReceive() 执行后 BroadcastReceiver 的实例就会被销毁),子线程可能还没有结束BroadcastReceiver 就先结束了。如果 BroadcastReceiver 结束了,它的宿主进程还在运行,那么子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。
动态广播注册步骤:
registerReceiver(new MyBroadcastReceiver(),new IntentFilter("test"));
注册动态广播。
new Intent("test").sendBroadcast(intent);
发送动态广播。
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
注意权限
静态注册与动态注册的区别
示例(Kotlin)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
class BroadcastActivity :BaseActivity(){
lateinit var timeChangeReceiver: TimeChangeReceiver
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_broadcast)
initView() }
private fun initView() { btnSendBR.setOnClickListener{ val intent = Intent("com.example.myapplication.broadcast.MY_BROADCAST") intent.setPackage(packageName)
sendOrderedBroadcast(intent,null) }
val intentFilter = IntentFilter() intentFilter.addAction("android.intent.action.TIME_TICK") timeChangeReceiver = TimeChangeReceiver() registerReceiver(timeChangeReceiver,intentFilter)
btnForce.setOnClickListener{ val intent = Intent("com.example.broadcast.FORCE_OFFLINE") sendBroadcast(intent) } }
inner class TimeChangeReceiver:BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) { Toast.makeText(context,"Time is changed",Toast.LENGTH_SHORT).show() } }
companion object{ fun actionStart(context: Context){ val intent = Intent(context, BroadcastActivity::class.java) context.startActivity(intent) } }
override fun onDestroy() { super.onDestroy() unregisterReceiver(timeChangeReceiver) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
class BootCompleteReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context,"Boot Complete",Toast.LENGTH_LONG).show() Log.d("TAG","Boot Complete") } }
|
1 2 3 4 5 6 7
| class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context,"receiver in MyBroadcastReceiver",Toast.LENGTH_SHORT).show() abortBroadcast() } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| open class BaseActivity : AppCompatActivity() {
lateinit var receiver:ForceOfflineReceiver
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)
supportActionBar?.hide()
Log.d("TAG_BaseActivity",javaClass.simpleName) ActivityCollector.addActivity(this) }
inner class ForceOfflineReceiver:BroadcastReceiver(){ override fun onReceive(context: Context, intent: Intent?) { AlertDialog.Builder(context).apply { setTitle("Warning") setMessage("You are forced to offline, Please try to login again.") setCancelable(false) setPositiveButton("OK"){_,_ -> ActivityCollector.finishAll() val i = Intent(context,MainActivity::class.java) context.startActivity(i) } show() } } }
override fun onResume() { super.onResume() val intentFilter = IntentFilter() intentFilter.addAction("com.example.broadcast.FORCE_OFFLINE") receiver = ForceOfflineReceiver() registerReceiver(receiver,intentFilter) }
override fun onPause() { super.onPause() unregisterReceiver(receiver) }
override fun onDestroy() { super.onDestroy() ActivityCollector.removeActivity(this) } }
|
AndroidManifest.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <receiver android:name=".broadcast.MyReceiver" android:enabled="true" android:exported="true">
<intent-filter android:priority="100"> <action android:name="com.example.myapplication.broadcast.MY_BROADCAST" /> </intent-filter> </receiver>
<receiver android:name=".broadcast.BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
|