【实战】广播实践,实现登录强制下线
本章学习了广播的机制,通过一个强制下线登陆用户的功能进行实战。
一、建立工具类ActivityCollector,实现关闭所有活动的功能,
1 package Tools; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.app.Activity; 7 8 //工具类 9 public class ActivityCollector { 10 public static List<Activity> activities = new ArrayList<>();//新建列表存储活动 11 public static void addActivity(Activity activity)//新增活动到列表 12 { 13 activities.add(activity); 14 } 15 public static void removeActivity(Activity activity)//移除活动出列表 16 { 17 activities.remove(activity); 18 } 19 public static void finishAll()//移除所有活动 20 { 21 for(Activity activity : activities) 22 { 23 if(!activity.isFinishing()) 24 { 25 activity.finish(); 26 } 27 } 28 } 29 }
二、新建基类BaseActivity继承于Activity,重写父类的onCreate方法和onDestory方法如下,其他的活动均继承于此基类,代码如下:
1 package BaseActivity; 2 3 import Tools.ActivityCollector; 4 import android.app.Activity; 5 import android.os.Bundle; 6 7 public class BaseActivity extends Activity { 8 @Override 9 protected void onCreate(Bundle savedInstanceState) { 10 // TODO Auto-generated method stub 11 super.onCreate(savedInstanceState); 12 ActivityCollector.addActivity(this);//添加此活动 13 } 14 15 @Override 16 protected void onDestroy() { 17 // TODO Auto-generated method stub 18 super.onDestroy(); 19 ActivityCollector.removeActivity(this);//移除此活动 20 } 21 }
三、使用表格布局TableLayout创建登陆界面,并且使用SharedPreferences实现记住密码功能,创建登陆界面的xml代码如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/TableLayout1" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:stretchColumns="1" > 7 8 <TableRow> 9 <TextView 10 android:layout_height="wrap_content" 11 android:text="Account:"/> 12 <EditText 13 android:id="@+id/account" 14 android:layout_height="wrap_content" 15 android:hint="Input your account"/> 16 </TableRow> 17 18 <TableRow> 19 <TextView 20 android:layout_height="wrap_content" 21 android:text="Password:"/> 22 <EditText 23 android:id="@+id/password" 24 android:layout_height="wrap_content" 25 android:hint="Input your password" 26 android:inputType="textPassword"/> 27 </TableRow> 28 29 <TableRow > 30 <CheckBox 31 android:id="@+id/remember_pass" 32 android:layout_height="wrap_content"/> 33 <TextView 34 android:layout_height="wrap_content" 35 android:text="Remember password"/> 36 </TableRow> 37 38 <TableRow> 39 <Button 40 android:id="@+id/login" 41 android:layout_height="wrap_content" 42 android:layout_span="2" 43 android:text="Login"/> 44 </TableRow> 45 46 </TableLayout>
四、做好了登陆界面,就要创建登陆对应的活动LoginActivity继承于BaseActivity;在代码中实现了记住密码功能;
1 package com.example.broadcastbestpractice; 2 3 import android.content.Intent; 4 import android.content.SharedPreferences; 5 import android.os.Bundle; 6 import android.preference.PreferenceManager; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.widget.Button; 10 import android.widget.CheckBox; 11 import android.widget.EditText; 12 import android.widget.Toast; 13 import BaseActivity.BaseActivity; 14 15 public class LoginActivity extends BaseActivity { 16 17 private SharedPreferences pref;//定义私有类型的SharedPreferences 18 private SharedPreferences.Editor editor;//定义私有类型的SharedPreferences.Editor 19 20 private EditText accountEdit;//定义私有类型四个控件 21 private EditText passwordEdit; 22 private Button login; 23 private CheckBox rememberPass; 24 25 @Override 26 protected void onCreate(Bundle savedInstanceState) { 27 // TODO Auto-generated method stub 28 super.onCreate(savedInstanceState); 29 setContentView(R.layout.login); 30 31 //获取PreferenceManager对象 32 pref = PreferenceManager.getDefaultSharedPreferences(this); 33 34 //实例化控件 35 accountEdit = (EditText) findViewById(R.id.account); 36 passwordEdit = (EditText) findViewById(R.id.password); 37 rememberPass = (CheckBox) findViewById(R.id.remember_pass); 38 login = (Button) findViewById(R.id.login); 39 40 //调用PreferenceManager的getBoolean方法获取存放起来的键值对,刚开始为false; 41 boolean isRemember = pref.getBoolean("remember_password", false); 42 43 //isRemember如果是真,则把存放起来的值取出来,赋值给控件,初始化让控件中显示存储的值 44 if(isRemember) 45 { 46 String account = pref.getString("account", ""); 47 String password = pref.getString("password", ""); 48 accountEdit.setText(account); 49 passwordEdit.setText(password); 50 rememberPass.setChecked(true); 51 } 52 53 //设置登录按钮的点击事件 54 login.setOnClickListener( new OnClickListener() { 55 56 @Override 57 public void onClick(View v) { 58 // TODO Auto-generated method stub 59 String account = accountEdit.getText().toString(); 60 String password = passwordEdit.getText().toString(); 61 if(account.equals("admin") && password.equals("123456")) 62 { 63 editor = pref.edit(); 64 if(rememberPass.isChecked())//检查复选框是否被选中,选中的话存储到editor中 65 { 66 editor.putBoolean("remember_password", true); 67 editor.putString("account", account); 68 editor.putString("password", password); 69 } 70 else//没选中的话清楚editor中的值 71 { 72 editor.clear(); 73 } 74 editor.commit();//提交editor 75 76 //使用Intent实现页面的跳转 77 Intent intent = new Intent(LoginActivity.this , MainActivity.class); 78 startActivity(intent); 79 finish();//销毁本活动 80 } 81 else//如果密码错误,使用Toast进行提示 82 { 83 Toast.makeText(LoginActivity.this, "account or password is invalid", Toast.LENGTH_SHORT).show(); 84 } 85 } 86 }); 87 } 88 }
五、布局登陆好的界面,这里用系统自动生成的页面来修改,简单起见,只添加一个按钮来实现销毁所有活动,实现强制下线;
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:id="@+id/LinearLayout1" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:paddingBottom="@dimen/activity_vertical_margin" 7 android:paddingLeft="@dimen/activity_horizontal_margin" 8 android:paddingRight="@dimen/activity_horizontal_margin" 9 android:paddingTop="@dimen/activity_vertical_margin" 10 tools:context="com.example.broadcastbestpractice.MainActivity" > 11 12 <Button 13 android:id="@+id/force_offline" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:text="Send force offline broadcast" /> 17 18 </LinearLayout>
六、编写MainActivity中的代码,发送广播实现强制下线功能;需要注意的是强制用户下线的代码不在这里,而在接收这条广播的广播接收器里面,这样强制下线就不会依赖于任何界面;只要收到广播就可以实现强制下线
1 package com.example.broadcastbestpractice; 2 3 import BaseActivity.BaseActivity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.view.View.OnClickListener; 8 import android.widget.Button; 9 10 public class MainActivity extends BaseActivity { 11 @Override 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.activity_main); 15 Button forceOffline = (Button) findViewById(R.id.force_offline); 16 17 //在点击事件里发送一条广播,广播的值为com.example.broadcastbestpractice.FORCE_OFFLINE,用于通知用户强制下线 18 forceOffline.setOnClickListener(new OnClickListener() { 19 @Override 20 public void onClick(View v) { 21 Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE"); 22 sendBroadcast(intent);//发出一条广播 23 } 24 }); 25 } 26 27 28 }
七、接下来就要创建一个广播接收器了,用于实现强制下线;新建类ForceOfflineReceiver继承于BroadcastReceiver,代码如下:
1 package com.example.broadcastbestpractice; 2 3 import com.example.broadcastbestpractice.LoginActivity; 4 import Tools.ActivityCollector; 5 import android.app.AlertDialog; 6 import android.content.BroadcastReceiver; 7 import android.content.Context; 8 import android.content.DialogInterface; 9 import android.content.Intent; 10 import android.view.WindowManager; 11 12 public class ForceOfflineReceiver extends BroadcastReceiver { 13 14 //重写BroadcastReceiver的onReceive方法,这里的Context一定要定义为final 15 public void onReceive(final Context context, Intent intent) { 16 // TODO Auto-generated method stub 17 18 //定义弹出框! 19 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context); 20 dialogBuilder.setTitle("Warning"); 21 dialogBuilder.setMessage("You are force to be offline, Please try to login again!"); 22 dialogBuilder.setCancelable(false);//设置不可取消,不然强制下线没有意义 23 24 //使用弹框的setPositiveButton方法设置对话框的确定按钮事件, 25 //具体的操作:“销毁所有活动,跳转到其他页面,往intent中加入标签FLAG_ACTIVITY_NEW_TASK,然后启动新活动 26 dialogBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() { 27 @Override 28 public void onClick(DialogInterface dialog, int which) { 29 // TODO Auto-generated method stub 30 ActivityCollector.finishAll();//销毁所有活动! 31 Intent intent = new Intent(context,LoginActivity.class); 32 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 33 context.startActivity(intent);//必须要加上前面的 34 } 35 }); 36 AlertDialog alertDialog = dialogBuilder.create(); 37 //设置AlertDialog的类型,保证在广播接收器中正常弹出 38 //系统提示。它总是出现在应用程序窗口之上。 39 //public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW +3; 40 41 //需要注意的是这种类型的AlterDialog必须要在AndroidManifedt中进行注册,不然不能使用 42 alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 43 //调用AlterDialog的show方法显示弹出框 44 alertDialog.show(); 45 } 46 47 }
八、最后我们只要设置下AndroidManifedt.xml就可以实现该功能了,主要要获取弹出框权限、注册活动、设置主活动、设置广播接收
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.broadcastbestpractice" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="14" 9 android:targetSdkVersion="21" /> 10 11 <!-- android.permission.SYSTEM_ALERT_WINDOW --> 12 <!-- 允许一个程序打开窗口使用 TYPE_SYSTEM_ALERT,显示在其他所有程序的顶层 --> 13 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> 14 15 <application 16 android:allowBackup="true" 17 android:icon="@drawable/ic_launcher" 18 android:label="@string/app_name" 19 android:theme="@style/AppTheme" > 20 <activity 21 android:name=".LoginActivity" 22 android:label="@string/app_name" > 23 <intent-filter> 24 <action android:name="android.intent.action.MAIN" /> 25 26 <category android:name="android.intent.category.LAUNCHER" /> 27 </intent-filter> 28 </activity> 29 30 <activity android:name=".MainActivity"></activity> 31 32 <receiver android:name=".ForceOfflineReceiver" 33 > 34 <intent-filter> 35 <action android:name="com.example.broadcastbestpractice.FORCE_OFFLINE"/> 36 </intent-filter> 37 </receiver> 38 39 </application> 40 41 </manifest>
至此,此次实现用户登录强制下线的功能已经全部实现,用到了UI设计、活动、SharePreferences存储、广播发送和接收等知识;运行效果图如下: