1.效果图
刚打开应用界面
输入正确的账号和密码后,点击login,效果如下
点击“Send force offline broadcast”按钮,就会出现下面的警告按钮
点击确定按钮,回到登陆界面
2.源代码
首先,强制下线意味着关闭所有活动,然后回到主界面。怎么一键kill掉所有activity呢。妥善的做法是创建一个collector来管理我们的类,具体做法如下
ActivityCollector.java
package com.example.broadcastbestpractice;
import android.support.v7.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
/**
* 项目名称:BroadcastBestPractice
* 类描述:
* 创建人:吴乐
* 创建时间:2016/3/31 16:19
* 修改人:吴乐
* 修改时间:2016/3/31 16:19
* 修改备注:
*/
public class ActivityCollector {
public static List<AppCompatActivity> activities = new ArrayList<AppCompatActivity>();
public static void addActivity(AppCompatActivity activity) {
activities.add(activity);
}
public static void removeActivity(AppCompatActivity activity) {
activities.remove(activity);
}
public static void finishAll() {
for (AppCompatActivity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}
且在每个活动的创建时,add到collector中;一个活动销毁前,从collector中remove即可,具体做法是创建一个继承自Activity的类来让所有活动继承
BaseActivity.java
package com.example.broadcastbestpractice;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
/**
* 项目名称:BroadcastBestPractice
* 类描述:
* 创建人:吴乐
* 创建时间:2016/3/31 16:23
* 修改人:吴乐
* 修改时间:2016/3/31 16:23
* 修改备注:
*/
public class BaseActivity extends AppCompatActivity{
@Override
public void onCreate(Bundle savedInstanceState ) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
题外话:这个方法很好用,如果项目中需要知道别人应用屏幕上每一个活动对应哪个类,只需要把继承的Activity改成你自己写的BaseActivity就好了。
直观上,第一步是创建登录界面,一个TableLayout就解决了
:
login.xml
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="1">
<TableRow>
<TextView
android:layout_height="wrap_content"
android:text="Account:"/>
<EditText
android:id="@+id/account"
android:layout_height="wrap_content"
android:hint="Input your account"/>
</TableRow>
<TableRow>
<TextView
android:layout_height="wrap_content"
android:text="Password:"/>
<EditText
android:id="@+id/password"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:hint="Input your password"/>
</TableRow>
<TableRow>
<Button
android:id="@+id/login"
android:layout_height="wrap_content"
android:layout_span="2"
android:text="Login"/>
</TableRow>
</TableLayout>
接下来仿真登录功能,逻辑很简单,判断输入的账号密码是否正确,如果正确,就跳转到新的界面(登陆成功)。
package com.example.broadcastbestpractice;
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
/**
* 项目名称:BroadcastBestPractice
* 类描述:
* 创建人:吴乐
* 创建时间:2016/3/31 16:49
* 修改人:吴乐
* 修改时间:2016/3/31 16:49
* 修改备注:
*/
public class LoginActivity extends BaseActivity{
private EditText accountEdit,passwordEdit;
private Button login;
@Override
public void onCreate(Bundle savedInstanceState) {
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(new View.OnClickListener() {
@Override
public void onClick(View v) {
String account = accountEdit.getText().toString();
String password = passwordEdit.getText().toString();
// 如果账号是 meinv ,密码是5277 ,则登录成功
if (account.equals("meinv") && password.equals("5277")){
Intent intent = new Intent(LoginActivity.this,MainActivity.class);
startActivity(intent);
finish();
}else {
Toast.makeText(LoginActivity.this,"account or password is invalibal",Toast.LENGTH_SHORT).show();
}
}
});
}
}
登录成功就进入主界面了,修改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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.broadcastbestpractice.MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/force_offline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send force offline broadcast"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
修改MainActivity.java
package com.example.broadcastbestpractice;
import android.annotation.TargetApi;
import android.content.Intent;
import android.net.Uri;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import static android.support.v4.app.ActivityCompat.startActivityForResult;
public class MainActivity extends BaseActivity {
private Button force_offline;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
force_offline = (Button)findViewById(R.id.force_offline);
force_offline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE");
sendBroadcast(intent);
}
});
}
}
主界面只有一个按钮:强制下线! 这个按钮发送了一个广播,广播内容 com.example.broadcastbestpractice.FORCE_OFFLINE。
接下来就好办了,广播接收器只要弹出警告,并且关闭所有activity即可。
package com.example.broadcastbestpractice;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.provider.Settings;
import android.view.WindowManager;
import static android.support.v4.app.ActivityCompat.startActivityForResult;
/**
* 项目名称:BroadcastBestPractice
* 类描述:
* 创建人:吴乐
* 创建时间:2016/3/31 17:09
* 修改人:吴乐
* 修改时间:2016/3/31 17:09
* 修改备注:
*/
public class ForceOfflineReceiver extends BroadcastReceiver{
@Override
public void onReceive(final Context context, Intent intent) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
dialogBuilder.setTitle("警告");
dialogBuilder.setMessage("你已经被强制下线,请重新登陆。");
dialogBuilder.setCancelable(false);//不能通过back按钮返回
dialogBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCollector.finishAll();//销毁所有活动
Intent intent = new Intent(context,LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//这是在广播接收器中启动活动的,所以要给Intent加入这个标志
context.startActivity(intent);
}
});
AlertDialog alertDialog = dialogBuilder.create();
// 需要设置AlertDialog类型,保证在广播接收器中可以正常弹出
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog.show();
}
}
讲了那么多,别忘了对manifest.xml进行修改,具体要修改三处,我就不细说了。
manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcastbestpractice">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"></activity>
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".ForceOfflineReceiver">
<intent-filter>
<action android:name="com.example.broadcastbestpractice.FORCE_OFFLINE" />
</intent-filter>
</receiver>
</application>
</manifest>
这样就可以完成预期的效果了。
最后一个悲伤的彩蛋,本人在android6.0系统测试的时候,发现不停的报错,查了一下是权限机制有改动了,有兴趣的同学可以研究一下:
彩蛋问题参考:alert问题