1.广播接收器
不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等。
本地广播是无法通过静态注册的方式来接收的。因为静态注册主要是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,程序肯定已经启动了。
本地广播的优势:
可以明确地知道正在发送的广播不会离开我们的程序,因此不必担心机密数据泄漏。
其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。
发送本地广播比发送系统全局广播将会更加高效。
示例代码:
public class MainActivity extends Activity { private IntentFilter intentFilter; private LocalReceiver intentFilter; private LocalBroadcastManager localBroadcastManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); localBroadcastManager = LocalBroadcastManager.getInstance(this); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent); } }); intentFilter = new IntentFilter(); intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST"); localReceiver = new LocalReceiver(); localBroadcastManager.registerReceiver(localReceiver, intentFilter); } @Override protected void onDestroy() { super.onDestroy(); localBroadcastManager.unregisterReceiver(localReceiver); } class LocalReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show(); } } }
案例:实现强制下线功能
public class ActivityCollector { public static List<Activity> activities = new ArrayList<>(); 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(); } } } }
public class BaseActivity extends Activity { private ForceOfflineReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityCollector.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } @Override protected void onResume() { super.onResume(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.example.broadcastbestpractice.FORCE_OFFLINE"); receiver = new ForceOfflineReceiver(); registerReceiver(receiver, intentFilter); } @Override protected void onPause() { super.onPause(); if(receiver != null){ unregisterReceiver(receiver); receiver = null; } } class ForceOfflineReceiver extends BroadcastReceiver{ @Override public void onReceive(final Context context, Intent intent) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Warning"); builder.setMessage("You are forced to be offline. Please try to login again."); builder.setCancelable(false); builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCollector.finishAll(); Intent intent = new Intent(context, LoginActivity.class); context.startActivity(intent); } }); builder.show(); } } }
public class LoginActivity extends BaseActivity { private EditText accountEdit; private EditText passwordEdit; private Button login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); accountEdit = (EditText) findViewById(R.id.account); passwordEdit = (EditText) findViewById(R.id.password); login = (Button) findViewById(R.id.login); login.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String account = accountEdit.getText().toString(); String password = passwordEdit.getText().toString(); if(account.equals("admin") && password.equals("123456")){ Intent intent = new Intent(LoginActivity.this, MainActivity.class); startActivity(intent); finish(); }else{ Toast.makeText(LoginActivity.this, "account or password is invalid", Toast.LENGTH_SHORT).show(); } } }); } }
public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button forceOffline = (Button) findViewById(R.id.force_offline); forceOffline.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE"); sendBroadcast(intent); } }); } }
最后说明:activity_main.xml布局文件里面只放置了一个按钮,activity_login.xml布局文件里面放置了账户密码框及登录按钮。
静态注册的广播接收器,是没有办法在onReceive()方法里弹出对话框这样的UI控件的。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog)。
使用静态注册广播,只能弹出System Alert类型的Dialog。但google在 6.0加入了运行时权限的概念,需要在Java代码中进行动态申请。为了防止旧的应用程序崩溃,只对targetSDK为23及以上的程序使用新的权限机制。 相应的解决方法有如下几种。
第一种方式是:targetSDK设为23以下就可以规避问题。
第二种方式是:设置里面给了这个调试应用【允许悬浮窗】 但是得手动的打开(我是没有找到这个悬浮窗到底在哪里)。
第三种方式是:使用无需权限显示悬浮窗。参考链接: Android无需权限显示悬浮窗: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1017/3589.html
问题解决
这里使用第三种,简单方便。 1 修改广播接收类中的import包 将
import android.support.v7.app.AlertDialog;
修改为:
import android.app.AlertDialog;
2 修改AlertDialog的弹窗类型为:TYPE_TOAST 将
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
修改为:
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST);
这样编译运行就可以正常弹出AlertDialog了。
上面的笔记来自 http://blog.csdn.net/chenHCYJ/article/details/53191675
继承BaseActivity,避免了在每一个活动中都去注册一个动态的广播接收器。
最后,因为我们始终需要保证只有处于栈顶的活动才能接收到这条强制下线广播,非栈顶的活动不应该也没有必要去接收这条广播,所以写在onResume()和onPause()方法里就可以很好地解决这个问题,当一个活动失去栈顶位置时就会自动取消广播接收器的注册。