Android的广播机制
一、广播的类型
标准广播:完全异步执行的广播,发出后所有广播接收器都同一时刻接收到这条广播信息,没有先后顺序,效率较高,但也无法截断。
有序广播:同步执行的广播,发出后同一时刻只有一个广播接收器能够收到这条广播消息,当这个接收器的逻辑执行完毕后才会继续传递,所以接收器是有先后顺序的,优先级高的可以先收到并且可以截断广播。
二、广播接收器
广播接收器可以*地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能够收到该广播,并在内部处理相应的逻辑。注册广播的方式一般有两种,在代码中注册和在AndroidManifest.xml中注册,其中前者也被称为动态注册,后者也被称为静态注册。新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。
三、接收系统广播
可以通过监听Android内置的系统级别的广播来得到各种系统状态信息。
1.动态注册监听网络变化
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
//当网络状态发生变化时,系统发出的正是一条值为android.net.conn.CONNECTIVITY_ CHANGE的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的action就行了
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
//动态注册广播接收器
registerReceiver(networkChangeReceiver, intentFilter);
}
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//ConnectivityManager是个系统服务类,专门用于管理网络连接的
ConnectivityManager connectionManager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
//查询系统的网络状态需要声明权限<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
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();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//动态注册的广播接收器一定都要取消注册
unregisterReceiver(networkChangeReceiver);
}
2.静态注册实现开机启动
使用静态注册让程序在未启动的情况下就能接收到广播。
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
}
}
监听系统开机广播也是需要声明权限的
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- 由于Android系统启动完成后会发出一条值为android.intent.action.BOOT_COMPLETED的广播,因此我们在这里添加了相应的action。 -->
<receiver android:name=".BootCompleteReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
注意:不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等。
四、发送自定义广播
1.自定义接收器和注册(其他程序接收同理)
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
}
}
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="com.example.broadcasttest. MY_BROADCAST"/>
</intent-filter>
</receiver>
2.修改按钮的点击事件发送自定义广播
public void onClick(View v) {
Intent intent = new Intent("com.example.broadcasttest. MY_BROADCAST");
sendBroadcast(intent);
}
五、发送有序广播
方法:
sendOrderedBroadcast(intent, null); //第二个参数是一个与权限相关的字符串
优先级设置:
<intent-filter android:priority="100" >
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
截断广播:
abortBroadcast();
六、使用本地广播
使用本地广播机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播。本地广播无法静态注册。
优势:
1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄漏的问题。
2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。
3. 发送本地广播比起发送系统全局广播将会更加高效。
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例
localBroadcastManager.sendBroadcast(intent); // 发送本地广播
intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter); // 注册本地广播监听器
记得在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();
}
}
七、实践——强制下线
实现思路:用ActivityController管理全部活动。在需要强制下线的时候发送一个广播,在自定义的接收器的onReceive()中显示对话框,确认后调用ActivityController的finishAll()方法关闭所有活动并回到登录界面。
ActivityController代码:
package com.linh.forceoffline;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<Activity>();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
//遍历activities中每个activity,关闭他们
public static void finishAll()
{
for(Activity activity:activities)
{
if(!activity.isFinishing())
{
activity.finish();
}
}
}
}
BaseActivity作为所有activity的父类:
package com.linh.forceoffline;
import android.app.Activity;
import android.os.Bundle;
public class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
LoginActivity实现简单的登录功能,登录后进入MainActivity。
MainActivity中点击按钮则发送一条广播。
自定义广播接收器ForceOfflineReceiver :
package com.linh.forceoffline;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.view.WindowManager;
public class ForceOfflineReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
//构建一个对话框
AlertDialog.Builder dialogBuilder=new AlertDialog.Builder(context);
dialogBuilder.setTitle("警告");
dialogBuilder.setMessage("你被强制下线,请重新登录");
//一定要不可取消
dialogBuilder.setCancelable(false);
//注册确认按钮
dialogBuilder.setPositiveButton("确认", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCollector.finishAll();//销毁所有活动
Intent intent=new Intent(context,LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//重新启动LoginActivity
context.startActivity(intent);
}
});
AlertDialog alertDialog=dialogBuilder.create();
//需要设置AlertDialog的类型,保证在广播接收器中可以正常弹出
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog.show();
}
}
最后在AndroidManifest.xml配置:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".LoginActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity" >
</activity>
<receiver android:name=".ForceOfflineReceiver" >
<intent-filter>
<action android:name="com.example.broadcastbestpractice. FORCE_OFFLINE" />
</intent-filter>
</receiver>
</application>