精华内容
下载资源
问答
  • 模拟用户账号异常状况,使用广播,弹出dialog通知用户账号出现异常并重新登录; 定义一个工具类,当用户账号异常时使用工具类发送广播弹出对话框提示用户;
  • android强制下线

    千次阅读 2015-05-25 13:57:04
    强制下线功能应该算是比较常见的了,很多的应用程序都具备这个功能,比如你的QQ 号在别处登录了,就会将你强制挤下线。其实实现强制下线功能的思路也比较简单,只需要 在界面上弹出一个对话框,让用户无法进行任何...


               强制下线功能应该算是比较常见的了,很多的应用程序都具备这个功能,比如你的QQ

    号在别处登录了,就会将你强制挤下线。其实实现强制下线功能的思路也比较简单,只需要

    在界面上弹出一个对话框,让用户无法进行任何其他操作,必须要点击对话框中的确定按钮,

    然后回到登录界面即可。可是这样就存在着一个问题,因为我们被通知需要强制下线时可能

    正处于任何一个界面,难道需要在每个界面上都编写一个弹出对话框的逻辑?如果你真的这

    么想,那思维就偏远了,我们完全可以借助本章中所学的广播知识,来非常轻松地实现这一

    功能。新建一个BroadcastBestPractice 项目,然后开始动手吧。

    强制下线功能需要先关闭掉所有的活动,然后回到登录界面。如果你的反应足够快的话,


    先创建一个ActivityCollector 类用于管理所有的活动,代码如

    下所示:

    public class ActivityCollector{

    public staticList<Activity> activities = new ArrayList<Activity>();

    public static voidaddActivity(Activity activity) {

    activities.add(activity);

    }

    public static voidremoveActivity(Activity activity) {

    activities.remove(activity);

    }

    public static void finishAll(){

    for (Activity activity :activities) {

    if (!activity.isFinishing()) {

    activity.finish();

    }

    }

    }

    然后创建BaseActivity类作为所有活动的父类,代码如下所示:

    public class BaseActivityextends Activity {

    @Override

    protected void onCreate(BundlesavedInstanceState) {

    super.onCreate(savedInstanceState);

    ActivityCollector.addActivity(this);

    }

    @Override

    protected void onDestroy() {

    super.onDestroy();

    ActivityCollector.removeActivity(this);

    }

    }

    接着需要创建一个登录界面的布局,还记得我们在3.3.4 节里编写的登录界面吗?这里

    也是直接拿来用就好了,这下可省了我们不少的功夫。新建布局文件login.xml,代码如下

    所示:

    <TableLayoutxmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:stretchColumns="1">

    第一行代码——Android

    208

    <TableRow>

    <TextView

    android:layout_height="wrap_content"

    android:text="Account:"/>

    <EditText

    android:id="@+id/account"

    android:layout_height="wrap_content"

    android:hint="Input youraccount" />

    </TableRow>

    <TableRow>

    <TextView

    android:layout_height="wrap_content"

    android:text="Password:"/>

    <EditText

    android:id="@+id/password"

    android:layout_height="wrap_content"

    android:inputType="textPassword"/>

    </TableRow>

    <TableRow>

    <Button

    android:id="@+id/login"

    android:layout_height="wrap_content"

    android:layout_span="2"

    android:text="Login"/>

    </TableRow>

    </TableLayout>

    以上代码都是直接复用之前写好的内容,非常开心。不过从这里开始,我们又需要靠自

    己去动手实现了。现在登录界面的布局已经完成,那么接下来就应该去编写登录界面的活动

    了,新建LoginActivity继承自BaseActivity,代码如下所示:


    209

    public class LoginActivityextends BaseActivity {

    private EditText accountEdit;

    private EditText passwordEdit;

    private Button login;

    @Override

    protected void onCreate(BundlesavedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.login);

    accountEdit = (EditText)findViewById(R.id.account);

    passwordEdit = (EditText)findViewById(R.id.password);

    login = (Button)findViewById(R.id.login);

    login.setOnClickListener(newOnClickListener() {

    @Override

    public void onClick(View v) {

    String account =accountEdit.getText().toString();

    String password =passwordEdit.getText().toString();

    // 如果账号是admin且密码是123456,就认为登录成功

    if(account.equals("admin") &&password.equals("123456")) {

    Intent intent = newIntent(LoginActivity.this,

    MainActivity.class);

    startActivity(intent);

    finish();

    } else {

    Toast.makeText(LoginActivity.this,"account or password

    is invalid",

    Toast.LENGTH_SHORT).show();

    }

    }

    });

    }

    }

    可以看到,这里我们模拟了一个非常简单的登录功能。首先使用setContentView()方法

    将login布局加载进来,并调用findViewById()方法分别获取到账号输入框、密码输入框以及

    第一行代码——Android

    210

    登录按钮的实例。然后在登录按钮的点击事件里面对输入的账号和密码进行判断,如果账号

    是admin并且密码是123456,就认为登录成功并跳转到MainActivity,否则就提示用户账号

    或密码错误。

    因此,你就可以将MainActivity 理解成是登录成功后进入的程序主界面了,这里我们并

    不需要在主界面里提供什么花哨的功能,只需要加入强制下线功能就可以了,修改

    activity_main.xml 中的代码,如下所示:

    <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    <Button

    android:id="@+id/force_offline"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:text="Send forceoffline broadcast" />

    </LinearLayout>

    非常简单,只有一个按钮而已。然后修改MainActivity 中的代码,如下所示:

    public class MainActivityextends BaseActivity {

    @Override

    protected void onCreate(BundlesavedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    Button forceOffline = (Button)findViewById(R.id.force_offline);

    forceOffline.setOnClickListener(newOnClickListener() {

    @Override

    public void onClick(View v) {

    Intent intent = newIntent("com.example.broadcastbestpractice.

    FORCE_OFFLINE ");

    sendBroadcast(intent);

    }

    });

    }

    }

    同样非常简单,不过这里有个重点,我们在按钮的点击事件里面发送了一条广播,广播

    的值为com.example.broadcastbestpractice.FORCE_OFFLINE,这条广播就是用于通知程序强

    制用户下线的。也就是说强制用户下线的逻辑并不是写在MainActivity 里的,而是应该写在

    接收这条广播的广播接收器里面,这样强制下线的功能就不会依附于任何的界面,不管是在

    程序的任何地方,只需要发出这样一条广播,就可以完成强制下线的操作了。

    那么毫无疑问,接下来我们就需要创建一个广播接收器了,新建ForceOfflineReceiver

    继承自BroadcastReceiver,代码如下所示:

    public class ForceOfflineReceiverextends BroadcastReceiver {

    @Override

    public void onReceive(finalContext context, Intent intent) {

    AlertDialog.BuilderdialogBuilder = new AlertDialog.Builder(context);

    dialogBuilder.setTitle("Warning");

    dialogBuilder.setMessage("Youare forced to be offline. Please try

    to login again.");

    dialogBuilder.setCancelable(false);

    dialogBuilder.setPositiveButton("OK",

    newDialogInterface.OnClickListener() {

    @Override

    public voidonClick(DialogInterface dialog, int which) {

    ActivityCollector.finishAll();// 销毁所有活动

    Intent intent = newIntent(context,

    LoginActivity.class);

    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    context.startActivity(intent);// 重新启动LoginActivity

    }

    });

    AlertDialog alertDialog =dialogBuilder.create();

    // 需要设置AlertDialog的类型,保证在广播接收器中可以正常弹出

    alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTE

    M_ALERT);

    alertDialog.show();

    }

    }

    这次onReceive()方法里可不再是仅仅弹出一个Toast 了,而是加入了较多的代码,那我

    们就来仔细地看一下吧。首先肯定是使用AlertDialog.Builder 来构建一个对话框,注意这里

    一定要调用setCancelable()方法将对话框设为不可取消,否则用户按一下Back 键就可以关闭

    第一行代码——Android

    212

    对话框继续使用程序了。然后使用setPositiveButton()方法来给对话框注册确定按钮,当用户

    点击了确定按钮时,就调用ActivityCollector 的finishAll()方法来销毁掉所有活动,并重新启

    动LoginActivity这个活动。另外,由于我们是在广播接收器里启动活动的,因此一定要给

    Intent 加入FLAG_ACTIVITY_NEW_TASK 这个标志。最后,还需要把对话框的类型设为

    TYPE_SYSTEM_ALERT,不然它将无法在广播接收器里弹出。

    这样的话,所有强制下线的逻辑就已经完成了,接下来我们还需要对AndroidManifest.xml

    文件进行配置,代码如下所示:

    <manifestxmlns:android="http://schemas.android.com/apk/res/android"

    package="com.example.broadcastbestpractice"

    android:versionCode="1"

    android:versionName="1.0">

    <uses-sdk

    android:minSdkVersion="14"

    android:targetSdkVersion="19"/>

    <uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW" />

    <application

    android:allowBackup="true"

    android:icon="@drawable/ic_launcher"

    android:label="@string/app_name"

    android:theme="@style/AppTheme">

    <activity

    android:name=".LoginActivity"

    android:label="@string/app_name">

    <intent-filter>

    <actionandroid:name="android.intent.action.MAIN" />

    <categoryandroid:name="android.intent.category.LAUNCHER" />

    </intent-filter>

    </activity>

    <activityandroid:name=".MainActivity" >

    </activity>

    <receiverandroid:name=".ForceOfflineReceiver" >

    <intent-filter>

    <actionandroid:name="com.example.broadcastbestpractice.

    FORCE_OFFLINE" />

    </intent-filter>

    </receiver>

    第2章先从看得到的入手,探究活动

    213

    </application>

    </manifest>

    这里有几点内容需要注意,首先由于我们在ForceOfflineReceiver 里弹出了一个系统级别

    的对话框,因此必须要声明android.permission.SYSTEM_ALERT_WINDOW 权限。然后对

    LoginActivity 进行注册,并把它设置为主活动,因为肯定不能让用户启动程序就直接进入

    MainActivity 吧。最后再对ForceOfflineReceiver 进行注册,并指定它接收com.example.

    broadcastbestpractice.FORCE_OFFLINE这条广播。

    这时用户将无法再对界面的任何元素进行操作,只能点击确定按钮,然后会重新回到登

    录界面。这样,强制下线功能就已经完整地实现了。

    结束了本章的最佳实践部分,接下来我们要进入一个特殊的环节。相信你一定也知道,

    几乎所有出色的项目都不会是由一个人单枪匹马完成的,而是由一个团队共同合作开发完成

    的。这个时候多人之间代码同步的问题就显得异常重要,因此版本控制工具也是应运而生。

    展开全文
  • } } else { return RestResultUtil.failed("用户密码异常,请重新输入!!!"); } } 3.拦截器(2个类) package com.datago.robot.common.interceptor; import com.datago.robot.common.utils.RestResult; import ...

    1.springbootWeb项目

    • 引包
            <!--redis-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
    • .yml文件配置redis信息
    #redis配置信息
    spring:
      redis:
        host: 127.0.0.1
        port: 6379
        password: 123456
        jedis:
          pool:
            max-active: 8
            max-idle: 500
            max-wait: -1
            min-idle: 0
        lettuce:
          shutdown-timeout: 0
    

    2.项目登录接口

    
     @Autowired
     private RedisTemplate<String,String> redisTemplate;
    
        @ApiOperation(value = "登录")
        @PostMapping(value = "/login")
        public RestResult<DatagoUser> login(@RequestBody DatagoUser datagoUser, HttpServletRequest request) {
    
            String kl = MD5andKL.KL(datagoUser.getPassword());
            datagoUser.setPassword(kl);
            datagoUser = datagoUserService.login(datagoUser);
            if (datagoUser != null) {
                if (datagoUser.getStatus().equals(true)) {
                    DatagoRole datagoRole = roleService.selectByRole(datagoUser.getRoleId());
                    datagoUser.setType(datagoRole.getType());
                    datagoUser.setLastLoginTime(new Date());
                    datagoUser.setLastLoginIp(UserContext.getIp2());
                    datagoUser.setSessionId(request.getRequestedSessionId());
                    datagoUserService.updateByPrimaryKeySelective(datagoUser);
    //                UserContext.setUser(datagoUser);
    
    				//主要核心代码
                    request.getSession().setAttribute("user",datagoUser.getId());
                    redisTemplate.opsForValue().set(datagoUser.getId(),request.getSession().getId());
    
                    return RestResultUtil.genSuccessResult(datagoUser);
                } else {
                    return RestResultUtil.failed("用户被禁用,请联系管理员!!!");
                }
            } else {
                return RestResultUtil.failed("用户密码异常,请重新输入!!!");
            }
        }
    

    3.拦截器(2个类)

    package com.datago.robot.common.interceptor;
    
    import com.datago.robot.common.utils.RestResult;
    import com.datago.robot.common.utils.Utils;
    import lombok.extern.slf4j.Slf4j;
    import net.sf.json.JSONObject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.PrintWriter;
    
    /**
     * @author HB
     */
    @Component
    @Slf4j
    public class LoginHandlerInterceptor implements HandlerInterceptor {
    
        @Autowired
        private StringRedisTemplate redisTemplate;
    
        /**
         * 1.在调用控制器方法前,拦截
         * <p>
         * 返回值为false,代表拦截
         * 返回值为true,代表放行
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            log.info("---------------------开始进入请求地址拦截----------------------------");
            //注释掉为了swagger-ui接口测试可以正常使用
    //        DatagoUser user = (DatagoUser) UserContext.getSession().getAttribute("user");
            String user = (String) request.getSession().getAttribute("user");
            RestResult restResult = new RestResult();
            if (Utils.isNotEmpty(user)) {
                //检测redis中是否含有sessionId
                String loginSessionId = redisTemplate.opsForValue().get(request.getSession().getAttribute("user"));
                if (loginSessionId != null && loginSessionId.equals(request.getSession().getId())) {
                    return true;
                } else {
                    restResult.setMessage("该账号在另一台设备上登录,您被强制下线,请您重新登陆!!!");
                }
            } else {
                restResult.setMessage("请您先进行登录");
            }
            restResult.setCode(-2);
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json; charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(JSONObject.fromObject(restResult).toString());
            return false;
        }
    
        /**
         * 2.在调用控制器方法后,拦截(在生成视图之前)
         */
        @Override
        public void postHandle(HttpServletRequest request,
                               HttpServletResponse response, Object handler,
                               ModelAndView modelAndView) throws Exception {
            log.info("=============进入拦截器了,请求处理后,渲染ModelAndView前调用。=================================");
            // TODO Auto-generated method stub
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
    
        /**
         * 3.在视图生成之后(后台所有所有逻辑都完成后)
         */
        @Override
        public void afterCompletion(HttpServletRequest request,
                                    HttpServletResponse response, Object handler, Exception ex) throws Exception {
            log.info("=============进入拦截器了,渲染ModelAndView后调用。=================================");
            // TODO Auto-generated method stub
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    
    
    }
    
    
    package com.datago.robot.common.interceptor;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    
    /**
     * @ProjectName powerFind
     * @Package com.datago.robot.common.config
     * @Name WebConfigurer
     * @Author HB
     * @Date 2021/2/18 10:20
     * @Version 1.0
     */
    @Configuration
    public class WebConfigurer implements WebMvcConfigurer {
    
        @Autowired
        LoginHandlerInterceptor loginInterceptor;
    
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // TODO Auto-generated method stub
            // addPathPatterns("/**") 表示拦截所有的请求,
            // excludePathPatterns("/login", "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
            registry.addInterceptor(loginInterceptor)
                    .addPathPatterns("/**")
                    .excludePathPatterns("/",
                            //登录接口
                            "/datagoUser/login",
                            //导出报名人员excel文件接口
                            "/file/**",
                            //展示活动照片接口
                            "/activityImage/**",
    
                            "/activity/getIsActivityCount",
                            "/activity/getActivityCount",
                            "/image/imageFileSave",
                            "/activity/importShowTemplate",
                            "/activity/templatePersonDownload",
                            "/honor/importShowTemplate",
                            "/honor/templatePersonDownload"
                    );
        }
    
    }
    

    4.写的有点。。。,欢迎留言一同探讨学习。
    5.要想拿来自己用必须要修改业务逻辑代码,方可使用;楼主比较懒,就直接拷贝来了,不喜么怪。。。。
    6.最后再共享下,redis资源(也可在博主资源下载中进行下载)

    • 博主CSDN资源
    https://download.csdn.net/download/sinat_37239798/15542982
    
    • 博主百度网盘资源
    链接:https://pan.baidu.com/s/1Uh6fsriyGpb8xDwx94ew7g 
    提取码:1234 
    复制这段内容后打开百度网盘手机App,操作更方便哦
    
    展开全文
  • 都已经2020年了,smart qq早就于2019年光荣下线,伴随着他的离开,qqbot也不再维护,以往的基于smart qq和qqbot的QQ机器人的设计模式也已经不复存在,今天,笔者将带来一种全新的Python QQ机器人设计方式,基于...

    都已经2020年了,smart qq早就于2019年光荣下线,伴随着他的离开,qqbot也不再维护,以往的基于smart qq和qqbot的QQ机器人的设计模式也已经不复存在,今天,笔者将带来一种全新的Python QQ机器人设计方式,基于python nonebot库和cool Q打造QQ机器人。

    组件介绍

    cool q为何物?

    酷Q官网

    酷Q是一个“简洁,轻盈,强大,免费”的qq机器人应用程序,甚至可以在群晖上搭建这样的一套系统,非常实用。官方将酷Q分为两种不同的版本,一个是酷Q Air,另一个是酷Q Pro。这种命名习惯与某果十分相似~

    对于两种不同版本的区别,官方给出了下图的解释

    47e5cc8d8fd4

    两种版本在下载时都是免费的,但是实际使用时pro版本是需要支付一定的费用的,笔者在这里就用Air版本进行QQ机器人的制作

    nonebot库为何物?

    nonebot库是一个基于酷Q的 Python 异步QQ机器人框架 ,官方给出了以下三种特性:

    1.简洁——提供极其简洁易懂的 API,使你可以毫无压力地开始验证你的绝佳创意,只需编写最少量的代码,即可实现丰富的功能。

    2.易于扩展——精心设计的消息处理流程及强大的 API 使得你可以很方便地将最简单的原型变为具有大量实用功能的完整聊天机器人,并持续保证扩展性。

    3.高性能——基于时下流行的 asyncio 模块,利用 WebSocket 进行通信,以获得极高的性能;同时,支持使用多个机器人账号来负载均衡用户消息,减少业务宕机的可能。

    开始准备

    接下来就正式开始搭建!

    47e5cc8d8fd4

    笔者使用的是酷Q Air小i版,不过既然我们选择使用Python来进行机器人的开发,所以具体选择图灵版还是小i版就显得不那么重要了。

    为酷Q安装HTTP API插件

    47e5cc8d8fd4

    进入GitHub中之后点击第一项下载即可。下载完成之后,我们需要打开酷Q的文件夹,在\酷Q Air\app目录下将我们下载的HTTP API插件粘贴进去

    47e5cc8d8fd4

    之后,我们运行酷Q应用程序CQA.exe,并且输入qq号和密码登录,之后在任务栏中找到酷Q的图标并用鼠标右键点击,找到【应用】-【应用管理】并进入配置界面。

    47e5cc8d8fd4

    找到HTTP API插件,并且点击右下侧的【启用】按钮。由于笔者已经启用了,因此按钮变成了停用。

    47e5cc8d8fd4

    配置酷Q HTTP API插件

    完成上述步骤之后,我们在酷Q文件夹中的

    \酷Q Air\data\app\io.github.richardchien.coolqhttpapi\config目录中找到<刚刚登录的QQ号>.json文件,并且用记事本或者其他文本编辑器打开编辑。按照图中的标注修改配置文件后保存退出。

    47e5cc8d8fd4

    至此为止,关于酷Q的准备已经完成。

    安装python库

    接下来使用pip安装nonebot库

    pip install nonebot

    安装完成后,我们的所有工具准备就已经完成。

    运行Python demo程序

    我们创建一个python文件,并且运行官方的demo代码

    import nonebot

    if __name__ == "__main__":

    nonebot.init()

    nonebot.load_builtin_plugins()

    nonebot.run(host='127.0.0.1', port=8080)

    运行程序,如果在控制台中出现下面的内容,则表明配置成功,python正在监听qq消息

    47e5cc8d8fd4

    此时我们便可以测试运行效果,向刚刚我们登录酷Q的qq号发送一条消息

    /echo

    47e5cc8d8fd4

    这样我们就可以看到机器人重复我们和它说的话了。

    异常处理

    有时候,我们本地的127.0.0.1 8080端口可能会被占用导致运行python程序的时候产生如下错误:

    OSError [WinError 10013]以一种访问权限不允许的方式做了一个访问套接字的尝试

    这时候我们使用管理员模式运行一个cmd窗口,输入

    netstat -ano|findstr 8080

    47e5cc8d8fd4

    这时候可以看到有一个进程号为 18292 的进程占用了我们的8080端口,我们需要将这个进程kill掉,在cmd窗口中继续输入

    taskkill /pid <进程号> /F

    如本例子中我们需要kill 18292进程,所以输入taskkill /pid 18292 /F

    这样重新运行python程序就不会出现刚才的异常了。

    结束语

    以上就是打造qq机器人的基本步骤,本教程持续更新~

    展开全文
  • 若账号出现异常登录,通知用户下线退出到登陆界面 提示:在界面上弹出一个对话框,让用户无法进行任何其他操作,必须点击对话框中的确定按钮,然后返回到登录界面。 技术要点: 1)不用每个页面都编写一个弹出...

    项目场景:

    若账号出现异常登录,通知用户下线退出到登陆界面

    提示:在界面上弹出一个对话框,让用户无法进行任何其他操作,必须点击对话框中的确定按钮,然后返回到登录界面。


    技术要点:

    1)不用每个页面都编写一个弹出对话框逻辑,借助广播机制和BaseActivity来实现这一功能
     2)强制下线功能需要关闭所有的活动,返回登陆界面,所以需要一个ActivityColleter来管理所有的活

     

    业务分析:

    1、登录QQ:
            通过LoginBtn按钮的点击事件:
                判断账号密码是否正确?
                    - intent跳转页面
        2、实现强制下线:
            - 强制下线,需要关掉所有的Activity,然后重新启动LoginActivity
            1)新建ActivityColleter类去管理所有的活动
            2)需要一个BaseActivity继承AppcampatActivity作为所有Activity的父类
            3)自定义通知:‘强制下线通知’,适当的时候通过Intent发送通知
            4)BaseActivity类中新建内部类继承BroadcastReceiver,重写onReceiver方法


    解决方案:

    1、ActivityColleter.java

    //to controll all of activities in this project
    public class ActivityColleter {
        //ActivityList
        private static List<Activity> activityList = new ArrayList<>();
        //addActivity
        public static void addActivity(Activity activity){
            activityList.add(activity);
        }
        //removeActivity
        public static void removeActivity(Activity activity){
            activityList.remove(activity);
        }
        //finishAllActivity
        public static void finishAllActivity(){
            for(Activity activity:activityList){
                if(!activity.isFinishing()){
                    activity.finish();
                }
            }
        }
    }

    2、BaseActivity

    //BaseActivity is the ancestor of all Activity
    //in other words,it is the father of all of them
    public class BaseActivity extends AppCompatActivity {
        //注册ForceOfflineReceiver receiver
        private ForceoffLineReceiver receiver;
    
        //Override OnCreate() function of Activity
        //when one activity is about to create ,baseActivity will controll it to be added into ActivityList
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityColleter.addActivity(this);
            Log.i("Tsss","BaseActivity.onCreate() is executed");
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            ActivityColleter.removeActivity(this);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            //当一个活动从新获取到栈顶位置时,会自动注册广播接收器
            IntentFilter intentFilter  = new IntentFilter();
            intentFilter.addAction("com.example.broadcastbestpractice.FORCE_OFFLINE");
            receiver=new ForceoffLineReceiver();
            registerReceiver(receiver,intentFilter);
            Log.i("Tsss","BaseActivity.onResume() is executed,register Receiver");
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            //当一个活动失去栈顶位置时,会自动取消广播接收器的注册
            if(receiver!=null){
                unregisterReceiver(receiver);
                Log.i("Tsss","BaseActivity.onPause() is executed,Unregister Receiver");
            }
        }
    
        //自定义内部类
        public class ForceoffLineReceiver extends BroadcastReceiver {
            @Override
            public void onReceive(final Context context, Intent intent) {
                //接收到广播后,进行的逻辑处理
                //1、构建一个对话框,并设置头,消息,是否可取消,确定按钮以及按钮单击事件
                AlertDialog.Builder builder=new AlertDialog.Builder(context);
                builder.setTitle("Warning");
                builder.setMessage("you are forced to be offline,Please try again");
                builder.setCancelable(false);
                builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //销毁所有的活动
                        ActivityColleter.finishAllActivity();
                        //重新启动LoginActivity
                        Intent intent = new Intent(context,LoginActivity.class);
                        context.startActivity(intent);
                    }
                });
                builder.show();
            }
        }
    
    }

    3、欢迎页面:SecondActivity

    public class SecondActivity extends BaseActivity implements View.OnClickListener {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.second_layout);
            //按钮单击事件
            Button force_offline_btn = findViewById(R.id.force_offline);
            force_offline_btn.setOnClickListener(this);
    
            Log.i("Tsss","SecondActivity.onCreate() is executed");
            //get intent
            Intent intent = getIntent();
            //get data
            String user = intent.getExtras().getString("user");
            //show data
            TextView textView = findViewById(R.id.user);
            textView.setText(textView.getText()+user);
        }
    
    //按钮触发强制下线广播
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.force_offline:
                    //发送force offline broadcast[这条广播不是系统自带的,而是自定义的]
                    Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE");
                    sendBroadcast(intent);
                    break;
            }
        }
    }

    参考书籍:https://www.baidu.com/link?url=QZZGyEIlKxj4NjHFLiAMvnrKGvDMYU_KWNUDvX6HjxSven6rVugny1tB81FzcEhPg5XbsVlsJYlGk4oCymlVdgW-9jVfPqgoCa0qhThRLfAZdaPOi-XOZZOt0vMdZ7NCPiuCj9YskVtB23T5K7HhrdvAvsz-i9eKuhOaeqe9Lklrg5CCPOplwcaxEZ_XK7Bo&wd=&eqid=a40cfd5f00024864000000025f73e3b0

    展开全文
  • qq手机令牌

    2013-02-17 22:57:22
    1.安全通知:帐号消费、异常,一切尽在掌握! 2.QQ登录查询:最近7天登录足迹,一查便知。 3.帐号锁:开关自如,全面掌控QQ登录。每次锁定最长15天,到期后自动解锁。 4. 游戏锁:下线锁号,装备无忧。每次锁定最长7...
  • QQ IM 通讯软件开发实战

    万次阅读 2018-07-03 02:45:09
    用习惯了微信的你,还记得当初的 QQ 吗?曾几何时,你是否也在梦想自己也能写出一个像 QQ 一样牛气的即时通讯软件?即使你不曾有过这个“野心”,你肯定也对 QQ 的实现原理感到好奇过,对吧?本达人课即将带您一探 ...
  • 例如QQ、微信都是进程。注:用户每启动一个进程,操作系统就会为该进程分配一个独立的内存空间。 线程:一个程序至少有一个进程,一个进程至少有一个线程.是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更...
  • 王者荣耀健康系统异常限制怎么解决?很多的玩家对王者荣耀健康系统异常的限制还不知道如何解决?下面电玩之家的小编就来为大家详细的介绍一下王者荣耀健康系统异常限制的解决教程,感兴趣的玩家快来看看吧!王者荣耀...
  • 欧洲云计算巨头数据中心起火 350万家网站下线 https://mp.weixin.qq.com/s/RGHI-UWAndz_cdZmgU5y2g 当地时间10日,法国东部下莱茵省省政府发布公告,当天凌晨法国大型网络服务商OVH公司位于下莱茵省首府斯特拉斯堡...
  • 山寨QQ源代码

    热门讨论 2012-02-11 08:20:49
    资源分设为1,因为我自己只有3分资源分了,所以就不设为0了 这个山寨QQ代码2000行,基于现在可以使用...用户下线后,和服务器已建立的套接字发生IOException异常,程序代码块跳转,服务器就通知用户的在线好友有人下线了。
  • 问:用iPhoneQQ在线软件挂上QQ以后关闭软件,QQ会随之下线吗? 答:不会。用iPhoneQQ在线软件挂上QQ后关闭本软件,你的QQ还是会显示iPhone在线,至于会在线多久,这个不确定,据网友反映在线时长在7-60天都有。这个...
  • Egret QQ玩一玩适配【踩坑日记】

    千次阅读 2018-11-16 15:10:37
    此外,这里需要理清楚一个概念:QQ 玩一玩 和 QQ 玩吧 并非同一个东西,QQ 玩一玩也叫 QQ 轻游戏 或 厘米游戏 ,是基于 bricks 引擎实现的。   技术限制 玩一玩平台不支持基于DOM Document对象的HTML元素处理 ...
  • 点击上方蓝色字体,选择“标星公众号”优质文章,第一时间送达▊老赵推荐(戳下方标题)阿里大牛程序员的Java问题排查工具单我已经不用 try catch 处理异常了!太烦人了Sprin...
  • QQ架构讨论

    千次阅读 2009-11-05 10:50:00
    http://groups.google.com/group/dev4server/browse_thread/thread/0d72668d11c4886b/a6d202489cabf285#a6d202489cabf285hi,...QQ现在的最高在线用户数是1900万,我们来讨论一下要作一个这样的架构如何来作更好,大家积
  • QQ红包技术方案全解密

    千次阅读 2017-03-24 16:44:26
    QQ红包技术方案全解密
  • 问: 为什么我的QQ号码会受到登录保护而需要激活? 答: 当腾讯公司的账号异常检测系统发现您的QQ号码可能存有异常情况时,您的QQ... 答: 当腾讯公司的账号异常检测系统发现您的QQ号码可能存有异常情况时,您的QQ
  • QQ2013协议分析(解密篇)

    千次阅读 2013-05-02 20:18:06
    版本信息:QQ2013beta2 测试QQ:2632949572 QQ密码两次md5所得密钥(MD5(MD5(QQ密码)+0x00000000+QQ号码)):47 D5 0A 0E 5E AD D1 BC F3 6E C3 48 B8 BD B6 00 下面,我们便来一起解析QQ登录得到会话密钥...
  • 2008-12-23 回答QQ三国版本再次更新,本次更新的版本里为广大玩家带来了一系列节日活动!圣诞、元旦、春节……一个都不能少!圣诞老人、财神爷降临三国,“馅儿饼”砸向三国……有趣的节日活动让你游戏乐翻天,炫丽...
  • SpringBoot集成单点登录-“被挤下线

    千次阅读 2018-12-18 15:20:19
    //后期在解释,此处处理单点登录踢出提示(类似,QQ你有新设备登录,您已经处于离线状态) sendLogOutMessage(userId); }catch (Exception e){ e.printStackTrace(); } return new UserTokenState(userId,jws,...
  • JavaSE版QQ设计文档

    2013-02-05 09:41:02
    模仿QQ,实现简单的qq功能:注册、登陆、查找好友、聊天功能。服务端还需实现配置文件设置、用户管理、日志显示的功能。   三、实现技术   a) 使用多线程进行多个客户端和服务端的连接。   b) 使用文件...
  • String msg = getMessage("已下线", appName, serverId, "OUT_OF_SERVICE", timeStamp); sendEmail(title, msg); } } @EventListener public void listen(EurekaInstanceRegisteredEvent event) { //HH: ...
  • QQ开发资料

    万次阅读 2004-10-19 17:17:00
    这有isQ整理的部分qq协议http://lumaqq.linuxsir.org/doc/Protocol%20Document%20from%20isQ.rarwww.realoa.net Delphi 的方案,无源码,有在线调试www.anyq.net C++的方案,有源代码lumaQQhttp://lumaqq.linuxsir....
  • 毕业设计-类似qq即时通信软件

    千次阅读 2011-04-07 10:04:00
    毕业设计课题很多,最后选择了对我来说稍微有点难度,但有能实现的题目:类似qq即时通信软件。 既然是类似qq,就应该好好的去了解下qq的原理。因为之前写过有关通信方面的东西,而且天天用qq也不陌生了。...
  • 腾讯QQ短暂上线了帐号注销服务,但一段时间后又再次下线。根据腾讯的解释,这次只是灰度测试,待到功能优化后注销服务会再次上线。QQ能注销之所以引起关注,是因为长久以来,中国互联网一直存在一种挺独特的现象——...
  • 我们最熟悉的好友系统案例当属QQ,实际上QQ是一款即时通讯工具,凭着好友系统沉淀了海量的好友关系链,从而铸就了一个坚不可摧的商业帝国。好友系统的重要性可见一斑。 熟悉互联网产品的人都知道,当产品有了一定的...
  • 公司项目需求,因为项目是开账户卖钱的,为了避免有的用户开一个账户N个人用,所以要求A账户只能...效果类似是qq登录的效果,先来张实现后的图 具体实现: 分两步, 第一步使用 HttpSessionAttributeListene
  • 但是XX管理系统肯定不能满足,所以在了解了json的皮毛之后,就着手作了这个伪QQ 完整代码已上传到github上,地址:https://github.com/LeYunone/19.12.25。有需要的可以自取。 作为一个伪QQ,代码中我实现了他的...
  • java 网络编程五 (仿QQ聊天程序)

    千次阅读 多人点赞 2017-08-17 13:49:09
    学完了socket通讯后,在老师的要求下,写了一个仿qq的聊天程序: 在服务器端 用一个HashMap 维护所有用户相关的信息,从而能够保证和所有的用户进行通讯。 客户端的动作: (1)连接(登录):发送userName 服务器的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,644
精华内容 1,057
关键字:

qq异常下线