安卓中每个应用程序都可以对自己感兴趣的广播进行注册,广播可来自系统也可来自应用程序。
标准广播(Normal broadcasts)
一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的 
 
有序广播(Ordered broadcasts)
一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了 
 
 
尝试接收系统广播 动态注册监听网络变化 
新建BroadcastTest项目 
在AndroidManifest.xml中添加代码注册网络权限 
 
1 <uses-permission android:name ="android.permission.ACCESS_NETWORK_STATE"  /> 
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 public class   extends  AppCompatActivity   	private  IntentFilter  intentFilter; 	private  NetworkChangeReceiver  networkChangeReceiver; 	 	 	protected  void onCreate(Bundle  savedInstanceState) { 		super .onCreate(savedInstanceState); 		setContentView(R .layout.activity_main); 		intentFilter = new  IntentFilter (); 		intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE" ); 		networkChangeReceiver = new  NetworkChangeReceiver (); 		registerReceiver(networkChangeReceiver, intentFilter); 		} 	 	 	protected  void onDestroy() { 		super .onDestroy(); 		unregisterReceiver(networkChangeReceiver); 		} 	class  NetworkChangeReceiver  extends  BroadcastReceiver   		 		 		public void onReceive(Context  context, Intent  intent) { 		Toast .makeText(context, "network changes" , Toast .LENGTH_SHORT ).show(); 		} 	} } 
这里定义了内部类NetworkChangeReceiver继承自BroadcastReceiver然后重写了onReceive函数,在onCreate中创建了一个IntentFilter的实例,并且添加了android.net.conn.CONNECTIVITY_CHANGE这个action,这是网络改变的时候系统会发出的一条广播的值,调用registerReceiver将两个实例传入就可以接收到这条广播了
添加对是否有网络的判断,修改MainActivity中NetworkChangeReceiver的代码 
 
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 31 32 33 public class   extends  AppCompatActivity   	private  IntentFilter  intentFilter; 	private  NetworkChangeReceiver  networkChangeReceiver; 	 	 	protected  void onCreate(Bundle  savedInstanceState) { 		super .onCreate(savedInstanceState); 		setContentView(R .layout.activity_main); 		intentFilter = new  IntentFilter (); 		intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE" ); 		networkChangeReceiver = new  NetworkChangeReceiver (); 		registerReceiver(networkChangeReceiver, intentFilter); 		} 	 	 	protected  void onDestroy() { 		super .onDestroy(); 		unregisterReceiver(networkChangeReceiver); 		} 	    class  NetworkChangeReceiver  extends  BroadcastReceiver           public void onReceive(Context  context,Intent  intent){             ConnectivityManager  connectivityManager=(ConnectivityManager )getSystemService(Context .CONNECTIVITY_SERVICE );             NetworkInfo  networkInfo=connectivityManager.getActiveNetworkInfo();             if (networkInfo!=null  && networkInfo.isAvailable()){                 Toast .makeText(context, "网络已连接" , Toast .LENGTH_SHORT ).show();             }else {                 Toast .makeText(context, "网络已断开" , Toast .LENGTH_SHORT ).show();             }                      }     } } 
在onReceive()方法中,首先通过getSystemService()方法得到了ConnectivityManager的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它的getActiveNetworkInfo()方法可以得到NetworkInfo的实例,接着调用NetworkInfo的isAvailable()方法,就可以判断出当前是否有网络了
静态注册实现开机启动 动态注册的广播比较自由,但是必须在程序启动以后才能收到广播,要让程序在未启动的时候收到广播就要用静态注册的方法
新建一个广播接收器 
修改BootCompleteReceiver的代码 
 
1 2 3 4 5 6 public class  BootCompleteReceiver  extends  BroadcastReceiver   	@Override  	public void onReceive(Context  context, Intent  intent) { 		Toast .makeText(context, "Boot Complete" , Toast .LENGTH_LONG ).show(); 		} } 
在AndroidManifest.xml文件中注册静态广播(一定要) 
 
1 2 3 4 5 6 7 </application > 		···        	<receiver  android:name =".MyBroadcastReceiver"               android:enabled ="true"              android:exported ="true" >           </receiver >  </application > 
在application标签内添加receiver标签注册静态广播,用法和标签非常相似,也是通过android:name来指定具体注册哪一个广播接收器,而enabled和exported属性则是根据我们刚才勾选的状态自动生成的 
添加接收开机广播的权限,同样修改AndroidManifest.xml 
 
1 <uses-permission android:name ="android.permission.RECEIVE_BOOT_COMPLETED" /> 
1 2 3 4 5 6 7 8 <receiver  	android:name =".BootCompleteReceiver"  	android:enabled ="true"  	android:exported ="true" > 	<intent-filter >  		<action  android:name ="android.intent.action.BOOT_COMPLETED" />  	</intent-filter >  </receiver > 
发送自定义广播 发送标准广播 
定义一个广播接收器,准备接收自己的广播,新建一个MyBroadcastReceiver 
 
1 2 3 4 5 6 7 public class  MyBroadcastReceiver  extends  BroadcastReceiver       @Override      public void onReceive(Context  context, Intent  intent) {         Toast .makeText(context, "收到自己的广播了!" , Toast .LENGTH_SHORT ).show();         abortBroadcast();     } } 
1 2 3 4 5 6 <receiver  android:name =".MyBroadcastReceiver"  	android:enabled ="true"  	android:exported ="true" > 		<intent-filter >  			<action  android:name ="com.example.broadcasttest.MY_BROADCAST" />  </intent-filter > </receiver > 
这里让MyBroadcastReceiver接收一条com.example.broadcasttest.MY_BROADCAST的广播,也就是我们要发出的广播
1 2 3 4 5 <Button        android:id ="@+id/button"         android:layout_width ="match_parent"         android:layout_height ="match_parent"         android:text ="按钮" /> 
1 2 3 4 5 6 7 8 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); 		} }); 
为按钮添加事件,发送一个广播
发送有序广播 广播是跨进程的通信方式,创建一个BroadcastTest2项目
新建一个AnotherBroadcastReceiver继承自BroadcastReceiver 
 
1 2 3 4 5 6 public class  AnotherBroadcastReceiver  extends  BroadcastReceiver       @Override      public void onReceive(Context  context, Intent  intent) {         Toast .makeText(context, "在另外的软件里收到了广播" , Toast .LENGTH_SHORT ).show();     } } 
1 2 3 4 5 6 7 <receiver  android:name =".AnotherBroadcastReceiver"              android:enabled ="true"              android:exported ="true" >             <intent-filter >                  <action  android:name ="com.example.broadcasttest.MY_BROADCAST" />          </intent-filter >  </receiver > 
这里AnotherBroadcastReceiver同样接收com.example.broadcasttest.MY_BROADCAST广播
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class   extends  AppCompatActivity   	... 	@Override  	protected  void onCreate(Bundle  savedInstanceState) { 		super .onCreate(savedInstanceState); 		setContentView(R .layout.activity_main); 		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.MY_BROADCAST" ); 				sendOrderedBroadcast(intent, null ); 			} 		}); 	... 	} ... } 
发送有序广播只需要改动一行代码,即将sendBroadcast()方法改成sendOrderedBroadcast()方法。sendOrderedBroadcast()方法接收两个参数,第一个参数仍然是Intent,第二个参数是一个与权限相关的字符串,这里传入null就行了
修改AndroidManifest.xml中的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <manifest  xmlns:android ="http://schemas.android.com/apk/res/android"  package ="com.example.broadcasttest2" >	<application   		android:allowBackup ="true"  		android:icon ="@mipmap/ic_launcher"  		android:label ="@string/app_name"  		android:supportsRtl ="true"  		android:theme ="@style/AppTheme" > 	... 	<receiver   		android:name =".MyBroadcastReceiver"  		android:enabled ="true"  		android:exported ="true" > 		<intent-filter  android:priority ="100" >  		<action  android:name ="com.example.broadcasttest.MY_BROADCAST"  />  		</intent-filter >  	</receiver >  </application > </manifest > 
通过android:priority属性给广播接收器设定了优先级
修改MyBroadcastReceiver中的代码
1 2 3 4 5 6 7 8 public class  MyBroadcastReceiver  extends  BroadcastReceiver   	@Override  	public void onReceive(Context  context, Intent  intent) { 		Toast .makeText(context, "received in MyBroadcastReceiver" , 		Toast .LENGTH_SHORT ).show(); 		abortBroadcast(); 	} } 
这里表示将这条广播截断
使用本地广播 前面我们发送和接收的广播全部属于系统全局广播,即发出的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。
本地广播使用LocalBroadcastManager来管理广播
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 31 32 33 34 35 36 37 public  class  MainActivity extends AppCompatActivity {	private  IntentFilter intentFilter; 	private  LocalReceiver localReceiver; 	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  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(); 		localBroadcastManager.unregisterReceiver(localReceiver); 	} 	class  LocalReceiver extends BroadcastReceiver { 		@Override  		public  大专栏  第五章:详解广播机制 ="keyword">void  onReceive(Context context, Intent intent) { 		Toast.makeText(context, "received local broadcast" , Toast.LENGTH_SHORT). show(); 		} 	} } 
广播的最佳实践-实现强制下线功能 创建ActivityCollector类 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<>();          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();     } } 
创建BaseActivity类作为所有活动的父类 1 2 3 4 5 6 7 8 9 10 11 12 13 public class  BaseActivity  extends  AppCompatActivity       @Override      protected  void onCreate(Bundle  savedInstanceState) {         super .onCreate(savedInstanceState);         ActivityCollector .addActivity(this );     }     @Override      protected  void onDestroy() {         super .onDestroy();         ActivityCollector .removeActivity(this );     } } 
新建LoginActivity,这里选择空白活动 
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 <?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:orientation ="vertical"      tools:context =".LoginActivity" >     <LinearLayout           android:layout_width ="match_parent"          android:layout_height ="wrap_content"          android:orientation ="horizontal" >         <TextView               android:layout_marginLeft ="30dp"              android:layout_width ="wrap_content"              android:layout_height ="wrap_content"              android:text ="账号" />         <EditText               android:id ="@+id/EditText_UserName"              android:layout_width ="0dp"              android:layout_height ="wrap_content"              android:hint ="输入账号"              android:maxLines ="1"              android:layout_weight ="1"              android:layout_marginRight ="30dp" />     </LinearLayout >      <LinearLayout           android:layout_width ="match_parent"          android:layout_height ="wrap_content"          android:orientation ="horizontal" >         <TextView               android:layout_marginLeft ="30dp"              android:layout_width ="wrap_content"              android:layout_height ="wrap_content"              android:text ="密码" />         <EditText               android:id ="@+id/EditText_Password"              android:layout_width ="0dp"              android:layout_height ="wrap_content"              android:hint ="输入密码"              android:maxLines ="1"              android:layout_weight ="1"              android:inputType ="textPassword"              android:layout_marginRight ="30dp" />     </LinearLayout >      <LinearLayout           android:layout_width ="match_parent"          android:layout_height ="wrap_content"          android:gravity ="center" >         <Button               android:id ="@+id/Button_Login"              android:layout_width ="wrap_content"              android:layout_height ="wrap_content"              android:text ="登陆" />     </LinearLayout >  </LinearLayout > 
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 31 public class  LoginActivity  extends  BaseActivity       private  EditText  userNameEdit;     private  EditText  passwordEdit;     @Override      protected  void onCreate(Bundle  savedInstanceState) {         super .onCreate(savedInstanceState);         setContentView(R .layout.activity_login);         Button  loginButton=findViewById(R .id.Button_Login );         userNameEdit=findViewById(R .id.EditText_UserName );         passwordEdit=findViewById(R .id.EditText_Password );         loginButton.setOnClickListener(new  View .OnClickListener () {             @Override              public void onClick(View  v) {                 String  userName=userNameEdit.getText().toString();                 String  password=passwordEdit.getText().toString();                 if (userName.equals("admin" )){                     if (password.toString().equals("admin" )){                         Intent  intent=new  Intent (LoginActivity .this ,MainActivity .class );                         startActivity(intent);                     }                 }else {                     Toast .makeText(LoginActivity .this , "输入有误!" , Toast .LENGTH_SHORT ).show();                 }             }         });     } } 
修改MainActivity 
修改activity_main.xml的代码添加一个按钮 
修改MainActivity中的代码 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class   extends  BaseActivity       @Override      protected  void onCreate(Bundle  savedInstanceState) {         super .onCreate(savedInstanceState);         setContentView(R .layout.activity_main);         Button  button=findViewById(R .id.Button_Main );         button.setOnClickListener(new  View .OnClickListener () {             @Override              public void onClick(View  v) {                 Intent  intent=new  Intent ("com.zanecode6574.cquclass.broadcastpractice.FORCE_OFFLINE" );                 sendBroadcast(intent);             }         });     } } 
由于广播接收器里面需要弹出一个对话框来阻塞用户的正常操作,但如果创建的是一个静态注册的广播接收器,是没有办法在onReceive()方法里弹出对话框这样的UI控件的,而我们显然也不可能在每个活动中都去注册一个动态的广播接收器。
修改BaseActivity中的代码 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class  BaseActivity  extends  AppCompatActivity       private  ForceOfflineReceiver  receiver;     @Override      protected  void onCreate(Bundle  savedInstanceState) {         super .onCreate(savedInstanceState);         ActivityCollector .addActivity(this );     }     @Override      protected  void onResume() {         super .onResume();         IntentFilter  intentFilter=new  IntentFilter ();         intentFilter.addAction("com.zanecode6574.cquclass.broadcastpractice.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(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) {                     ActivityCollector .finishAll();                     Intent  intent=new  Intent (context,LoginActivity .class );                     context.startActivity(intent);                 }             });             builder.show();         }     } } 
onReceive()方法里使用AlertDialog.Builder来构建一个对话框,注意这里一定要调用setCancelable()方法将对话框设为不可取消,否则用户按一下Back键就可以关闭对话框继续使用程序了。然后使用setPositiveButton()方法来给对话框注册确定按钮,当用户点击了确定按钮时,就调用ActivityCollector的finishAll()方法来销毁掉所有活动,并重新启动LoginActivity这个活动。
修改AndroidManifest.xml 这里将主活动设置为LoginActivity即可
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 <?xml version="1.0" encoding="utf-8"?> <manifest  xmlns:android ="http://schemas.android.com/apk/res/android"      package ="com.zanecode6574.cquclass.broadcastpractice" >          <uses-permission  android:name ="android.permission.GET_ACCOUNTS"  />      <uses-permission  android:name ="android.permission.READ_PROFILE"  />      <uses-permission  android:name ="android.permission.READ_CONTACTS"  />      <application           android:allowBackup ="true"          android:icon ="@mipmap/ic_launcher"          android:label ="@string/app_name"          android:roundIcon ="@mipmap/ic_launcher_round"          android:supportsRtl ="true"          android:theme ="@style/AppTheme" >         <activity  android:name =".LoginActivity" >              <intent-filter >                  <action  android:name ="android.intent.action.MAIN"  />                  <category  android:name ="android.intent.category.LAUNCHER"  />              </intent-filter >          </activity >          <activity  android:name =".MainActivity" > </activity >      </application >  </manifest >