Android入门第46天-使用BroadCast来模拟异地登录事件发生后的主动退出另一个设备重登录

news2024/10/7 10:24:29

简介

随着对BroadCast的越来越深入,我们今天要实现一个稍微复杂一点的BroadCast。即我们常用来有时APP打开时如果多个设备同时登录一个帐号,而我们只允许一个设备登录一个帐号时,此时我们的APP会弹一个对话框如:您的账号在别处登录,请重新登陆!。

设计

要制作这样的效果我们依旧是采用BroadCast,而且是一个自定义的Broadcast。此处需要:

  1. 自定义send一个broadcast;
  2. 注册一个receiver,使得它监听我们这个自定义的broadcast;
  3. 在receiver的onReceive事件中,弹出一个“无窗体悬浮alert dialog”;
  4. 由于Android6及以后的相应权限问题,你还要添加这个无窗体的悬浮alert dialog的权限;
  5. 又由于我们用的是SDK27及以后版本,因此光添加权限还没有用,还要使用代码唤出android关于这个app的一个“授权”系统窗口,在这个授权窗口内,用户自己点:allow后再进行打开这个app操作,此时这个悬浮alert dialog才能正确被唤起否则当这个alert dialog一旦被唤出你会得到一个permission denied 2038的错误,然后Android App自动退出;

好了,说了这么多我们来看代码

全代码

activity_main.xml

<?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:id="@+id/buttonLoginInOtherPlace"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="模拟异地登录" />
</LinearLayout>

 activity_login.xml

<?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="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="login_id:" />

        <EditText
            android:width="120dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="password:" />

        <EditText
            android:width="120dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/buttonLoginSubmit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="点一下代表登录了" />
    </LinearLayout>
</LinearLayout>

以上定义了两个窗体,运行顺序如下:

  1. activity_main先运行,上面显示一个“模拟异地登录”按钮,点一下会弹出一个alert dialog告诉你你现在要登出;
  2. 用户点一下这个alert dialog上的【确定】,登录出登录,跳转到一个登录的activity_login界面;
  3. 在这个activity_login界面直接点【登录】又登进activity_main

先来看我们的Receiver,它接受来自activity_main的【模拟异地登录】按钮发送过来的broad cast。

BroadCastReceiver.java

package org.mk.android.demo.broadcast;

import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;

public class BroadCastReceiver extends BroadcastReceiver {
    private final String TAG = "BroadCastWithActivity";

    @Override
    public void onReceive(final Context context, Intent intent) {

        Log.i(TAG, "receive broadcast->" + intent.getAction());
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
        dialogBuilder.setTitle("警告:");
        dialogBuilder.setMessage("您的账号在别处登录,请重新登陆!");
        dialogBuilder.setCancelable(false);
        dialogBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ActivityCollector.getInstance().finishAll();
                Intent intent = new Intent(context, LoginActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
            }
        });
        AlertDialog alertDialog = dialogBuilder.create();
        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
        alertDialog.show();
    }
}

接着,我们来看AndroidManifest.xml文件中的注册以及相应的静态权限申请(这个对话框除了静态权限还需要代码在弹出对话框前申请动态权限,这块代码我们写在了MainActivity.java里的)。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.DemoBroadCastWithActivity"
        tools:targetApi="31">
        <activity
            android:name=".LoginActivity"
            android:exported="false">
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
        <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>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
        <receiver android:name=".BroadCastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
            <intent-filter>
                <action android:name="ANDROID.INTENT.ACTION.MEDIA_MOUNTED"/>
                <action android:name="ANDROID.INTENT.ACTION.MEDIA_UNMOUNTED"/>
                <data android:scheme="file"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>

接着我们来看我们的BroadCastReceiver写法。

BroadCastReceiver.java

package org.mk.android.demo.broadcast;

import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;

public class BroadCastReceiver extends BroadcastReceiver {
    private final String TAG = "BroadCastWithActivity";
    private static final String BROADCAST_ACTON = "org.mk.android.demo.broadcast";
    @Override
    public void onReceive(final Context context, Intent intent) {
        if(intent.getAction().equals(BROADCAST_ACTON)) {
            Log.i(TAG, "receive broadcast->" + intent.getAction());
            AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
            dialogBuilder.setTitle("警告:");
            dialogBuilder.setMessage("您的账号在别处登录,请重新登陆!");
            dialogBuilder.setCancelable(false);
            dialogBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    ActivityCollector.getInstance().finishAll();
                    Intent intent = new Intent(context, LoginActivity.class);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    context.startActivity(intent);
                }
            });
            AlertDialog alertDialog = dialogBuilder.create();
            alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
            alertDialog.show();
        }
    }
}

我们可以看到,它里面定义了一个alert dialog,这个dialog在弹出时没有context因此我们把它叫作无activity(窗体)依托dialog,因此这种dialog是必须要申请权限的,这是Android的规定。然后这个alert dialog有一个【确定】按钮,点一下这个【确定】按钮,就会以startActivity的方式再次打开activity_main界面,此处我们需要注意的是,这个startActivity里的intent的类型必须为Intent.FLAG_ACTIVITY_NEW_TASK),否则你死活从这个登录界面跳不回activity_main的界面了。

接着看来MainActivity以及里面发生消息的部分(含代码动态申请Android权限)。

MainActivity.java

package org.mk.android.demo.broadcast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends BaseActivity {
    private BroadCastReceiver localReceiver;
    private LocalBroadcastManager localBroadcastManager;
    private IntentFilter intentFilter;
    private static final String BROADCAST_ACTON = "org.mk.android.demo.broadcast";
    private final String TAG = "BroadCastWithActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button buttonLoginInOtherPlace = (Button) findViewById(R.id.buttonLoginInOtherPlace);
        localBroadcastManager = LocalBroadcastManager.getInstance(this);

        //初始化广播接收者,设置过滤器
        localReceiver = new BroadCastReceiver();
        intentFilter = new IntentFilter();
        intentFilter.addAction(BROADCAST_ACTON);
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);
        buttonLoginInOtherPlace.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!Settings.canDrawOverlays(MainActivity.this)) {
                    Intent mintent = new Intent();
                    mintent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                    startActivity(mintent);
                }

                Log.i(TAG, ">>>>>>MainActivity->onClick");
                Intent intent = new Intent(BROADCAST_ACTON);
                localBroadcastManager.sendBroadcast(intent);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
}

这边核心注意点:

代码动代申请权限

if (!Settings.canDrawOverlays(MainActivity.this)) {
    Intent mintent = new Intent();
    mintent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    startActivity(mintent);
}

代码运行到这,Android会打开一个该APP相应的系统权限对话框

然后把这边的Allow手动启用。

接着我们重新运行这个APP,效果如下。

运行效果

点击【确定】登出activity_main切换到activity_login

在这个界面点击【点一下代表登录了】按钮,再次回到activity_main

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/86270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

结合面试详细分析 HashMap 源码

个人文档站点&#xff1a;小熊学Java 1、底层结构 相信大家都已经听过很多了&#xff0c;这里就不多阐述了&#xff0c;至于什么时候是数组&#xff0c;什么时候会变成链表&#xff0c;后续会讲解&#xff0c;别急&#xff01; JDK版本数据结构JDK1.7数组链表JDK1.8数组 &…

腾讯云数据万象:智能+存储双驱动,数倍提升内容生产效能

伴随数字经济市场稳步扩张&#xff0c; AI和富媒体融合的场景式体验、营销模式、分享渠道已经成为数字商业中不可或缺的部分。12月1日&#xff0c;在2022腾讯全球数字生态大会存储专场&#xff0c;腾讯云数据万象发布产品更新&#xff0c;与腾讯云对象存储COS共同打造智能存储生…

[附源码]Nodejs计算机毕业设计基于JAVA语言的国货美妆店管理系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

JavaScript-DOM操作表单

目录 表单事件 表单方法 操作表单 获取表单的值 表单内容html checkbox的获取办法 表单事件 注意&#xff1a;onsubmit,onreset只能给表单添加 表单.submit ;表单.onreset; form.onsubmitfunction(){alert(表单即将被提交);};form.onresetfunction(){alert(表单即将被重…

ChatGPT:竟然精通ENVI IDL、ArcGIS等软件!

目录 01 使用途径 02 使用 01 使用途径 我试了很多网站&#xff0c;包括注册登录、插件、镜像网站&#xff0c;微信机器人&#xff0c;QQ机器人&#xff0c;但是目前这些或多或少都由于OpenAI的限制无法正常使用。所以总的来说需要科学上网并且需要国外手机号&#xff0c;这有…

CCIE-重认证-300-410-补充题库-必须的哟

实验题 VRF router bgp 65000 bgp router-id x bgp log-neighbor-cha address-fa ipv4 vrf green red con neigh x remote-as 65000 neigh x act 重复red inter e0/0 ip vrf for red ip add x x 重复e0/1,for green inter e0/2.100 enc dot 100 ip vrf for red ip add x x …

无工具情况下linux数据库命(postgresql)令行建表操作文档

首先将帮助文档下的test.sql放在服务器的某个位置。sql文件如下图&#xff1a; /*Navicat Premium Data TransferSource Server : postgres-123Source Server Type : PostgreSQLSource Server Version : 100010Source Host : 192.168.3.123:5432Source C…

完全开源的代码生成器之code-generator

什么是code-generator code-generator是一个低代码平台, 可以解决90%单表增删改查工作量, 你可以通过自定义代码模板来生成适合自己的代码。 目前该系统主要针对的是语言是java, 数据源是mysql(其他数据库待测试)&#xff0c;数据源支持mysql, oracle, dm, PostgreSql等数据库…

[附源码]计算机毕业设计的在线作业批改系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis MavenVue等等组成&#xff0c;B/S模式…

Revit 中注释族的应用详解及公共族库工具

一、Revit 中注释族的应用详解&#xff1a; 注释族是用来表示二维注释的族文件&#xff0c;它被广泛用于很多构件的二维视图表现。下面以一个实例来说明注释族的应用 1、注释族创建实例 用“公制常规注释.rft”族样板创建一个注释族&#xff0c;在“族类别和族参数”对话框中选…

视觉SLAM ch9

状态估计的概率解释&#xff1a;位姿x和路标y服从某种概率分布&#xff0c;目的是通过某些运动数据u&#xff08;比如惯性测量传感器IMU输入&#xff09;和观测数据z&#xff08;比如拍摄到的照片像素点的值&#xff09;来确定状态量x和y的分布。 一、关于卡尔曼滤波器和扩展卡…

ssm+Vue计算机毕业设计校园食堂订餐系统(程序+LW文档)

ssmVue计算机毕业设计校园食堂订餐系统&#xff08;程序LW文档&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技…

Vim解决问题的方式

目录1 认识 . 命令2 不要自我重复3 以退为进4 执行、重复、回退5 查找并手动替换1 认识 . 命令 . 命令让我们重复上次的修改&#xff0c;它是Vim中最为强大的多面手。我们来练习下&#xff0c;如何快速的在vim进行操作&#xff0c;由下图形&#xff1a; 转成如下图形&#x…

win10下CH340模块下载stc89c52程序

没想到读研究生了还有水课需要用上51单片机&#xff0c;本科的时候一直是用开发板烧录程序的&#xff0c;这次舍不得花钱买开发板只能瞎折腾了。 准备材料 1.ch340转接板&#xff0c;最普通的那种3~5块钱 2.买的是一个焊接好的小单片机系统 &#xff08;BB一句&#xff0c;这…

内皮细胞生长添加剂(ECGF/ECGS)丨艾美捷解决方案

内皮细胞生长添加剂&#xff08;ECGF/ECGS&#xff09;是一种内皮细胞体外培养不可缺少的补充物质&#xff0c;可以优化细胞的体外生长环境&#xff0c;促进内皮细胞的正常增殖和生长。ECGF/ECGS是一种无菌浓缩&#xff08;100X&#xff09;溶液&#xff0c;含有正常人内皮细胞…

仅5天注册用户超百万,爆火ChatGPT究竟是什么?

作者&#xff1a;qizailiu&#xff0c;腾讯 IEG 应用研究员&#xff0c;来自腾讯技术工程 OpenAI 近期发布聊天机器人模型 ChatGPT&#xff0c;迅速出圈全网。它以对话方式进行交互。以更贴近人的对话方式与使用者互动&#xff0c;可以回答问题、承认错误、挑战不正确的前提、拒…

大数据集群HIVESQL经常执行失败问题调查

一、背景 BI集群&#xff0c;有60多个节点&#xff0c;2P数据&#xff0c;机器都已经运行了3年以上 二、现象 提交hive任务会经常失败&#xff0c;有时候能成功&#xff0c;上午失败概率大&#xff0c;下午成功的概率大。 异常日志&#xff1a; 日志1、 2021-09-30 08:28:35.…

攻防世界 easyphp

Easyphp 对于初学者其实一点也不easy哈 打开题目场景 <?php highlight_file(__FILE__); $key1 0; $key2 0;$a $_GET[a]; $b $_GET[b];if(isset($a) && intval($a) > 6000000 && strlen($a) < 3){if(isset($b) && 8b184b substr(md5($…

xxljob-core包handler文件夹里面的文件,MethodJobHandler类,让我们自己项目使用,就是将要执行的方法放到人家对象里面

目录 1 handler文件夹2 总结1 handler文件夹 以上是目录结构。 IJobHandler 是一个抽象类,具体的实现类就是impl里面的文件 那这个抽象类里面有什么方法 /*** job handler 任务处理触发器* 就是 要执行哪些代码* 抽象类* @author jing*/ public abstract class IJobHand…

Postgresql源码(93)Postgresql函数内事务控制实现原理(附带Oracle对比)

相关 《Postgresql源码&#xff08;60&#xff09;事务系统总结》 《Postgresql源码&#xff08;93&#xff09;Postgresql函数内事务控制实现原理&#xff08;附带Oracle对比&#xff09;》 0 总结 Postgresql与Oracle都是扁平化处理函数内外的事务控制语句的&#xff1a;即函…