一、全局广播
Android中的广播可以分为两种类型:标准广播和有序广播
标准广播:一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序。无法进行截断。
有序广播:一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。此时的广播接收器是有先后顺序的,优先级高的广播就可以先收到广播,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息。
1.系统广播
Android内置了许多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。比如手机开机,电量变化,时间或时区发生变化也会发出一条广播。如果想要接收这些广播,就需要广播接收器。
2.静态注册
1、注册方式:在AndroidManifest里进行注册。
首先在Application节点里头,添加一个receiver节点。name则是我们的广播接收者。
比如说,我们监听开机广播,那么我们跟前面一样,创建一个收音机,也就是广播接收者:
package com.sunofbeaches.broadcastreceiverdemo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class BootCompletedReceiver extends BroadcastReceiver {
public static final String TAG = "BootCompletedReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "actions -- >" + action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
Log.d(TAG, "开机完成");
}
}
}
接着,这就是静态注册了,在AndroidMainfest.xml里的application节点里添加receiver节点,并且添加意图过滤的action:
<receiver android:name=".BootCompletedReceiver">
<!--用来接收广播-->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
并且需要添加权限:
静态注册内部类广播要注意什么呢?
首先是静态注册,其次是内部广播接收者!
1、这个内部广播接收者需要声明为静态的;
2、这个内部的静态接收者必须是public的;
3、在receiver的name里是外部类的名字$内部类的名字
3.动态注册
假设我们做一个地图软件 ,当我们知道屏幕锁定以后,那么就不去更新数据,如果是屏幕打开了,就去更新数据。为什么要这么做呢,一是省电,二是省流量,对吧!
怎么做呢?
首先,我们编写一个广播接收者:
我是在MainActivity这个类里头创建了这个广播接收者,一般来说,动态接收的广播多数情况下是内部类,这样子可以操作外部类(外部类可以操作内部类)。
package com.sunofbeaches.broadcastdemo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private ScreenStatusReceiver mScreenStatusReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//动态注册广播
//第一步,创建意图过滤器
IntentFilter intentFilter = new IntentFilter();
//第二步,添加要监听的广播action
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
//第三步,创建广播接收者,并且设置成成员变量,以便于取消注册,释放资源
if (mScreenStatusReceiver == null) {
mScreenStatusReceiver = new ScreenStatusReceiver();
}
//第四步,注册广播接收者
this.registerReceiver(mScreenStatusReceiver, intentFilter);
}
private class ScreenStatusReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
//屏幕关闭
Log.d(TAG, "屏幕关闭...停止数据更新");
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
//屏幕打开
Log.d(TAG, "屏幕关闭...继续数据更新");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消广播注册
if (mScreenStatusReceiver != null) {
this.unregisterReceiver(mScreenStatusReceiver);
mScreenStatusReceiver = null;
}
}
}
两种注册方式的区别
静态注册可以一直监听着,即使应用没有起来,也可以监听着,但是耗资源,长期监听着。
静态注册的广播优先级高于动态注册的广播。
动态注册的优点就是省资源,需要的时候才监听,不需要的时候需要取消注册。
不可以静态注册的广播
有些广播,只可以动态注册的。有那些呢?
android.intent.action.SCREEN_ON
android.intent.action.SCREEN_OFF
android.intent.action.BATTERY_CHANGED
android.intent.action.CONFIGURATION_CHANGED
android.intent.action.TIME_TICK
二、发送自定义广播
1.发送标准广播
前面说到了,要注意的是,内部广播接收者类,需要是静态的,Public的,注册的时候,是外部类名$内部类名
package com.sunofbeaches.broadcastdemo;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
/**
* Created by TrillGates on 18/3/18.
* God bless my code!
*/
public class SendBroadcastActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_broadcast);
}
public void sendBroadcast(View view) {
Intent intent = new Intent();
//action只能有一个,所以叫setAction而不是addActon。
//而广播接收者可以监听多个广播,所以是addAction
//action的命名一般是报名+动作名,这样子比较唯一
intent.setAction("com.sunofbeaches.broadcastdemo.SEND_BROADCAST_CLICK");
//也可以携带数据
intent.putExtra("Content", "这是我点击按钮发送的广播!");
sendBroadcast(intent);
}
public static class InnerReceiver extends BroadcastReceiver{
private static final String TAG = "InnerReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "Inner receiver 接收到的actions... " + action);
if ("com.sunofbeaches.broadcastdemo.SEND_BROADCAST_CLICK".equals(action)) {
String content = intent.getStringExtra("Content");
Log.d(TAG, "content is == " + content);
}
}
}
}
静态注册
<receiver android:name=".SendBroadcastActivity$InnerReceiver">
<intent-filter>
<action android:name="com.sunofbeaches.broadcastdemo.SEND_BROADCAST_CLICK"/>
</intent-filter>
</receiver>
注意版本问题需要改为
Intent intent=new Intent("com.example.boradcasttest.MY_BROADCAST");
//参数是“包名”,接收器类名(也要加上包名)
intent.setComponent(new ComponentName("com.example.boradcasttest","com.example.boradcasttest.MyBroadcastReceiver"));
sendOrderedBroadcast(intent,null);
2.发送有序广播
Intent AnotherIntent=new Intent("com.example.boradcasttest.MY_BROADCAST");
AnotherIntent.setComponent(new ComponentName("com.example.boradcasttest","com.example.boradcasttest.BroadcastText2"));
sendOrderedBroadcast(AnotherIntent,null);
priority优先级,优先级是1000~-1000之间,超出了范围则会使用边界值
<receiver
android:name=".BroadcastText2"
android:enabled="true"
android:exported="true">
<intent-filter
android:priority="20">
<action android:name="com.example.boradcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter
android:priority="100">
<action android:name="com.example.boradcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
//终止广播往下传
abortBroadcast();
三、本地广播
系统广播很容易引起安全性问题,比如我们发送的一些携带关键性数据的广播有可能被其他应用程序截获,或者其他的程序不停向我们的广播接收器里发送各种垃圾广播。为了能解决,使得我们的发出的广播只能在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播。
本地广播主要使用一个LocalBroacastManager来对广播进行管理,并提供了发送广播和广播接收器的方法。
package com.example.boradcasttest;
import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
//private NetworkChangeReceiver networkChangeReceiver;
private MyBroadcastReceiver myBroadcastReceiver;
private BroadcastText2 anotherBroadcastReceiver;
private LocalBroadcastManager localBroadcastManager;
private LocalReceiver localReceiver;
@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 View.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();
unregisterReceiver(localReceiver);
}
//接受到广播就执行以下操作
class LocalReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received local broadcast", Toast.LENGTH_LONG).show();
}
}
}
首先通过LocalBroadcastManager的getInstance方法得到了它的一个实例,然后在注册广播接收器的时候调用的是LocalBroadcastManage的registerReceiver()方法,在发送广播的时候调用 localBroadcastManager的sendBroadcast()方法。
强制下线
package com.example.broadcastbestpractice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
/**
* 由于广播接收器里面需要弹出一个对话框来阻塞用户的正常操作,但如果创建的是一个静态注册的广播接收器,是没办法在onReceive()方法
* 里弹出对话框这样的UI控件的,而我们显然不可能在每个活动中都去注册一个动态的广播接收器,我们在BaseActivity中动态注册一个
* 广播接收器就行了,因为所有活动都继承自BaseActivity。
*/
//创建BaseActivity类作为所有活动的父类
public class BaseActivity extends AppCompatActivity {
private ForceOfflineReceiver receiver;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
/**
* 我们再onResume和onPause注册和取消注册广播接收器
* 是因为我们始终需要保证只有处在栈顶的活动才能接收到这条强制下线广播,非栈顶的活动不应该也没必要接收到这条广播
*/
@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;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
/**
*广播接收器(注册什么广播,接收什么广播)
*/
class ForceOfflineReceiver extends BroadcastReceiver
{
@Override
public void onReceive(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.");
//将对话框设置为不可取消,否则用户按一下Back就可以关闭对话框
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);//重新启动LoginActivity
}
});
builder.show();
}
}
}
package com.example.broadcastbestpractice;
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
/**
* 由于强制下线需要关闭所有的活动,然后回到登录界面。因此我们创建
* 一个ActivityCollector类用于管理所有的活动
*/
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();
}
}
activities.clear();
}
}
package com.example.broadcastbestpractice;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
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 View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE");
sendBroadcast(intent);
}
});
}
}
package com.example.broadcastbestpractice;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class LoginActivity extends BaseActivity {
private EditText accountEdit;
private EditText passwordEdit;
private Button login;
private SharedPreferences.Editor mEditor;
@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);
SharedPreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();
PreferenceManager.getDefaultSharedPreferences(this);
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String account=accountEdit.getText().toString();
String password=passwordEdit.getText().toString();
//如果账号是admin,且密码是123456,就认为登录成功
if(account.equals("Chen")&&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_LONG).show();
}
}
});
}
}