安卓广播基本用法和实战:模拟强制下线
目录
参考资料:郭霖老师第一行代码第二版第五章
一.什么是广播
在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。在生活中,我们的收音机的广播电台需要调到特定的频率才能接收到内容,在android中也是一样,通过sendBroadcast来发送广播并携带一个action,只有接收者的action和发送者action相同,才可以接收到这个广播.
二.广播有什么用
监听应用发出的广播消息,并且做出响应,包括不同组件之间的通信,应用与应用之间的都可以监听到
组件之间:比如当手机电量到30的时候,我在页面弹出一个框
应用之间:打开淘宝时支付时唤起支付宝
3.注册广播
1.动态注册
在代码中注册
2.静态注册
在manifest中注册
动态和静态区别:静态广播的生存期比动态广播的长很多.
动态广播无需在manifest中注册,而静态广播需要
注意有时候而且有时候需要一些权限
Android8.0之后,所有隐式广播(没有指定发送到哪个应用程序的广播,大多数系统广播属于隐式广播)不允许使用静态注册的方法
4.其他
标准广播
有序广播:即多个广播可以设置优先级,发送的时候是sendOrderBroadcast(action),可以在manifest中的receiver中设置android:priority:"100"来设置优先级,也可以使用abortBroadcast()来阻断后面的广播
本地广播:全局广播可以被其他程序接收到,本地广播不会,需要多加LocalBroadcastManager
5.创建方法
动态广播:
Intent intent = new Intent(action写在里面); sendBroadcast(intent);
在另一个活动中(一般是BaseActivity)里:一个IntentFilter来接收action,一个继承了BroadCast的Receiver,注册一下,注销一下就可以i
静态广播:通过编译器新建一个Receiver,某一个活动发出action,Receiver来接收,要自己在manifest里设置action
举栗代码:
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.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
class NetworkChangeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"network changes",Toast.LENGTH_LONG).show();
}
}
}
代码解释:
首先定义了一个IntentFilter(意图过滤器).然后新建一个类去继承了BroadReceiver这个类,然后在onceiver方法里去写你要接受什么样的广播就行了.然后调用registerReceiver方法注册,最后在ondestory中取消了注册即可.
问题:IntentFilter这个东西是干嘛的呢?
答:主要用来过滤隐式意图。当用户进行一项操作的时候,Android系统会根据配置的 “意图过滤器”来寻找可以响应该操作的组件,服务。当网络变化时,系统发出的就是一条android.net.conn.CONNECTIVITY_CHANGE的广播,所以我们就用IntentFilter把它捕获.
注意:动态注册的广播注册器一定要取消注册才行.
第二步:
因为这里我们要访问系统的网络,所以需要声明权限,在manifest下
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
然后我们运行程序,点击home键,在模拟机上找到控制手机流量开关的地方,打开关闭流量即看到toast弹出(先把wifi那个关了,才有反应,在实体手机上似乎是只在应用内部显示,无法在全局显示)
改进只显示网络变化不够,还要知道是网络是打开还是关闭:
所以就在onReceive方法中改:
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.isConnected()){
Toast.makeText(context,"移动网络打开",Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(context,"移动网络关闭",Toast.LENGTH_SHORT).show();
}
}
}
解释:ConnectivityManager这个类有很多作用,可以用来监控网络连接
getSystemService:系统服务,然后参数是要获取的系统服务,如这里就获取了连接服务
然后调用connectivityManager的一个方法getActiveNetworkInfo()去获取网络状态
四.发送自定义广播(即非系统广播)
<?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"
android:orientation="vertical"
tools:context=".SecondActivity">
<EditText
android:id="@+id/account"
android:hint="请输入你的账号"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/password"
android:hint="请输入你的密码"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/login"
android:layout_gravity="center"
android:text="登录"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
package com.example.myapplication;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private TextView textView;
Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView =findViewById(R.id.textview);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("broadcast");
//需要加上下面这一句,不然收不到广播,第一个参数为包名,第二个参数为广播接收器
intent.setComponent(new ComponentName("com.example.myapplication","com.example.myapplication.MyReceiver"));
intent.putExtra("content","你的账号"+account.getText().toString()+"你的密码"+password.getText().toString());
sendBroadcast(intent);
}
});
}
}
新建一个接收器
package com.example.myapplication;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String content = intent.getStringExtra("content");
Toast.makeText(context,content,Toast.LENGTH_SHORT).show();
}
}
在manifests中,把要接收的action写好,所以是一个静态广播
<receiver
android:name=".MyReceiver">
<intent-filter>
<action android:name="broadcast" />
</intent-filter>
</receiver>
六.实战:模拟强制下线
因为我们在被强制下线时不知道在哪个界面,所以首先创建一个ActivityColletor来作为专门的集合类对所有活动进行管理
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();
}
}
activities.clear();
}
}
通过上面的集合类进行活动的管理,还要有一个类BaseActivity作为所有活动的父类去接受广播,这样不管你在哪个活动中都可以通过父类BaseActivity去调用开放方法finishAll去终结活动.(在这里还有一个需要注意的地方,就是我们的取消注册接收器放在了onPause()方法中,这样就是为了当一个activity失去栈顶位置就会自动取消广播接收器的注册.)
public class BaseActivity extends AppCompatActivity {
private OfflineReceiver offlineReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("offline");
offlineReceiver = new OfflineReceiver();
registerReceiver(offlineReceiver,intentFilter);
}
@Override
protected void onPause() {
super.onPause();
if (offlineReceiver != null){
unregisterReceiver(offlineReceiver);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
class OfflineReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, final Intent intent) {
AlertDialog.Builder builder = new AlertDialog.Builder(BaseActivity.this);
builder.setMessage("你被强制下线");
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCollector.finishAll();
Intent intent1 = new Intent(BaseActivity.this,ThirdActivity.class);
startActivity(intent1);
}
});
builder.show();
}
}
}
第三个activity:(实现登录)
<?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"
android:orientation="vertical">
<EditText
android:id="@+id/account"
android:hint="请输入你的账号"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/password"
android:hint="请输入你的密码"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/login"
android:layout_gravity="center"
android:text="登录"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
public class ThirdActivity extends BaseActivity{
private EditText account;
private EditText password;
private Button Login;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
account = findViewById(R.id.account);
password = findViewById(R.id.password);
Login = findViewById(R.id.login);
Login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = account.getText().toString();
String content1 = password.getText().toString();
if (content.equals("admin")&&content1.equals("123456")){
Intent intent = new Intent(ThirdActivity.this,fourth.class);
startActivity(intent);
}
else {
AlertDialog.Builder builder = new AlertDialog.Builder(ThirdActivity.this);
builder.setMessage("用户名或密码错误");
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
}
}
});
}
}
第四个acticvity,实现强制下线
<?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"
android:orientation="vertical"
tools:context=".fourth">
<Button
android:id="@+id/force_offline"
android:text="强制下线"
android:layout_marginTop="25dp"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
package com.example.myapplication;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class fourth extends BaseActivity {
private Button force;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fourth);
force = findViewById(R.id.force_offline);
force.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("offline");
sendBroadcast(intent);
}
});
}
}
kotlin版本代码:
一个监听时间变化的系统广播:一分钟toast一次
动态注册
class BroadcastActivity : AppCompatActivity() {
lateinit var timeChangeReceiver: TimeChangeReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_broadcast)
val intentFilter = IntentFilter()
intentFilter.addAction("android.intent.action.TIME_TICK")
timeChangeReceiver = TimeChangeReceiver()
registerReceiver(timeChangeReceiver, intentFilter)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(timeChangeReceiver)
}
inner class TimeChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context, "Time has changed", Toast.LENGTH_SHORT).show()
}
}
}
静态注册:
前面说了Android8.0之后隐式广播不允许静态注册,而以下这个开机广播比较特殊,还可以使用静态注册
可以通过新建一个Broadcast Receiver来创建一个广播接收者
class BootCompleteReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show()
}
}
最后需要在manifest中写权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver
android:name=".other.BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
运行结果:
自定义广播:
这里使用静态广播去接收一个按钮发出来的广播
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show()
}
}
这里定义我们的action,一会发广播时就要匹配这个值
<receiver
android:name=".other.MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.timingdemo.other.MY_BROADCAST" />
</intent-filter>
</receiver>
默认情况下我们发出的自定义广播为隐式广播,所以一定要调用setPackage()方法
button.setOnClickListener {
val intent = Intent("com.example.timingdemo.other.MY_BROADCAST")
intent.setPackage(packageName) //这一行必须加,否则为隐式广播无法被接收
sendBroadcast(intent)
}
发送有序广播
定义另外一个Receiver
class AnotherBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show()
}
}
设定优先级
<receiver
android:name=".other.AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="50">
<action android:name="com.example.timingdemo.other.MY_BROADCAST" />
</intent-filter>
</receiver>
<receiver
android:name=".other.MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.example.timingdemo.other.MY_BROADCAST" />
</intent-filter>
</receiver>
改用这个方法即可 sendOrderedBroadcast(intent, null)
button.setOnClickListener {
val intent = Intent("com.example.timingdemo.other.MY_BROADCAST")
intent.setPackage(packageName) //这一行必须加,否则为隐式广播无法被接收
sendOrderedBroadcast(intent, null)
}
如果想要阻断后面的广播,使用
abortBroadcast()
强制下线功能
与上面那个功能一样,只不过改为了kotlin版本
object ActivityCollector {
private val activities = ArrayList<Activity>()
fun addActivity(activity: Activity){
activities.add(activity)
}
fun removeActivity(activity: Activity){
activities.remove(activity)
}
fun finishAll(){
for (activity in activities) {
if (!activity.isFinishing){
activity.finish()
}
}
activities.clear()
}
}
open class BaseActivity : AppCompatActivity() {
lateinit var receiver: ForceOfflineReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityCollector.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
ActivityCollector.removeActivity(this)
}
override fun onResume() {
super.onResume()
val intentFilter = IntentFilter()
intentFilter.addAction("com.example.timingdemo.base.FOR_OFFCE_OFFLINE")
receiver = ForceOfflineReceiver()
registerReceiver(receiver, intentFilter)
}
override fun onPause() {
super.onPause()
unregisterReceiver(receiver)
}
inner class ForceOfflineReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
AlertDialog.Builder(context).apply {
setTitle("Warning")
setMessage("you are forced to be offline. please try to login again.")
setCancelable(false)
//如果lambda参数未使用,可以使用下划线来替代,只有一个参数时可以使用it来代替
setPositiveButton("OK") { _ , _ ->
ActivityCollector.finishAll()
val intent = Intent(context, LoginActivity::class.java)
context.startActivity(intent)
}
show()
}
}
}
}
class LoginActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
val accountEdit = findViewById<EditText>(R.id.accountEdit)
val passwordEdit = findViewById<EditText>(R.id.passwordEdit)
login.setOnClickListener {
val account = accountEdit.text.toString()
val password = passwordEdit.text.toString()
if (account == "admin" && password == "123") {
val intent = Intent(this, Main2Activity::class.java)
startActivity(intent)
finish()
} else {
Toast.makeText(this, "account or password is invalid", Toast.LENGTH_SHORT).show()
}
}
}
}
class Main2Activity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
forceOffLine.setOnClickListener {
val intent = Intent("com.example.timingdemo.base.FOR_OFFCE_OFFLINE")
sendBroadcast(intent)
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".activity.LoginActivity">
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="账户"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="密码"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="@+id/textView4"
app:layout_constraintStart_toStartOf="@+id/textView4"
app:layout_constraintTop_toBottomOf="@+id/textView4" />
<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="44dp"
android:text="登录"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView7" />
<EditText
android:id="@+id/accountEdit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="@+id/textView4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView4"
app:layout_constraintTop_toTopOf="@+id/textView4" />
<EditText
android:id="@+id/passwordEdit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="@+id/textView7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView7"
app:layout_constraintTop_toTopOf="@+id/textView7" />
</androidx.constraintlayout.widget.ConstraintLayout>
本文地址:https://blog.csdn.net/qq873044564/article/details/90347661
下一篇: C++读写文件