我的全部文章整理在此篇,將陸續更新收錄:知無涯,行者之路莫言終(個人編程之路)java
1).BroadcastReceiver`靜態`使用
2).BroadcastReceiver`動態`使用
3).BroadcastReceiver`有序`廣播
4).BroadcastReceiver和`系統`行爲的結合
5).小例子:使用BroadcastReceiver更新音樂播放器進度條
複製代碼
如今才發現BroadcastReceiver原來這麼精簡,純源碼才260
直接繼承Object,沒有實現接口,沒有家庭背景,能夠說是個很簡單的類android
類名:BroadcastReceiver 父類:Object 修飾:public abstract
實現的接口:[]
包名:android.content 依賴類個數:9
內部類/接口個數:1
源碼行數:653 源碼行數(除註釋):260
屬性個數:2 方法個數:36 public方法個數:36
複製代碼
靜態使用也就是配置在
AndroidManifest.xml
中配置意圖過濾器來匹配
關於intent的相關知識,見前一篇,這裏不作解釋編程
/**
* 做者:張風捷特烈<br></br>
* 時間:2019/1/21/021:16:53<br></br>
* 郵箱:1981462002@qq.com<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.com.br.toast"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
複製代碼
---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {
val intent = Intent("www.toly1994.com.br.toast")
sendBroadcast(intent)
}
複製代碼
intent必須指定廣播的component,纔有效ruby
---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {
val intent = Intent("www.toly1994.com.br.toast")
intent.component = ComponentName(
"com.toly1994.tolyservice",//項目包名
"com.toly1994.tolyservice.receiver.receiver.ToastBroadcastReceiver"//廣播接收者全類名
)
sendBroadcast(intent)
}
複製代碼
廣播接收者的onReceive回調中有intent: Intent,你應該明白怎麼傳數據了吧bash
---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {
val intent = Intent("www.toly1994.com.br.toast")
id_et_txt.text
//爲intent添加數據
intent.putExtra("toast_data", id_et_txt.text.toString())
intent.component = 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()
}
複製代碼
感受從上面來看,BroadcastReceiver的onReceive確實耦合性很是低
外部只須要用intent和context.sendBroadcast就能觸發它
但彷佛BroadcastReceiver也沒有太大的亮點,做用平平
爲了說明他的亮點,如今咱們新建一個app:Anotherapp
併發
能夠發如今另外一個app裏也能正常使用這個廣播
這就有點意思了,我在A項目中寫了一個類,它的方法能夠在B項目中觸發
這就是靜態廣播厲害的地方,也是我第一次接觸的跨進程通訊
(這說明解耦到必定的境界,就天下與我同,然而我將無處不在,手動滑稽)app
BroadcastReceiver動態使用分爲註冊和註銷,
不須要在AndroidManifest.xml註冊
只有在註冊後和註銷前的時間段才能使用,不然廣播無效(即onReceive方法不會掉)dom
/**
* 註冊廣播
*/
private fun register() {
val filter = IntentFilter()//建立意圖過濾器
filter.addAction("www.toly1994.com.br.toast2")//添加意圖
mReceiver = Toast2BroadcastReceiver()//建立 Toast2BroadcastReceiver
registerReceiver(mReceiver, filter)//註冊
}
/**
* 發送廣播
*/
private fun sendMsg() {
val intent = Intent()
intent.action = "www.toly1994.com.br.toast2"
intent.putExtra("toast_data", id_et_txt.text.toString())
sendBroadcast(intent)
}
複製代碼
你說,哥就不註銷怎麼樣?---答:異常唄
若是不註銷,崩了一個異常,源碼也好心提醒你要unregisterReceiver
ide
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()//註銷廣播
}
複製代碼
你可能會說:就一個200多行的類,還搞那麼多事...post
動態註冊的廣播
|---優點:能夠自由的控制註冊和取消,有很大的靈活性。
|---劣勢:只有在註冊以後才能起做用,在Activity的onDestroy後若是未被註銷,會報異常
----因此動態註冊的廣播存活時間最長也就約等於Activity的生命週期長度
靜態註冊的廣播
|---優點:不受程序是否啓動的約束,隨時使用
|---劣勢:優點一樣也是劣勢,沒法取消,何時都能用
複製代碼
先講個場景:男孩(Boy)說:一塊石頭的價值 1元
以後將石頭給了雕刻家,並將預期的價值1000元傳遞給雕刻家
以後雕刻家將石頭給了寶石家,並將預期的價值10W元傳遞給寶石家
以後寶石家將石頭給了收藏家,並將預期的價值100W元傳遞給收藏家
收藏家向外稱城本身的寶石價值100W
/**
* 做者:張風捷特烈<br></br>
* 時間:2019/1/21/021:16:53<br></br>
* 郵箱:1981462002@qq.com<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()
}
}
複製代碼
/**
* 註冊廣播
*/
private fun register() {
val filter = IntentFilter()//建立意圖過濾器
filter.addAction("www.toly1994.com.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.com.br.toast2"
val collectorBReceiver = CollectorBReceiver()
sendOrderedBroadcast(
intent, null,
collectorBReceiver, null, 1,
"價值1元", null
)
}
/**
* 註銷廣播
*/
private fun unRegister() {
unregisterReceiver(boyReceiver)
unregisterReceiver(graverReceiver)
unregisterReceiver(rubyManReceiver)
}
複製代碼
* 說明:雕刻家
*/
class GraverBReceiver : BroadcastReceiver() {
/**
* 接收時調用的方法
*/
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "雕刻家:$resultData", //[1]獲取結果並展現
Toast.LENGTH_LONG).show()
abortBroadcast();//[2]終止廣播
resultData = "價值10W元"//[3]傳遞數據---給下一個廣播
}
}
複製代碼
/**
* 註冊廣播
*/
private fun register() {
boyReceiver = BoyBReceiver()
val boyFilter = IntentFilter()//建立意圖過濾器
boyFilter.addAction("www.toly1994.com.br.toast2")//添加意圖
boyFilter.priority = 10//指定過濾器優先級
graverReceiver = GraverBReceiver()
val graverFilter = IntentFilter()//建立意圖過濾器
graverFilter.addAction("www.toly1994.com.br.toast2")//添加意圖
graverFilter.priority = 20//指定過濾器優先級
rubyManReceiver = RubyManBReceiver()
val rubyManFilter = IntentFilter()//建立意圖過濾器
rubyManFilter.addAction("www.toly1994.com.br.toast2")//添加意圖
rubyManFilter.priority = 21//指定過濾器優先級
registerReceiver(boyReceiver, boyFilter)//註冊
registerReceiver(graverReceiver, graverFilter)//註冊
registerReceiver(rubyManReceiver, rubyManFilter)//註冊
}
複製代碼
上面是BroadcastReceiver有序廣播的動態註冊形式的代碼,
靜態註冊在AndroidManifest.xml裏配置相似,就不廢話了
還有一點注意的是sendOrderedBroadcast方法調用時傳入的BroadcastReceiver
爲最後調用的BroadcastReceiver,不須要註冊!
如下皆使用動態註冊,不少系統級的行爲靜態註冊都是無效的
/**
* 做者:張風捷特烈<br></br>
* 時間:2019/1/22/022:16:43<br></br>
* 郵箱:1981462002@qq.com<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]添加要註冊的action
filter.addAction("android.intent.action.SCREEN_OFF")
filter.addAction("android.intent.action.SCREEN_ON")
// [4]註冊廣播接收者
registerReceiver(mScreenReceiver, filter)
}
複製代碼
注意權限:
<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.com<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.messageBody
val date = Date(smsMessage.timestampMillis)//時間
val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA)
//[4]獲取發送者
val address = smsMessage.originatingAddress
val 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)
}
複製代碼
這裏傳入一個Textview用於顯示電量
/**
* 做者:張風捷特烈<br></br>
* 時間:2019/1/22/022:16:43<br></br>
* 郵箱:1981462002@qq.com<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 / total
Log.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)
}
複製代碼
/**
* 做者:張風捷特烈<br></br>
* 時間:2019/1/22/022:16:43<br></br>
* 郵箱:1981462002@qq.com<br></br>
* 說明:app安裝/卸載改變時廣播監聽
*/
class AppBReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
val uri = intent.data
if (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";
//廣播更新進度--Action
public static final String ACTION_UPDATE = "action_update";
}
---->[MusicPlayerWithBrStub]-----------------------------
mTimer.schedule(new TimerTask() {
@Override
public 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 {
@Nullable
private ProgressView progressView;
public UpdateReceiver(@Nullable ProgressView progressView) {
this.progressView = progressView;
}
@Override
public 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的源碼,暫時就不讀了(讀了一下,沒怎麼讀得通...),之後再開篇吧!