广播的最佳实践-实现强制下线功能

主体思想

使用ActivityCollector来收集所有活动,所有的活动均继承自BaseActivity,使得每个活动在onCreate方法执行时,都会被加入到AcrivityCollector中。在每个活动onDestroy时都会从ActivityCollector中移除。这样就可以一次性将所有的活动全部销毁。在登陆界面输入账号密码,代码的设定是只有当敲击Login按钮时才会去获取两个EditText中,符合逻辑

交互的流程是,输入账号密码,无误后跳转到MainActivity,点击按钮发送广播,app中的recreiver接收到广播执行onReceive方法,弹出AlertDialog窗口,给ok按钮设置监听。按下ok之后,用ActivityCollector销毁所有活动,重新加载login活动

值得注意

因为是从接收器中弹出活动,intent需加入FLAG_ACTIVITY_NEW_TASK,形如intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),在接受器里启动活动,需以这样的格式:context.startActivity(intent),而intent的初始化是以这样的形式:Intent intent = new Intent(context,MainActivity.class),似乎在接收器里都用context来表示接收器的上下文

再有,因为提示框是从广播接收器中弹出的,所以需要把对话框的类型设置为TYPE_SYSTEM_ALERT,形如:alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT),由于都调用了系统级别的对话框,也需要在Manifest文件下注册<user-permission android:name="android.permission.SYSTEM_ALERT_WINDOW">,该应用才能使用该对话框

另外需要注意的是,AlertDialog的造法:

1
2
3
4
5
6
7
AlertDialog.Builder builder = new AlertDialog.Builder(context)
builder.setTitle
builder.setMessage
builder.setPositiveButton("OK",new OnclickListener{....})
AlertDialog alertDialog = builder.create();
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
alertDialog.show()

 

先弄个builder,再用这个builder去建一个AlertDialog,接着再设置这个AlertDialog的类型,最后让他show出来

代码结构

代码结构代码结构

上代码

ActivityCollector.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ActivityCollector {

public static List<Activity> activities = new ArrayList<Activity>();

public static void addActivity(Activity activity){
activities.add(activity);
}

public static void removeActivity(Activity activity){
activities.remove(activity);
}

public static void finishAll(){
for(Activity activity : activities){
if(!activity.isFinishing()){
activity.finish();
}
}
}
}

BaseActivity.class

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BaseActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}

@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}

forceReceiver.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ForceOffReceiver extends BroadcastReceiver{

@Override
public void onReceive(final Context context, final Intent intent) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Waring");
builder.setCancelable(false);
builder.setMessage("you are forced to offline");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCollector.finishAll();
Intent intent1 = new Intent(context,loginActivity.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
});

AlertDialog alertDialog = builder.create();
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog.show();
}
}

loginActivity.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

public class loginActivity extends BaseActivity {

EditText accountEdit;

EditText passwordEdit;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
accountEdit = (EditText) findViewById(R.id.content_account);
passwordEdit = (EditText) findViewById(R.id.content_password);
Button btn = (Button) findViewById(R.id.btn_login);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String account = accountEdit.getText().toString();
String password = passwordEdit.getText().toString();
if(account.equals("guojiale") && password.equals("123456")){
Intent intent = new Intent(loginActivity.this,MainActivity.class);
startActivity(intent);
} else{
Toast.makeText(loginActivity.this, "Check your account and password", Toast.LENGTH_SHORT).show();
}
}
});
}

}

MainActivity.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainActivity extends BaseActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.btn_sendBrodcast);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("forceOff");
sendBroadcast(intent);
}
});
}

}

零散笔记

广播接收器中不允许开线程,如果广播接收器中有太多操作,耗时太长,程序会报错

在一个应用的Manifest文件中,配置
使得该应用获得监听系统开机广播的权限,在中,写 rr这是在注册监听器

在manifest中receiver(接收器)的intent-filter中的action是表示该接收器监听的广播类型一旦收到相应类型的广播,就会执行该接收器中的逻辑

在一个活动中发送广播,sendBroadcast为发送标准广播,sendOrderedBroadcast为发送有序广播,在Manifest文件下配置receiver需要的intentFilter,用priority来判断接收器的接收顺序

使用abortBroadcast() 截断广播

1
2
3
4
LocalBroadcastManager l = LocalBraodcastManager.getInstace(this)
l.registerReceiver(localReceiver,intentFilter);
l.sendBroadcast(intent)
l.unregisterReceiver(localReceiver)

 

抛出异常:
java.lang.RuntimeException: Unable to start receiver com.example.gaby.broadcastbestpractice.ForceOffReceiver: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@d5bf998 – permission denied for this window type

原因:使用了系统级别的对话框,但没有在Manifest文件下声明该权限
解决办法:在Manifest文件下声明< user-permission android:name=”android.permission.SYSTEM_ALERT_WINDOW” />

posted @ 2016-03-20 22:04  Gabyler  阅读(538)  评论(0编辑  收藏  举报