在应用程序中的一个常见功能是 “强制下线”。比如 QQ 号在别处登录后,就会把当前的 QQ 号挤下线。实现思路是:在界面上弹出一个对话框,让用户无法进行任何其他操作,只能点击对话框中的确定按钮, 然后回到登录界面。
要实现强制下线之前,必须先关闭所有的活动。首先,创建一个 Activities 类用于管理所有的活动:
public class Activities {
public static List<Activity> activities = new ArrayList<>();
/**
* 新增活动
*
* @param activity
*/
public static void add(Activity activity) {
activities.add(activity);
}
/**
* 移除活动
*
* @param activity
*/
public static void remove(Activity activity) {
activities.remove(activity);
}
/**
* 结束所有活动
*/
public static void finishAll() {
for (Activity activity : activities) {
if (activity.isFinishing()) {
activity.finish();
}
}
}
}
然后创建 BaseActivity 类作为所有活动的父类:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Activities.add(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
Activities.remove(this);
}
}
接下来要创建一个登录界面,新建 LoginActivity(在 Android Studio 中,建议创建空的活动),编辑 activity_login.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal">
<TextView
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="账号:"
android:textSize="18sp" />
<EditText
android:id="@+id/account"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal">
<TextView
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="密码:"
android:textSize="18sp" />
<EditText
android:id="@+id/password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:inputType="textPassword" />
</LinearLayout>
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="登录" />
</LinearLayout>
我们使用 LinearLayout 编写了一个登陆布局,使用纵向排列,从上到下分别是账号、密码和登陆按钮。
接着修改 LoginActivity 中的代码:
public class LoginActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
final EditText accountEditText=(EditText)findViewById(R.id.account);
final EditText passwordEditText=(EditText)findViewById(R.id.password);
Button loginBtn=(Button)findViewById(R.id.login);
loginBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String account=accountEditText.getText().toString();
String password=passwordEditText.getText().toString();
if(account.equals("admin")&&password.equals("1")){//登录成功
startActivity(new Intent(LoginActivity.this,MainActivity.class));
finish();
}else{//登录失败,弹出提示
Toast.makeText(LoginActivity.this, "账号或密码不正确", Toast.LENGTH_SHORT).show();
}
}
});
}
}
LoginActivity 继承自 BaseActivity。然后获取账号与密码的输入值,进行判断。如果登录成功,就启动 MainActivity 活动;否则登录失败,弹出提示。
这里假设把 MainActivity作为登陆成功后进入的主界面,修改对应的布局文件:
<?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_horizontal"
android:orientation="vertical">
<Button
android:id="@+id/force_offline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送强制下线广播"
/>
</LinearLayout>
这里,我们只定义了一个按钮,用于触发强制下线功能。然后修改 MainActivity 中的代码:
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.force_offline).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendBroadcast(new Intent("net.deniro.android.FORCE_OFFLINE"));
}
});
}
}
在按钮的点击事件中发送了一条广播,用于通知程序让强制用户下线。
强制用户下线的逻辑是写在接收这条广播的广播接收器里面,这样强制下线的功能就不会依附于任何的界面,不管是在程序的任何地方,只需要发出这样一条广播,就可以完成强制下线的操作啦O(∩_∩)O~
我们在 BaseActivity 中动态注册了一个广播接收器,因为所有的活动都是继承自 BaseActivity 的,这样这些活动就都可以支持这个广播接收器咯。
修改 BaseActivity 中的代码:
public class BaseActivity extends AppCompatActivity {
private ForceOffLineReceiver receiver;
...
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.setCancelable(false);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Activities.finishAll();//销毁所有活动
context.startActivity(new Intent(context, LoginActivity.class));//启动【登录】活动
}
});
builder.show();
}
}
@Override
protected void onResume() {
super.onResume();
//注册广播接收器
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("net.deniro.android.FORCE_OFFLINE");
receiver = new ForceOffLineReceiver();
registerReceiver(receiver, intentFilter);
}
@Override
protected void onPause() {
super.onPause();
if(receiver!=null){//注销
unregisterReceiver(receiver);
}
receiver=null;
}
}
这里使用了 AlertDialog,并 setCancelable() 让对话框不可取消(如果可取消,用户点击一下 back 键,对话框就没啦),然后使用 setPositiveButton() 方法定义【确定】按钮,当用户点击按钮时,销毁所有活动并/启动【登录】活动。
我们重写了 onResume() 和 onPause() 这两个生命周期的函数,然后分别在这两个方法里注册和注销了自定义的广播接收器,之所以这样写是因为我们要始终需要保证只有处于栈顶的活动才能接收到下线广播,非栈顶活动不需要也没有必要接收这条广播。
最后修改 AndroidManifest.xml:
<activity
android:name=".LoginActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
将 LoginActivity 设置为主活动。
运行程序:
登录界面输入正确的账号和密码后,点击登录进入主界面:
主界面点击【发送强制广播】按钮:
强制下线提示
网友评论
Intent intent = new Intent(context, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
最简单的办法就是继承Application,然后registerActivityLifecycleCallbacks来监听Activity的生命周期。
首先我承认你说的对。
然后我想说的是面向对象的三大特征是封装继承多态,多态要靠继承来实现,但是不是继承的唯一目的,基类是用来封装一些共有的特性,共有的一些方法而存在的。在很多情况下组合优于继承,这个大部分都知道,与本问题无关。
回到作者这篇文章本身,这个基类的封装没有任何问题,这个类就是要在某个具体的回调里做一个具体的事情,而且是每个子类都要做的,你再怎么用组合,也是要在onCreate,onDestory里面去做具体的事,你不想每个子类都写一遍相同的代码,继承一个公共类是最简单最合适的实现。
至于大部分第三方库如果需要用这种侵入式的方式需要你继承一个基类来实现功能,我认为都不是很合适的实现,除非你是FragmentActivity AccompatActivity这种对Activity的封装。
你都知道大部分情况下继承不是最优解,为什么对提供功能的第三方库就能这么宽容呢。
就目前Android开发来说,第三方库需要你强行继承某个Activity的实现并不多见(见过几个,具体是什么忘记了,然后这个库被我否掉了,至于微信支付宝之类的不算,这些并不具体显示某个界面),而且这类库明显的特征是在某个具体的页面才需要,那么针对某个具体的界面,具体的功能可以做单独的处理。
以上仅针对Activity的继承,如果谁提供的第三方库真的是需要你继承Activity实现而且还是你必须要用的,一般你的基类就可以继承这个第三方库需要你继承的类了。
我说的是你用别人的库,而不是别人用你的库,例如别人的AAR里面有个Activity,这种方法是显然做不到的。