Android广播机制的详解
工作也一年了,自觉学到的东西不多,最近常常在反思,因为在一家产品公司,产品效果不是很好,所以就比较闲了,于是最近在疯狂找东西学,所以找了郭霖大神第一行代码来看,发现还是有一些知识点不懂的,也有一些知识点因为工作不常用所以略为生疏了,所以接下来的日子就是一边看书一边总结了,每天进步一点点,不为什么,就为找个漂亮点的老婆,这篇文章部分总结自郭霖《第一行代码》第五章 全局大喇叭,详解广播机制和网上的一些资料。如果侵权麻烦联系本人即删。
广播机制
广播
按发送顺序
- 标准广播 –> 完全异步执行的广播,所有广播接收器几乎同时收到,效率高,无法截断。
- 有序广播 –> 同步执行的广播,按优先级顺序发送,同一时间只有一个广播接收,前面的广播可以截断后面的。
按发送范围
- 全局广播 –> 发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播,数据不安全。
- 本地广播 –> 只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,其他软件不会监听到我们的广播数据。
- 广播接收器
- 动态注册 –> 在代码中注册,必须启动软件后才能接收到广播;此外,动态注册的广播接收器一定都要取消注册。
- 静态注册 –> AndroidManifest.xml 中注册,程序未启动就可以监听,例如很常用的开机启动监听。
1.1标准广播 : 其实也就是无序广播,一起发送给所有人
//新建一个 MyBroadcastReceiver继承自 BroadcastReceiver,代码如下所示:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver",
Toast.LENGTH_SHORT).show();
}
}
//AndroidManifest.xml 注册自己的广播接收器
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcasttest"
android:versionCode="1"
android:versionName="1.0" >
……
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
……
<!-- MyBroadcastReceiver 接 收 一 条 值 为 com.example.broadcasttest.
MY_BROADCAST的广播-->
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="com.example.broadcasttest. MY_BROADCAST"/>
</intent-filter>
</receiver>
</application>
</manifest>
//activity_main.xml布局文件 点击按钮发送广播
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send Broadcast"
/>
</LinearLayout>
//程序入口
public class MainActivity extends Activity {
……
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
//点击按钮就发出标准广播。
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//intent还可以携带其他一些数据的。
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
//标准广播和有序广播差别就是发送广播的方法不一样,后者用的是sendOrderedBroadcast(intent,null);
sendBroadcast(intent);
}
});
……
}
……
}
1.2无序广播 : 可以通过在xml文件配置设置优先级来优先获取广播,再让自己决定是否要把广播传递下去
这个demo1.2是用来接收 demo1.1自定义广播,因为除了系统发出的广播我们可以接收,自己程序定义的广播(demo1.1中的)我们demo1.2也是可以监听到的。
//新建一个广播接收者,当1.1中的demo启动,会触发这里的接收器,因为在Androidmanifest.xml文件里那边设置的action和demo1一样。
public class AnotherBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in AnotherBroadcastReceiver",
Toast.LENGTH_SHORT).show();
}
}
//Androidmanifest.xml配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcasttest2"
android:versionCode="1"
android:versionName="1.0" >
……
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
……
<!--AnotherBroadcastReceiver接受的和demo1筛选的是同一个活动-->
<receiver android:name=".AnotherBroadcastReceiver" >
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
</application>
</manifest>
//程序入口
public class MainActivity extends Activity {
……
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
//有序广播,但是这里没啥用,因为没设置优先级,也没进行截断广播的操作,但是可以做些事情,看下面的deom。
sendOrderedBroadcast(intent, null);
}
});
……
}
……
}
稍微修改demo1.2就可以看到如何截断广播了
//AndroidManifest.xml 修改配置文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcasttest"
android:versionCode="1"
android:versionName="1.0" >
……
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
……
<!--这里设置了.AnotherBroadcastReceiver的优先级为100,所以1.1的-->
<receiver android:name=".AnotherBroadcastReceiver">
<intent-filter android:priority="100" >
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
</application>
</manifest>
// 高优先级的截断广播,其下面的无法监听到。
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceive",
Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}
1.3 全局广播 上面讲的全局广播。
1.4 本地广播
全局广播有可能会被其他软件监听到,或者其他软件不停的发送一些垃圾广播到我们接收器里,会有安全性问题,所以有Android有一套本地广播的机制,只能在本应用内生效,所以本地广播无法用静态注册的方式,因为静态注册就是为了让程序没启动也能收到广播,不过他用的是LocalBroadcastManager,看代码
public class MainActivity extends Activity {
private IntentFilter intentFilter;
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
// 获取实例
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.broadcasttest.
LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent); // 发送本地广播
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);
// 注册本地广播监听器
}
@Override
protected void onDestroy() {
super.onDestroy();
//一样要取消注册。
localBroadcastManager.unregisterReceiver(localReceiver);
}
class LocalReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received local broadcast",
Toast.LENGTH_SHORT).show();
}
}
}
2.1动态注册
实现监听网络变化Demo
- 创建一个广播接收器,内部类。
- 在需要调用activity的类中进行动态注册。
- 销毁activity时一定要取消注册广播接收器。
//主入口
public class MainActivity extends Activity {
//活动过滤器
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
//我们的广播接收器想要监听什么广播,符合这个条件的才通过;当网络发生变化,系统发出这条值"android.net.conn.CONNECTIVITY_CHANGE"
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
//第二步注册广播接收器
registerReceiver(networkChangeReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
//第三步临走前取消注册广播接收器
unregisterReceiver(networkChangeReceiver);
}
/**
*第一步:内部类,继承自 BroadcastReceiver,并重写父类的 onReceive()方法就行了
**/
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isAvailable()) {
Toast.makeText(context, "network is available",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "network is unavailable",
Toast.LENGTH_SHORT).show();
}
}
}
}
//AndroidManifest.xml 查询系统的网络状态就是需要声明权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcasttest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<!--声明权限-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
……
</manifest>
2.2静态注册
实现开机启动Demo
/**
*不再使用内部类的方式来定义广播接收器,因为稍后我们需要在AndroidManifest.xml 中将这个广播接收器的类名注册进去。
**/
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
}
}
//AndroidManifest.xml 需要申明
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitytest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--获取权限-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".FirstActivity"
android:label="This is FirstActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--注册广播接收器,在过滤器里面加上活动类型android.intent.action.BOOT_COMPLETED-->
<receiver android:name=".BootCompleteReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
其实最常用的是通过广播实现 强制下线功能,也可以用来刷新数据。