广播的巧妙利用——仿QQ实现强制下线功能
2017-04-17 15:10
951 查看
利用广播实现强制下线功能
强制下线功能大家应该都知道,很多程序都有此功能,比如QQ,微信,微博等。意思是你的账号在别处登录,就会强制你下线,如图所示其实实现强制下线功能的思路也比较简单,只需要在界面上弹出一个对话框,让用户无法进行任何其他操作,必须要点击对话框中的确定按钮,才能返回到登录界面,但是存在这样一个问题,我们被通知需要强制下线可能处在任何一个界面,那么我们需要在每个界面上都写一个弹出对话框的逻辑吗?实际并不是,要是那样我们真的写不起啊!
那么现在看一下我们的思路,强制下线功能首先需要关掉所有的Activity,然后回到登录界面,这里我们就需要用到Java的特点:封装。
首先我们需要新建一个类作为所有Activity的父类,这种类我们叫他基类(BaseActivity),在这个类里写一个内部类管理所有Activity,叫ActivityController
package com.example.czy.forceofflinedemo; import android.app.Activity; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.View; import java.util.ArrayList; import java.util.List; /** * Created by CZY on 2017/4/17. */ public abstract class BaseActivity extends AppCompatActivity { //定义一个内部类对象 private ActivityController activityController; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //绑定布局 setContentView(bindLayout()); //初始化组件 initView(); //初始化数据 initData(); //创建出管理所有Activity类的对象 activityController = new ActivityController(); //将所有的Activity添加进来 activityController.addActivity(this); } @Override protected void onDestroy() { //在这个生命周期中销毁所有的Activity activityController.removeActivity(this); super.onDestroy(); } /** * 绑定布局 * * @return */ protected abstract int bindLayout(); /** * 初始化组件 */ protected abstract void initView(); /** * 初始化数据 */ protected abstract void initData(); /** * 绑定组件 * * @param resId 组件id * @param <T> 强制转换类型 * @return */ protected <T extends View> T bindView(int resId) { return (T) findViewById(resId); } /** * 管理所有Activity的类 */ private class ActivityController { private List<Activity> activities = new ArrayList<>(); /** * 添加Activity * * @param activity */ private void addActivity(Activity activity) { activities.add(activity); } /** * 移除Activity * * @param activity */ private void removeActivity(Activity activity) { activities.remove(activity); } /** * 结束所有Activity */ private void finishAll() { for (Activity activity : activities) { if (!activity.isFinishing()) { activity.finish(); } } } } }
以上就是Activity基类的代码,接下来我们要写布局文件了,创建一个登录的Activity,名字叫做LoginActivity,布局文件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:gravity="center" android:orientation="vertical" android:padding="10dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="用户名" android:textColor="#000000" android:textSize="16sp" /> <EditText android:id="@+id/name_et" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="6" android:hint="请输入用户名" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="密 码" android:textColor="#000000" android:textSize="16sp" /> <EditText android:id="@+id/password_et" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="6" android:hint="请输入密码" /> </LinearLayout> <Button android:id="@+id/login_btn" android:layout_width="match_parent" android:layout_height="wrap_co 4000 ntent" android:text="登录" /> </LinearLayout>
接下来我们修改LoginActivity中的代码
package com.example.czy.forceofflinedemo; import android.content.Intent; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; /** * Created by CZY on 2017/4/17. */ public class LoginActivity extends BaseActivity { private EditText nameEt, passwordEt; private Button loginBtn; @Override protected int bindLayout() { return R.layout.activity_login; } @Override protected void initView() { nameEt = bindView(R.id.name_et); passwordEt = bindView(R.id.password_et); loginBtn = bindView(R.id.login_btn); } @Override protected void initData() { loginBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //获取用户名 String name = String.valueOf(nameEt.getText()); //获取密码 String password = String.valueOf(passwordEt.getText()); //如果用户名为user并且密码为123456,就认为登录成功 if (name.equals("user") && password.equals("123456")) { Intent intent = new Intent(LoginActivity.this, MainActivity.class); startActivity(intent); finish(); } else { Toast.makeText(LoginActivity.this, "用户名或密码错误,请重新输入", Toast.LENGTH_SHORT).show(); } } }); } }
通过模拟一个简单的登录功能,首先将LoginActivity继承BaseActivity,然后调用findViewById()分别获取到用户名输入框、密码输入框、登录按钮的对象,在登录按钮里对输入的用户名和密码做判断,如果输入的用户名admin和密码123456一致,就认为登录成功,否则失败。登录成功后跳转到MainActivity,这里就认为我们跳转到了主界面,那么我们就在这里进行强制下线功能。
因为要将LoginActivity设置为启动Activity,所以还需要在AndroidManifest.xml中将起设置为启动Activity
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.czy.forceofflinedemo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" /> <activity android:name=".LoginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
将activity_main.xml进行修改
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" tools:context="com.example.czy.forceofflinedemo.MainActivity"> <Button android:id="@+id/force_offline_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="发送广播" /> </LinearLayout>
这里我们就添加一个按钮来触发强制下线功能,修改MainActivty中的代码
package com.example.czy.forceofflinedemo; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends BaseActivity { private Button forceOfflineBtn; @Override protected int bindLayout() { return R.layout.activity_main; } @Override protected void initView() { forceOfflineBtn = bindView(R.id.force_offline_btn); } @Override protected void initData() { forceOfflineBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //发送广播 Intent intent = new Intent("forceOffline"); sendBroadcast(intent); } }); } }
那么在这里有个重点,我们在按钮的点击事件里发送了一条广播,值为forceOffline,这条广播就是用于通知程序强制用户下线的,也就是说强制下线的逻辑并不是写在MainActivity里的,而是应该写在接收这条广播的广播接收器里面,这样强制下线的功能就不会依附于任何界面,不论在程序的任何地方,只需要发出这样一条广播就可以完成强制下线功能了。
那么接下来我们就要创建一个广播接收器来接收这条广播,由于广播接收器里面需要弹出一个对话框来阻止用户的正常操作,如果创建静态广播接收器 的话是没办法在onReceive()里弹出对话框这样的控件的,所以要创建一个动态广播接收器,但是我们并不知道用户是在什么时候,哪个界面中被强制下线,又不能在每个Activity中都去写一个动态广播接收器,那么该怎么办呢?
答案很简单,不要忘记我们前面写的那个基类BaseActivity,我们只需要在这里写一个动态广播接收器就可以了,因为所有的Activity都是继承这个BaseActivity这个类的。
修改BaseActivity中的代码(这次的代码包含了之前里面写的代码,也就是全部代码)
package com.example.czy.forceofflinedemo; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.View; import java.util.ArrayList; import java.util.List; /** * Created by CZY on 2017/4/5. */ public abstract class BaseActivity extends AppCompatActivity { private ActivityController activityController; private ForceOfflineReceiver receiver; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //绑定布局 setContentView(bindLayout()); //初始化组件 initView(); //初始化数据 initData(); //创建出管理所有Activity类的对象 activityController = new ActivityController(); activityController.addActivity(this); } @Override protected void onResume() { super.onResume(); IntentFilter intentFilter = new IntentFilter("forceOffline"); receiver = new ForceOfflineReceiver(); registerReceiver(receiver, intentFilter); } @Override protected void onPause() { if (receiver != null) { unregisterReceiver(receiver); receiver = null; } super.onPause(); } @Override protected void onDestroy() { activityController.removeActivity(this); super.onDestroy(); } /** * 绑定布局 * * @return */ protected abstract int bindLayout(); /** * 初始化组件 */ protected abstract void initView(); /** * 初始化数据 */ protected abstract void initData(); /** * 绑定组件 * * @param resId 组件id * @param <T> 强制转换类型 * @return */ protected <T extends View> T bindView(int resId) { return (T) findViewById(resId); } /** * 管理所有Activity的类 */ private class ActivityController { private List<Activity> activities = new ArrayList<>(); /** * 添加Activity * * @param activity */ private void addActivity(Activity activity) { activities.add(activity); } /** * 移除Activity * * @param activity */ private void removeActivity(Activity activity) { activities.remove(activity); } /** * 结束所有Activity */ private void finishAll() { for (Activity activity : activities) { if (!activity.isFinishing()) { activity.finish(); } } } } /** * 广播接收器 */ private class ForceOfflineReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, Intent intent) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("下线通知"); builder.setMessage("您的账号在另一地点登录,您被迫下线了。如果这不是您本人的操作,那么您的密码可能已经泄露,建议您修改密码。"); builder.setPositiveButton("重新登录", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(context, LoginActivity.class); startActivity(intent); //调用结束所有Activity的方法 activityController.finishAll(); } }); builder.setNegativeButton("退出", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { activityController.finishAll(); } }); builder.setCancelable(false); builder.show(); } } }
先看ForceOfflineReceiver这个内部类,这就是动态广播接收器,我们将弹出对话框的操作写在了这个类的onReceive()里,用AlertDialog.Builder来建造一个弹窗,注意这里一定要调用这个方法builder.setCancelable(),参数设置为false,否则用户按下系统返回键就可以将对话框关闭继续使用程序了。然后调用setPositiveButton()设置点击按钮重新登录跳转到登录页并且销毁所有的Activity。setNegativeButton()设置点击按钮退出程序(销毁所有Activity也就是退出程序)。
那么我们看一下是怎么注册这个广播接收器的,我们重写了Activity的两个方法onResume()和onPause()这两个生命周期,分别在这两个生命周期中注册和注销广播接收器,那为什么要这么写呢?原来不是都在onCreate()和onDestroy()这两个生命周期里去注册和注销广播接收器吗。这是因为我们始终需要保证只有处于栈顶的Activity才能接收到这条广播,非栈顶的Activity没必要去接收这条广播,,当一个Activity失去栈顶位置时就会自动注销该广播接收器所以在onResume()和onPause()这两个生命周期中注册和注销该广播。
到这里强制下线的所有的代码就都写完了。接下来我们运行一下看看效果
这是启动时显示的登录页
我们输入用户名user和密码123456
接下来点击登录跳转到主页
然后点击发送广播,弹出对话框,这时我们点击系统返回键或旁边空白处都是无效的
如果点击重新登录就跳转到登录页,如果点击退出,那么程序就退掉了
希望本次内容能给你带来收获,代码下载请点击 –> 项目源代码
相关文章推荐
- 利用广播实现强制下线功能
- 利用广播实现强制下线功能
- 广播的最佳实践-实现强制下线功能
- 广播的最佳实践-实现强制下线功能
- 广播的实现--实现强制下线功能
- 广播的最佳实践--实现强制下线功能
- Android进阶之路 - 广播实现强制下线功能
- 实现强制下线功能-广播实践案例
- 广播的最佳实践——实现强制下线功能
- 广播的最佳实践-实现强制下线功能
- 详解广播机制——实现强制下线功能
- 第一行代码-5.5 广播的最佳实践 实现强制下线功能
- Android通过广播实现强制下线功能
- 广播的最佳实践——实现强制下线功能
- 利用广播机制实现强制下线
- Android 广播------实现强制下线功能
- Adroid中广播接收者的使用,实现强制下线功能
- Android学习总结(八)———— 广播的最佳实践(实现强制下线功能)
- Android笔记(二十六)广播实践——实现强制下线功能