欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

安卓广播基本用法和实战:模拟强制下线

程序员文章站 2022-03-24 15:06:52
参考资料:郭霖老师第一行代码第二版第五章一.什么是广播在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。在生活中,我们的收音机的广播电台需要调到特定的频率才能接收到内容,在android中也是一样,通过sendBroadcast来发送广播并携带一个action,只有接收者的action和发送者action相同,才可以接收到这个广播.二.广播有什么用......

目录

一.什么是广播

二.广播有什么用

3.注册广播

4.其他

5.创建方法

四.发送自定义广播(即非系统广播)

 

六.实战:模拟强制下线

kotlin版本代码:

动态注册

静态注册:

自定义广播:

发送有序广播

强制下线功能


参考资料:郭霖老师第一行代码第二版第五章

一.什么是广播

在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

相关标签: Android Kotlin