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

目录

一.什么是广播

二.广播有什么用

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>

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值