第5章 详解广播机制
本系列学习笔记第5章
前言
打算把android基本知识点写一个系列,旨在把android基础书,例如《Android 第一行代码 第2版》、《爱上android》、《疯狂android讲义》等书的一些知识点记录一下,会持续更新内容,为了方便自己复习,也希望可以帮助到大家!
1、广播机制的简介
android中的广播主要可以分为:
1)标准广播
一种异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播信息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。
2)有序广播
是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕之后,广播才会继续传递,所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播信息,并且前面的广播接收器还可以截断正在传递的广播。
2、接收系统广播
2.1 动态注册监听网络变化
声明权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
MainActivity,java文件
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private NetworkChangeReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
receiver = new NetworkChangeReceiver();
registerReceiver(receiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
class NetworkChangeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.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();
}
}
}
}
2.2 更加全面的动态注册监听网络变化
public class NetChangeActivity extends AppCompatActivity{
private NetWorkChangeReceiver netWorkChangeReceiver;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
netWorkChangeReceiver = new NetWorkChangeReceiver();
registerReceiver(netWorkChangeReceiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (netWorkChangeReceiver != null){
unregisterReceiver(netWorkChangeReceiver);
}
}
//广播接受者
class NetWorkChangeReceiver extends BroadcastReceiver{
private static final int NETTYPE_WIFI = 0x01;
private static final int NETTYPE_CMWAP = 0x02;
private static final int NETTYPE_CMNET = 0x03;
private static final int NETTYPE_NONE = 0x04;
private int netType = NETTYPE_NONE;
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "网络状态发生变化:"+getNetWorkName(getNetWorkTpye()), Toast.LENGTH_SHORT).show();
}
/**
* 获取当前网络类型
* @return
*/
private int getNetWorkTpye(){
//获取网络管理者
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
//获取当前网络信息
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo == null){
return netType;
}
int type = networkInfo.getType();
if (type == ConnectivityManager.TYPE_MOBILE){
String extraInfo = networkInfo.getExtraInfo();
if (!TextUtils.isEmpty(extraInfo)){
if (extraInfo.toLowerCase().equals("cmnet")){
netType = NETTYPE_CMNET;
}else {
netType = NETTYPE_CMWAP;
}
}
}else if (type == ConnectivityManager.TYPE_WIFI){
netType = NETTYPE_WIFI;
}
return netType;
}
private String getNetWorkName(int type){
String netName = "";
switch (type){
case NETTYPE_WIFI:
netName = "WIFI";
break;
case NETTYPE_CMWAP:
netName = "wap网络";
break;
case NETTYPE_CMNET:
netName = "net网络";
break;
case NETTYPE_NONE:
netName = "无网络";
break;
}
return netName;
}
}
}
2.3 静态注册实现开机启动
声明权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
注册
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
BootCompleteReceiver.java文件
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "手机开机啦!", Toast.LENGTH_SHORT).show();
}
}
3、发送自定义广播
3.1 发送标准广播
activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.broadcastreceivertest.MainActivity">
<Button
android:id="@+id/btn_send_broadcast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="发送标准广播"
android:textColor="@color/colorAccent"
/>
</LinearLayout>
MyBroadcastReceiver.java文件
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"
android:enabled="true"
android:exported="true"
>
<intent-filter>
<action android:name="com.example.broadcastreceivertest.MyBroadcastReceiver"/>
</intent-filter>
</receiver>
MainActivity.java文件
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.btn_send_broadcast);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcastreceivertest.MyBroadcastReceiver");
sendBroadcast(intent);
}
});
}
}
新建另外一个工程
AnotherBroadcastReceiver.java文件
public class AnotherBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show();
}
}
注册
<receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true"
>
<intent-filter>
<action android:name="com.example.broadcastreceivertest.MyBroadcastReceiver"/>
</intent-filter>
</receiver>
运行俩个项目就可以发现在一个程序里发出的广播,在另外一个程序中也可以收到了
image.png image.png
3.2 发送有序广播
广播是一种可以跨进程的通信方式
在上面代码的基础上修改:
AndroidManifest.xml文件
<receiver android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true"
>
<intent-filter
android:priority="1000"
>
<action android:name="com.example.broadcastreceivertest.MyBroadcastReceiver"/>
</intent-filter>
</receiver>
MyBroadcastReceiver.java文件
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}
MainActivity.java文件
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.btn_send_broadcast);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcastreceivertest.MyBroadcastReceiver");
sendOrderedBroadcast(intent,null);
}
});
}
}
对广播进行截断之后,现在可以发现另外一个程序就接收不到广播了
image.png
3.3 有序广播的另外一个重载方法
image.png可以指定接收结果的广播接收者,该广播必须是程序内部,无论前面有多少个广播接收者,哪怕广播被中断,该广播都能接收信息,而且不需要注册
image.png
4、使用本地广播
之前送发送和接收的广播全部属于系统全局广播,即发出的广播可以被其它应用程序接收到,并且我们也可以接收其它应用程序的广播,这样就很容易引起安全性的问题,因此android中引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。
image.pngactivity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.broadcastreceivertest.MainActivity">
<Button
android:id="@+id/btn_send_broadcast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="发送本地广播"
android:textColor="@color/colorAccent"
/>
</LinearLayout>
MainActivity.java文件
public class MainActivity extends AppCompatActivity {
private LocalBroadcastManager localBroadcastManager;
private LocalReceiver localReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.btn_send_broadcast);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcastreceivertest.Local_Broadcast");
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver,intentFilter);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcastreceivertest.Local_Broadcast");
localBroadcastManager.sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localReceiver);
}
class LocalReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接收本地广播", Toast.LENGTH_SHORT).show();
}
}
}
5、广播的最佳实践--------实现强制下线功能
ActivityCollector.java文件
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}
BaseActivty.java文件
public class BaseActivity extends AppCompatActivity {
private ForceOffLineReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.broadcastreceivertest.FORCE_OFFLINE");
receiver = new ForceOffLineReceiver();
registerReceiver(receiver,filter);
}
@Override
protected void onPause() {
super.onPause();
if (receiver != null){
unregisterReceiver(receiver);
receiver = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
class ForceOffLineReceiver extends BroadcastReceiver{
@Override
public void onReceive(final Context context, Intent intent) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("温馨提示:");
builder.setMessage("你已经被强制下线,请重新登录");
builder.setCancelable(false);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityCollector.finishAll();
Intent mIntent = new Intent(context,LoginActivity.class);
context.startActivity(mIntent);
}
});
builder.show();
}
}
}
activity_login.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="60dp">
<TextView
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="18sp"
android:text="Account:" />
<EditText
android:id="@+id/account"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="60dp">
<TextView
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="18sp"
android:text="Password:" />
<EditText
android:id="@+id/password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:inputType="textPassword" />
</LinearLayout>
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Login" />
</LinearLayout>
LoginActivity.java文件
public class LoginActivity extends BaseActivity {
private EditText accoutEdit;
private EditText passwordEdit;
private Button btnLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
accoutEdit = (EditText) findViewById(R.id.account);
passwordEdit = (EditText) findViewById(R.id.password);
btnLogin = (Button) findViewById(R.id.login);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String account = accoutEdit.getText().toString();
String password = passwordEdit.getText().toString();
if (account.equals("admin") && password.equals("123456")){
Intent intent = new Intent(LoginActivity.this,ForceOffLineActivtiy.class);
startActivity(intent);
finish();
}else {
Toast.makeText(LoginActivity.this, "用户名或者密码不正确", Toast.LENGTH_SHORT).show();
}
}
});
}
}
activity_forceoffline.xml文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_off_line"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="强制下线"
android:textColor="@color/colorAccent"
/>
</android.support.constraint.ConstraintLayout>
ForceOffLineActivtiy.java文件
public class ForceOffLineActivtiy extends BaseActivity {
private Button btnOffLine;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forceoffline);
btnOffLine = (Button) findViewById(R.id.btn_off_line);
btnOffLine.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcastreceivertest.FORCE_OFFLINE");
sendBroadcast(intent);
}
});
}
}
image.png
image.png
image.png
6、广播的实践--------实现桌面快捷方式的创建与删除
public class ShortcutUtils {
/**
* 创建快捷方式
* @param context 上下文
* @param shortcutName 快捷方式的名称
* @param iconRes 设置快捷方式图片Res 如 R.drawable.xxx / R.mipmap.xxx
* @param actionIntent 设置快捷方式动作
* @param allowRepeat 是否允许重复创建
*/
public static void addShortcut(Context context, String shortcutName,
int iconRes, Intent actionIntent, boolean allowRepeat){
Intent shortcutintent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
//是否允许重复创建
shortcutintent.putExtra("duplicate",allowRepeat);
//快捷方式的名称
shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutName);
//设置快捷方式图片
Parcelable icon = Intent.ShortcutIconResource.fromContext(context.getApplicationContext(), iconRes);
shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
//设置快捷方式动作
shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent);
//向系统发送广播
context.sendBroadcast(shortcutintent);
}
/**
* 创建快捷方式
* @param context 上下文
* @param shotcutName 快捷方式的名称
* @param bitmap 设置快捷方式图片
* @param actionIntent 设置快捷方式动作
* @param allowRepeat 是否允许重复创建
*/
public static void addShortcut(Context context, String shotcutName,
Bitmap bitmap, Intent actionIntent, boolean allowRepeat){
Intent shortcutintent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
//是否允许重复创建
shortcutintent.putExtra("duplicate",allowRepeat);
//快捷方式的名称
shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_NAME, shotcutName);
//设置快捷方式图片
shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
//设置快捷方式动作
shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent);
//向系统发送广播
context.sendBroadcast(shortcutintent);
}
/**
* 删除快捷键
*
*/
public static void deleteShortcut(Context context, String name
,Intent actionIntent,boolean allowRepeat){
Intent shortcutintent = new Intent("com.android.launcher.action.UNINSTALL_SHORTCUT");
//是否循环删除
shortcutintent.putExtra("duplicate",allowRepeat);
//快捷方式的名称
shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
//设置快捷方式动作
shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent);
//向系统发送广播
context.sendBroadcast(shortcutintent);
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//删除快捷方式的点击事件
public void deleteShortCut(View view) {
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.baidu.com"));
ShortcutUtils.deleteShortcut(this,"打开百度",intent,false);
}
//按钮点击事件 创建快捷方式
public void addShortCut(View view) {
//快捷方式打开的意图必须是隐式意图
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.baidu.com"));
ShortcutUtils.addShortcut(this,"打开百度",R.mipmap.ic_launcher,intent,false);
}
}