赞
踩
概述:Android中的每个应用程序都可以对自己感兴趣的广播进行注册, 这样该程序就只会接收到自己所关心的广播内容,这些广播可能是
来自于系统的,也可能是来自于其他应用程序的。Android 提供了一套完整的API,允许应用程
序自由地发送和接收广播。
广播的分类:
标准广播:**( Normal broadcasts)**是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的 效率会比较高,但同时也意味着它是 无法被截断的 。
工作流程:
有序广播:( Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,
广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的
广播接收器就无法收到广播消息了。
工作流程:
概述:广播接收器可以自由地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能够收到该广播,并在内部处理相应的逻辑。注册广播的方式一般有两种,在代码中注册和在AndroidManifest.xml中注册,其中前者也被称为动态注册,后者也被称为静态注册。
实现广播接收器:
package com.example.broadcasttest; import androidx.appcompat.app.AppCompatActivity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter(); //创建IntentFilter实例 intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); //为IntentFilter实例添加此action networkChangeReceiver = new NetworkChangeReceiver(); //创建networkChangeReceiver实例 registerReceiver(networkChangeReceiver, intentFilter); //调用registerReceiver进行注册,使networkChangeReceiver能收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广播。 } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(networkChangeReceiver); //注意动态注册的广播接收器一定都要取消注册才行 } class NetworkChangeReceiver extends BroadcastReceiver { //此类继承BroadcastReceiver,每当网络状态发生变化时,onReceive()方法将被执行 @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(); } } } }
访问权限的声明:(在注册文件中加入此代码)
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
用Android Studio提供的快捷方式来创建一
个广播接收器,右击com.example.broadcasttest包建投New→Other→Broadcast Receiver,会弹出如下页面:
勾选这两个属性,完成创建。
修改BootCompleteReceiver中代码
package com.example.broadcasttest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show(); //显示提示消息
}
}
修改注册代码
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> //为此程序申请新的访问权限,开机访问权限 <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.BroadcastTest"> <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> //让此程序可以对开机广播做出反应 </intent-filter> </receiver> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
补充:Android8.0及以上除系统自发的广播如:开关机,网络权限,接收短信等可静态注册,别的BroadcastReceiver都得动态注册
定义一个广播接收器
package com.example.broadcasttest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show(); //接收到广播后发出弹窗回应
}
}
创建一个按钮,用作发送广播的触发点
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/send_broadcast"
android:id="@+id/button"/>
</LinearLayout>
修改主活动代码为
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //动态为广播接收器注册相应,当系统发出值为 com.example.broadcasttest.MY_BROADCAST 的广播时,此广播接受器做出响应 registerReceiver(new MyBroadcastReceiver(), new IntentFilter("com.example.broadcasttest.MY_BROADCAST")); /*intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver = new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter);*/ Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST"); //点击按钮,发出值为 com.example.broadcasttest.MY_BROADCAST 的广播。 sendBroadcast(intent); } }); }
结果为,点击按钮,界面弹出 received in MyBroadcastReceiver 的弹窗
发送有序广播秩序秩序要更改广播的发送方法:
sendOrderedBroadcast(intent, null); //ordered有序的
为广播接收器设置优先级:(由于安卓8.0以上不支持静态注册,所以创建IntentFilter对象用于广播对的动态注册,让此程序对什么广播值响应,还有设置接收器的优先级)
IntentFilter intentFilter = new IntentFilter("com.example.broadcasttest.MY_BROADCAST");
intentFilter.setPriority(100); //设置优先级为100
registerReceiver(new MyBroadcastReceiver(), intentFilter);
将广播截断,使用方法 abortBroadcast(); 将广播截断,使后面能对此广播值响应的广播接收器无法接受此条广播。
概述:为了能够简单地解决广播的安全性问题,Android 引人了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。
使用LocBroadcastManager对象来对广播进行管理,并提供了发送广播和注册广播接收器的方法。
package com.example.broadcasttest; import androidx.appcompat.app.AppCompatActivity; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private LocalReceiver localReceiver; private LocalBroadcastManager localBroadcastManager; //private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //为广播注册 /*intentFilter = new IntentFilter("com.example.broadcasttest.MY_BROADCAST"); intentFilter.setPriority(100); registerReceiver(new MyBroadcastReceiver(), intentFilter);*/ localBroadcastManager = LocalBroadcastManager.getInstance(this); //获取实例 /*intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver = new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter);*/ Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST"); //sendBroadcast(intent); //sendOrderedBroadcast(intent, null); localBroadcastManager.sendBroadcast(intent); } }); intentFilter = new IntentFilter("com.example.broadcasttest.LOCAL_BROADCAST"); //动态注册广播接收器 localReceiver = new LocalReceiver(); localBroadcastManager.registerReceiver(localReceiver,intentFilter); //使用localBroadcastManager类的方法为广播接收器注册。 } @Override protected void onDestroy() { super.onDestroy(); //unregisterReceiver(networkChangeReceiver); localBroadcastManager.unregisterReceiver(localReceiver); //为动态注册的广播接收器取消注册 } 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(); } } } class LocalReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show(); } } }
小结使本地广播的优势:
**强制下线功能需要先关闭掉所有的活动,然后回到登录界面。**如果你的反应足够快的话,应该会想到我们在第2章的最佳实践部分早就已经实现过关闭所有活动的功能了,因此这里只需要
使用同样的方案即可。
先实现一次性关闭所有活动功能:
创建ActivityCollector类用于将所有活动储存起来,便于一起操作
package com.example.broadcastbestpractice; import android.app.Activity; import java.util.ArrayList; import java.util.List; 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(); } } } }
创建BaseActivity类,使器程为所有活动的父类,通过此类将新开的活动添加进ActivityCollector类进行存储:
package com.example.broadcastbestpractice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; public class BaseActivity extends AppCompatActivity { private ForceOfflineReceiver receiver; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { //在活动创建时将活动添加 super.onCreate(savedInstanceState); ActivityCollector.addActivity(this); } @Override //进入可见时注册广播 protected void onResume() { super.onResume(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.example.broadcastbestpractice.FORCE_OFFLINE"); receiver = new ForceOfflineReceiver(); registerReceiver(receiver, intentFilter); } @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(Context context, Intent intent) { AlertDialog.Builder builder = new AlertDialog.Builder(context); //创建弹窗 builder.setTitle("Warning"); //设置弹窗标题为Warning //设置弹窗内容 builder.setMessage("You are force to be offline. Please try to login again."); builder.setCancelable(false); //设置弹窗权限,使其无法通过back键退出 builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { //在下部设置按钮,名字为OK @Override //设置按钮响应事件 public void onClick(DialogInterface dialogInterface, int i) { ActivityCollector.finishAll(); //结束所有活动 Intent intent = new Intent(context, LoginActivity.class); //从此活动跳转至登录页面 context.startActivity(intent); //开启登录页面活动 } }); builder.show(); } } }
创建登录界面活动,修改注册代码,使此活动为主活动
package com.example.broadcastbestpractice; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class LoginActivity extends AppCompatActivity { private EditText accountEdit; private EditText passwordEdit; private Button login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); accountEdit = (EditText) findViewById(R.id.account); passwordEdit = (EditText) findViewById(R.id.password); login = (Button) findViewById(R.id.login); login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String account = accountEdit.getText().toString(); String password = passwordEdit.getText().toString(); if (account.equals("admin") && password.equals("123456")) { Intent intent = new Intent(LoginActivity.this, MainActivity.class); startActivity(intent); //进入下一活动 finish(); //销毁当前活动 }else { Toast.makeText(LoginActivity.this, "account or password is invalid", Toast.LENGTH_SHORT).show(); //密码或账号错误进行弹窗提示 } } }); } }
设置登录活动的布局:
一个竖的线性布局里嵌套两个横的线性布局:
横的布局用于存放提示信息和输入数据
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout //输入账号 android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal"> <TextView android:layout_width="90dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="18sp" android:text="@string/account"/> <EditText android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/account" android:layout_gravity="center_vertical"/> </LinearLayout> <LinearLayout //输入密码 android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal"> <TextView android:layout_width="90dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="18sp" android:text="@string/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:layout_width="match_parent" android:layout_height="60dp" android:text="@string/login" android:id="@+id/login"/> </LinearLayout>
页面展示:
修改主活动代码:
package com.example.broadcastbestpractice; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button forceOffline = (Button) findViewById(R.id.force_offline); forceOffline.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //点击按钮,发送广播 Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE"); sendBroadcast(intent); } }); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。