1.BroadcastReceiver简介
BroadcastReceiver翻译为广播接收者,广播是一种用于程序之间进行信息传输的机制。是典型的发布-订阅模式。
2.分类
- 标准广播(Normal Broadcasts):
完全异步
,在同一时刻能被所有广播接收者收到(他们之间没有任何先后顺序),消息传递的效率相对高;但是接收者无法将处理结果传递给下一个接收者,且无法拦截
广播Intent的传递。工作示意图如下。
- 有序广播(Ordered Broadcasts):
一种**同步执行
**的广播,广播发出之后同一时刻只有单独一个广播接收器能够收到广播,当这条广播执行完毕后才能继续传递。所有的广播接收器都有先后顺序,优先级高的先接收到广播
;前面的广播接收器还可以截断
正在传递的广播,截断后,后面的接收器就无法正常接收广播。在有序广播中,还可以将第一个接收器中处理后的```结果通过广播
传递给下一个接收器`。工作示意图如下。
它的优先级可以通过xml设置,也可以通过IntentFilter.setPriority()动态设置。(-1000-1000之间),值越大优先级越高。 - 本地广播(Local Broadcasts):
上述两种广播都属于系统全局的广播,我们发出的广播可以被任何应用程序接收到,也可以接收到任意程序发送的广播,这样就很容易引起安全问题(比如发送的广播携带用户信息等,很容易被其他截断)。本地广播即只能在本应用程序内进行传递的广播,并且广播接收器只能接收应用程序内部的广播。
这样就解决了安全性问题。 - 粘性广播(Sticky Broadcasts):
这种广播一般而言不会终止,只要有符合条件的广播接收者都能接收,除非某广播接收者告知不要再发送广播。
3.生命周期
如果一个广播处理完onReceive(),那么这个系统将认定此对象将不再活动,会被系统杀掉。
因此,在广播中不能进行耗时操作,否则会导致ANR。因为BroadcastReceiver的生命周期很短,在执行完onReceive()后就被销毁了,销毁时其子线程可能还未结束。
在 Android 中,程序的响应( Responsive )被活动管理器( Activity Manager )和窗口管理器( Window Manager )这两个系统服务所监视。当 BroadcastReceiver 在 10 秒内没有执行完毕,Android 会认为该程序无响应。所以在 BroadcastReceiver 里不能做一些比较耗时的操作,否侧会弹出ANR ( Application No Response )的对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent 给 Service ,由 Service 来完成。而不是使用子线程的方法来解决,因为 BroadcastReceiver 的生命周期很短(在 onReceive() 执行后 BroadcastReceiver 的实例就会被销毁),子线程可能还没有结束BroadcastReceiver 就先结束了。如果 BroadcastReceiver 结束了,它的宿主进程还在运行,那么子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。
4.使用
BroadcastReceiver一般使用步骤:
- 自定义类继承BroadcastReceiver,重写onReceive()方法
- 根据需求判断以哪种方式注册广播。注册方式有两大类。见标题5
使用案例1(使用系统广播
-监听网络状态的改变):(**动态注册广播**
)
自定义类:
package com.wdl.broadcastreceiverde;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.widget.Toast;
/**
* author: wdl
* time: 2018/12/7 19:09
* des: 监听网络变化的广播
*/
public class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//获取系统关于网络的服务
ConnectivityManager manager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
assert manager != null;
//获取网络状态
NetworkInfo info = manager.getActiveNetworkInfo();
if (info!=null&&info.isAvailable()){
Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
}
}
}
动态注册此广播:
package com.wdl.broadcastreceiverde;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private NetworkChangeReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
receiver = new NetworkChangeReceiver();
IntentFilter filter = new IntentFilter();
//添加action 关于网络状态的广播
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//动态注册广播
registerReceiver(receiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
}
效果:
有序广播的使用:(静态注册-有序
)
自定义两个广播并在AndroidManifest.xml文件中静态注册:
package com.wdl.broadcastreceiverde;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyAnotherReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "This is another cast!", Toast.LENGTH_SHORT).show();
}
}
package com.wdl.broadcastreceiverde;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver {
public static final String TAG = "MyBroadcastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
//运行在主线程中
Log.e("wdl", "MyBroadcastReceiver: "+Thread.currentThread().getId());
String value = intent.getStringExtra(TAG);
Toast.makeText(context, ""+value, Toast.LENGTH_SHORT).show();
//截断广播,有序广播时才调用,否则会报错
abortBroadcast();
}
}
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="500">
<action android:name="com.wdl.broadcastreceiverde.MyBroadcastReceiver" />
</intent-filter>
</receiver>
<receiver
android:name=".MyAnotherReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="200">
<action android:name="com.wdl.broadcastreceiverde.MyBroadcastReceiver" />
</intent-filter>
</receiver>
通过android:priority设置优先级别
发送广播即可接按顺序接收到广播,通过abortBroadcast()
可截断广播。
注意:
通过有序广播将第一个广播接收器的处理结果传递给下一个广播接收器。在优先级最高的广播接收器中通过setResult(int code,String data,Bundle bundle)设置值
,在第二个广播接收器中通过getResultCode()
等获取相关值。
5.注册方式
- 动态注册
receiver = new NetworkChangeReceiver();
IntentFilter filter = new IntentFilter();
//添加action 关于网络状态的广播
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//动态注册广播
registerReceiver(receiver, filter);
动态注册的广播不是常驻型
广播,其会受到activity生命周期的影响;在activity结束前,必须移除广播接收器。
3. 静态注册
在AndroidManifest.xml中声明:
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
静态注册时常驻型广播,即应用程序退出后,如果有广播来时,程序也会被系统自动运行。
顺序:
- 有序广播:
优先级高的先接接收;
同优先级,动态优先于静态;
同优先级同类,静态:先扫描优于后扫描,动态:先注册优于后注册; - 普通广播:
无优先级,动态优于静态;
同优先级同类广播,静态:先扫描优于后扫描,动态:先注册优于后注册;
5.本地广播的使用
基本方法与上述的一致,区别在于本地广播是使用LocalBroadcastManager
来对广播进行管理,而上述使用的是Context。
使用:
package com.wdl.broadcastreceiverde;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class LocalBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "receiver local broadcast!", Toast.LENGTH_SHORT).show();
}
}
package com.wdl.broadcastreceiverde;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private LocalBroadCastReceiver localReceiver;
private LocalBroadcastManager localManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localManager = LocalBroadcastManager.getInstance(this);
localReceiver = new LocalBroadCastReceiver();
localManager.registerReceiver(localReceiver,new IntentFilter("com.wdl.localbroadcast"));
btnSendLocal = findViewById(R.id.btnSendLocal);
btnSendLocal.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.wdl.localbroadcast");
localManager.sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
localManager.unregisterReceiver(localReceiver);
}
}
广播 | 方法 | 效果 |
---|---|---|
本地广播 | LocalBroadcastManager .sendBroadcast(Intent intent) | 发送本地标准广播 |
本地广播 | LocalBroadcastManager .sendBroadcastSync(Intent intent) | 发送本地有序广播 |
标准广播 | Context .sendBroadcast(Intent intent) | 发送标准广播 |
有序广播 | Context .sendOrderedBroadcast(Intent intent) | 发送有序广播 |
本地广播 | LocalBroadcastManager .registerReceiver(Receive re,IntentFilter filter) | 注册本地广播 |
非本地广播 | Context .registerReceiver(Receive re,IntentFilter filter) | 注册广播 |
6.广播注意事项
- IntentFilter中必须设置action对象
- 每次接受一个广播都会重新生成一个接受广播的对象
- BroadcastReceiver中尽量不要处理耗时逻辑,可转交给Service处理
此文章参考至:
https://www.jianshu.com/p/51aaa65d5d25
https://www.jianshu.com/p/1083545bde51