Android点将台:传令官[

编程入门 行业动态 更新时间:2024-10-26 00:29:12

Android<a href=https://www.elefans.com/category/jswz/34/1725825.html style=点将台:传令官["/>

Android点将台:传令官[

零、前言
1.本文的知识点
1).BroadcastReceiver`静态`使用  
2).BroadcastReceiver`动态`使用   
3).BroadcastReceiver`有序`广播    
4).BroadcastReceiver和`系统`行为的结合  
5).小例子:使用BroadcastReceiver更新音乐播放器进度条
复制代码

2.BroadcastReceiver总览

现在才发现BroadcastReceiver原来这么精简,纯源码才260
直接继承Object,没有实现接口,没有家庭背景,可以说是个很简单的类

类名:BroadcastReceiver      父类:Object      修饰:public abstract
实现的接口:[]
包名:android.content   依赖类个数:9
内部类/接口个数:1
源码行数:653       源码行数(除注释):260
属性个数:2       方法个数:36       public方法个数:36
复制代码

一、BroadcastReceiver静态使用

静态使用也就是配置在AndroidManifest.xml中配置意图过滤器来匹配
关于intent的相关知识,见前一篇,这里不做解释


1.写一个类继承自BroadcastReceiver
/*** 作者:张风捷特烈<br></br>* 时间:2019/1/21/021:16:53<br></br>* 邮箱:1981462002@qq<br></br>* 说明:谈一个吐司的BroadcastReceiver*/
class ToastBroadcastReceiver : BroadcastReceiver() {/*** 接收时调用的方法*/override fun onReceive(context: Context, intent: Intent) {Toast.makeText(context, "Toly", Toast.LENGTH_SHORT).show()}
}---->[app/src/main/AndroidManifest.xml]------------------
<receiver android:name=".receiver.receiver.ToastBroadcastReceiver"><intent-filter><action android:name="www.toly1994.br.toast"/><category android:name="android.intent.category.DEFAULT"/></intent-filter>
</receiver>
复制代码

2.测试的Activity

---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {val intent = Intent("www.toly1994.br.toast")sendBroadcast(intent)
}
复制代码

3.静态广播在Android8.0+

intent必须指定广播的component,才有效

---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {val intent = Intent("www.toly1994.br.toast")intentponent = ComponentName("com.toly1994.tolyservice",//项目包名"com.toly1994.tolyservice.receiver.receiver.ToastBroadcastReceiver"//广播接收者全类名)sendBroadcast(intent)
}
复制代码

4.静态广播中的数据获取

广播接收者的onReceive回调中有intent: Intent,你应该明白怎么传数据了吧

---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {val intent = Intent("www.toly1994.br.toast")id_et_txt.text//为intent添加数据intent.putExtra("toast_data", id_et_txt.text.toString())intentponent = ComponentName("com.toly1994.tolyservice",//项目包名"com.toly1994.tolyservice.receiver.receiver.ToastBroadcastReceiver"//广播接收者全类名)sendBroadcast(intent)
}---->[ToastBroadcastReceiver]------------------
/*** 接收时调用的方法*/
override fun onReceive(context: Context, intent: Intent) {val data = intent.getStringExtra("toast_data")//data?:"NO MSG"表示如果data是空,就取"NO MSG"Toast.makeText(context, data?:"NO MSG", Toast.LENGTH_SHORT).show()
}
复制代码

5.BroadcastReceiver有什么用?

感觉从上面来看,BroadcastReceiver的onReceive确实耦合性非常低
外部只需要用intent和context.sendBroadcast就能触发它
但似乎BroadcastReceiver也没有太大的亮点,作用平平
为了说明他的亮点,现在我们新建一个app:Anotherapp


可以发现在另一个app里也能正常使用这个广播
这就有点意思了,我在A项目中写了一个类,它的方法可以在B项目中触发
这就是静态广播厉害的地方,也是我第一次接触的跨进程通信
(这说明解耦到一定的境界,就天下与我同,然而我将无处不在,手动滑稽)


二、BroadcastReceiver动态使用

BroadcastReceiver动态使用分为注册和注销,不需要在AndroidManifest.xml注册
只有在注册后和注销前的时间段才能使用,否则广播无效(即onReceive方法不会掉)

1.注册广播与发送消息
/*** 注册广播*/
private fun register() {val filter = IntentFilter()//创建意图过滤器filter.addAction("www.toly1994.br.toast2")//添加意图mReceiver = Toast2BroadcastReceiver()//创建 Toast2BroadcastReceiverregisterReceiver(mReceiver, filter)//注册
}/*** 发送广播*/
private fun sendMsg() {val intent = Intent()intent.action = "www.toly1994.br.toast2"intent.putExtra("toast_data", id_et_txt.text.toString())sendBroadcast(intent)
}
复制代码

2.注销广播

你说,哥就不注销怎么样?---答:异常呗
如果不注销,崩了一个异常,源码也好心提醒你要unregisterReceiver

2019-01-22 14:10:50.940 4892-4892/com.toly1994.tolyservice E/ActivityThread:
Activity com.toly1994.tolyservice.receiver.BrActivity has leaked IntentReceiver
com.toly1994.tolyservice.receiver.receiver.Toast2BroadcastReceiver@32500e2 that
was originally registered here. Are you missing a call to unregisterReceiver()?at android.app.LoadedApk$ReceiverDispatcher.<init>(LoadedApk.java:1333)at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:1114)at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1405)at android.app.ContextImpl.registerReceiver(ContextImpl.java:1378)at android.app.ContextImpl.registerReceiver(ContextImpl.java:1366)at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:603)at com.toly1994.tolyservice.receiver.BrActivity.onCreate(BrActivity.kt:27)
复制代码
 /*** 注销广播*/private fun unRegister() {unregisterReceiver(mReceiver);}override fun onDestroy() {super.onDestroy()unRegister()//注销广播
}
复制代码

3.静态和动态广播的区别

你可能会说:就一个200多行的类,还搞那么多事...

动态注册的广播
|---优势:可以自由的控制注册和取消,有很大的灵活性。
|---劣势:只有在注册之后才能起作用,在Activity的onDestroy后如果未被注销,会报异常
----所以动态注册的广播存活时间最长也就约等于Activity的生命周期长度静态注册的广播
|---优势:不受程序是否启动的约束,随时使用
|---劣势:优势同样也是劣势,无法取消,什么时候都能用
复制代码

三、BroadcastReceiver有序广播

先讲个场景:男孩(Boy)说:一块石头的价值 1元
之后将石头给了雕刻家,并将预期的价值1000元传递给雕刻家
之后雕刻家将石头给了宝石家,并将预期的价值10W元传递给宝石家
之后宝石家将石头给了收藏家,并将预期的价值100W元传递给收藏家
收藏家向外称城自己的宝石价值100W


1.有序广播(没有指定顺序时,按注册顺序)


1.1:四个广播接收者
/*** 作者:张风捷特烈<br></br>* 时间:2019/1/21/021:16:53<br></br>* 邮箱:1981462002@qq<br></br>* 说明:男孩*/
class BoyBReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {Toast.makeText(context, "男孩:$resultData",//[1]获取结果并展示Toast.LENGTH_LONG).show()
//      abortBroadcast();//[2]终止广播resultData = "价值1000元" //[3]传递数据---给下一个广播}
}/*** 说明:雕刻家*/
class GraverBReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {Toast.makeText(context, "雕刻家:$resultData", //[1]获取结果并展示Toast.LENGTH_LONG).show()
//      abortBroadcast();//[2]终止广播resultData = "价值10W元"//[3]传递数据---给下一个广播}
}/*** 说明:宝石家*/
class RubyManBReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {Toast.makeText(context, "宝石家:$resultData",Toast.LENGTH_LONG).show()
//      abortBroadcast();//[2]终止广播resultData = "价值100W元"//[3]传递数据---给下一个广播}
}/*** 说明:收藏家*/
class CollectorBReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {Toast.makeText(context,"收藏家:$resultData", //获取结果并展示Toast.LENGTH_LONG).show()}
}
复制代码

1.2.动态注册并发送有序广播
/*** 注册广播*/
private fun register() {val filter = IntentFilter()//创建意图过滤器filter.addAction("www.toly1994.br.toast2")//添加意图boyReceiver = BoyBReceiver()graverReceiver = GraverBReceiver()rubyManReceiver = RubyManBReceiver()registerReceiver(boyReceiver, filter)//注册registerReceiver(graverReceiver, filter)//注册registerReceiver(rubyManReceiver, filter)//注册
}/*** 发送有序广播*/
private fun sendOrder() {val intent = Intent()intent.action = "www.toly1994.br.toast2"val collectorBReceiver = CollectorBReceiver()sendOrderedBroadcast(intent, null,collectorBReceiver, null, 1,"价值1元", null)
}/*** 注销广播*/
private fun unRegister() {unregisterReceiver(boyReceiver)unregisterReceiver(graverReceiver)unregisterReceiver(rubyManReceiver)
}
复制代码

2.中途终止广播有序广播

 * 说明:雕刻家*/
class GraverBReceiver : BroadcastReceiver() {/*** 接收时调用的方法*/override fun onReceive(context: Context, intent: Intent) {Toast.makeText(context, "雕刻家:$resultData", //[1]获取结果并展示Toast.LENGTH_LONG).show()abortBroadcast();//[2]终止广播resultData = "价值10W元"//[3]传递数据---给下一个广播}
}
复制代码

3.自定义广播顺序

 /*** 注册广播*/private fun register() {boyReceiver = BoyBReceiver()val boyFilter = IntentFilter()//创建意图过滤器boyFilter.addAction("www.toly1994.br.toast2")//添加意图boyFilter.priority = 10//指定过滤器优先级graverReceiver = GraverBReceiver()val graverFilter = IntentFilter()//创建意图过滤器graverFilter.addAction("www.toly1994.br.toast2")//添加意图graverFilter.priority = 20//指定过滤器优先级rubyManReceiver = RubyManBReceiver()val rubyManFilter = IntentFilter()//创建意图过滤器rubyManFilter.addAction("www.toly1994.br.toast2")//添加意图rubyManFilter.priority = 21//指定过滤器优先级registerReceiver(boyReceiver, boyFilter)//注册registerReceiver(graverReceiver, graverFilter)//注册registerReceiver(rubyManReceiver, rubyManFilter)//注册}
复制代码

上面是BroadcastReceiver有序广播的动态注册形式的代码,
静态注册在AndroidManifest.xml里配置类似,就不废话了
还有一点注意的是sendOrderedBroadcast方法调用时传入的BroadcastReceiver
为最后调用的BroadcastReceiver,不需要注册!


四、广播和系统行为的结合

以下皆使用动态注册,很多系统级的行为静态注册都是无效的

1.开屏锁屏广播

/*** 作者:张风捷特烈<br></br>* 时间:2019/1/22/022:16:43<br></br>* 邮箱:1981462002@qq<br></br>* 说明:开屏锁屏广播*/
class ScreenBReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {//[1]获取到当前广播的事件类型val action = intent.action//[2]对当前广播事件类型做一个判断if ("android.intent.action.SCREEN_OFF" == action) {Log.e(TAG, "屏幕锁屏了")} else if ("android.intent.action.SCREEN_ON" == action) {Log.e(TAG, "屏幕解锁了")}}companion object {private const val TAG = "ScreenBReceiver"}
}---->[ScreenBrActivity使用方法]------------------------------------
/*** 动态的去注册屏幕解锁和锁屏的广播*/
private fun register() {// [1]动态的去注册屏幕解锁和锁屏的广播mScreenReceiver = ScreenBReceiver()// [2]创建intent-filter对象val filter = IntentFilter()// [3]添加要注册的actionfilter.addAction("android.intent.action.SCREEN_OFF")filter.addAction("android.intent.action.SCREEN_ON")// [4]注册广播接收者registerReceiver(mScreenReceiver, filter)
}复制代码

2.短信监听广播

注意权限:<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>

/*** 作者:张风捷特烈<br></br>* 时间:2019/1/22/022:16:43<br></br>* 邮箱:1981462002@qq<br></br>* 说明:短信监听广播*/
class SMSBReceiver : BroadcastReceiver() {//当短信到来的时候 就会执行这个方法override fun onReceive(context: Context, intent: Intent) {//[1]获取发短信送的号码  和内容val objects = intent.extras!!.get("pdus") as Array<*>val format = intent.getStringExtra("format")for (pdu in objects) {//[2]获取smsmessage实例val smsMessage =if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {SmsMessage.createFromPdu(pdu as ByteArray, format)} else {SmsMessage.createFromPdu(pdu as ByteArray)}//[3]获取发送短信的内容val body = smsMessage.messageBodyval date = Date(smsMessage.timestampMillis)//时间val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA)//[4]获取发送者val address = smsMessage.originatingAddressval receiveTime = format.format(date)Log.e("SMSBReceiver", "body:$body---$address---$receiveTime")}}
}---->[SMSBrActivity使用方法]------------------------------------
/*** 动态注册短信广播接收者*/
private fun register() {//注册短信广播接收者val smsFilter = IntentFilter()smsFilter.addAction("android.provider.Telephony.SMS_RECEIVED")mSmsReceiver = SMSBReceiver()registerReceiver(mSmsReceiver, smsFilter)
}
复制代码

3.监听电量变化广播

这里传入一个Textview用于显示电量

/*** 作者:张风捷特烈<br></br>* 时间:2019/1/22/022:16:43<br></br>* 邮箱:1981462002@qq<br></br>* 说明:监听电量变化*/
class BatteryBReceiver(private val mTV:TextView ) : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {// 当前电量val currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)// 总电量val total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1)val percent = currLevel * 100 / totalLog.e("BatteryBReceiver", "battery: $percent%")mTV.setTextColor(randomRGB())mTV.text = "battery: $percent%"}/*** 返回随机颜色** @return 随机颜色*/fun randomRGB(): Int {val random = Random()val r = 30 + random.nextInt(200)val g = 30 + random.nextInt(200)val b = 30 + random.nextInt(200)return Color.rgb(r, g, b)}
}---->[BatteryBrActivity使用方法]------------------------------------
/*** 动态电量广播接收者*/
private fun register() {val filter = IntentFilter()filter.addAction(Intent.ACTION_BATTERY_CHANGED)mBatteryChangeReceiver = BatteryBReceiver(id_tv_info)registerReceiver(mBatteryChangeReceiver, filter)
}
复制代码

4.app安装/卸载改变时广播监听

/*** 作者:张风捷特烈<br></br>* 时间:2019/1/22/022:16:43<br></br>* 邮箱:1981462002@qq<br></br>* 说明:app安装/卸载改变时广播监听*/
class AppBReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {val action = intent.actionval uri = intent.dataif (action == "android.intent.action.PACKAGE_ADDED") {Log.e("AppBReceiver", uri.toString() + "被安装了")} else if (action == "android.intent.action.PACKAGE_REPLACED") {Log.e("AppBReceiver", uri.toString() + "被更新了")} else if (action == "android.intent.action.PACKAGE_REMOVED") {Log.e("AppBReceiver", uri.toString() + "被卸载了")}}
}---->[AppBrActivity使用方法]------------------------------------/*** 动态注册app安装/卸载改变时广播监听*/private fun register() {val filter = IntentFilter()filter.addAction("android.intent.action.PACKAGE_ADDED")filter.addAction("android.intent.action.PACKAGE_REPLACED")filter.addAction("android.intent.action.PACKAGE_REMOVED")filter.addDataScheme("package")mAppReceiver = AppBReceiver()registerReceiver(mAppReceiver, filter)}//但是貌似这个用动态注册并不怎么有用//因为一般卸载,安装都不是在当前Activity中,加了一下静态,便可以了//注意,在测试中发现,只加静态的配置也是无效的<receiver android:name=".receiver.receiver.AppBReceiver"><intent-filter ><action android:name="android.intent.action.PACKAGE_ADDED"/><action android:name="android.intent.action.PACKAGE_REPLACED"/><action android:name="android.intent.action.PACKAGE_REMOVED"/><data android:scheme="package"/></intent-filter>
</receiver>
复制代码

还有一些系统行为套路都差不多,需要的时候查查对应的action就行了


五、使用广播更新音乐进度条

在绝命暗杀官[-Service-]中实现过一个音乐播放条,其中音乐的播放进度是靠Handler+回调实现的
BroadcastReceiver本职就在于通知,在这里用BroadcastReceiver实现音乐的播放进度

---->[常量类]-----------------------------
public class Cons {//广播更新进度--数据public static final String DATA_MUSIC_POSITION = "data_music_position";//广播更新进度--Actionpublic static final String ACTION_UPDATE = "action_update";
}---->[MusicPlayerWithBrStub]-----------------------------
mTimer.schedule(new TimerTask() {@Overridepublic void run() {if (mPlayer.isPlaying()) {int pos = mPlayer.getCurrentPosition();int duration = mPlayer.getDuration();//发送广播更新进度--->    Intent intent = new Intent(Cons.ACTION_UPDATE);--->    int progress = (int) (pos * 100.f / duration);--->    intent.putExtra(Cons.DATA_MUSIC_POSITION, progress);--->    mContext.sendBroadcast(intent);}}
}, 0, 1000);---->[MusicActivity#registerReceiver]-----------------------------
|-- 这里我新建一个类,你也可以直接在Activity中建个内部类,要简单些
public class UpdateReceiver extends BroadcastReceiver {@Nullableprivate ProgressView progressView;public UpdateReceiver(@Nullable ProgressView progressView) {this.progressView = progressView;}@Overridepublic void onReceive(Context context, Intent intent) {if (Cons.ACTION_UPDATE.equals(intent.getAction())) {int progress = intent.getIntExtra(Cons.DATA_MUSIC_POSITION, 0);if (progressView != null) {--->    progressView.setProgress(progress);}}}
}/*** 注册广播*/
---->[MusicActivity#registerReceiver]-----------------------------
private fun registerReceiver() {mReceiver = UpdateReceiver(id_pv_pre)val filter = IntentFilter()filter.addAction(Cons.ACTION_UPDATE)registerReceiver(mReceiver, filter)//注册
}---->[MusicActivity#onDestroy]-----------------------------
override fun onDestroy() {super.onDestroy()unregisterReceiver(mReceiver)//注销广播mMusicPlayer.release()
}
复制代码

其实也就是发广播-->收广播-->操作,用起来并不困难
至于BroadcastReceiver的源码,暂时就不读了(读了一下,没怎么读得通...),以后再开篇吧!


后记:捷文规范
1.本文成长记录及勘误表
项目源码日期附录
V0.1--无2018-2-27

发布名:Android点将台:传令官[-BroadcastReceiver-]
捷文链接:juejin.im/post/5c7675…

2.更多关于我
笔名QQ微信
张风捷特烈1981462002zdl1994328

我的github:github/toly1994328
我的简书:www.jianshu/u/e4e52c116…
我的简书:www.jianshu/u/e4e52c116…
个人网站:www.toly1994

3.声明

1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持

更多推荐

Android点将台:传令官[

本文发布于:2024-02-06 10:25:20,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1748592.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:点将台   Android

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!