《第一行代码》广播

Alexlee 2018-04-20

10、广播接收器(Broadcast Receiver)

  Android 中的广播主要可以分为两种类型,标准广播和有序广播

  标准广播(Normal broadcasts):

  是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言

  有序广播(Ordered broadcasts):

  是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播

  注册广播的方式一般有两种,在代码中注册和在 AndroidManifest.xml 中注册,其中前者也被称为动态注册,后者也被称为静态注册

创建一个广播接收器:

  新建一个类,让它继承自 BroadcastReceiver,并重写父类的 onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行

  动态注册:

    因为逻辑都是写在onCreate()中,所以必须要在程序启动之后才能接收到广播

    动态注册的广播接收器一定都要取消注册才行,这里我们是在 onDestroy()方法中通过调用 unregisterReceiver()方法来实现的

class NetworkChangeReceiver extends BroadcastReceiver {

			@Override
			public void onReceive(Context context, Intent intent) {
				Toast.makeText(context, "network changes",Toast.LENGTH_SHORT).show();
			}
		}
//MainActivity:

		protected void onCreate(Bundle savedInstanceState) {
			...
			//当网络状态发生变化时,系统发出的正是一条值为 android.net.conn.CONNECTIVITY_CHANGE 的广播

			intentFilter = new IntentFilter();	
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

			//接收器
			networkChangeReceiver = new NetworkChangeReceiver();
			registerReceiver(networkChangeReceiver, intentFilter);
		}
			

		@Override
		protected void onDestroy() {
			super.onDestroy();
			unregisterReceiver(networkChangeReceiver);
		}

		class NetworkChangeReceiver extends BroadcastReceiver {

		}

 打 开AndroidManifest.xml 文件,在里面加入如下权限

  

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

  

静态注册:

  可以让程序在未启动的情况下就能接收到广播

    新建一个 BootCompleteReceiver 继承自BroadcastReceiver:

public class BootCompleteReceiver extends BroadcastReceiver {

			public void onReceive(Context context, Intent intent) {
				Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
			}
		}

 在 AndroidManifest.xml中添加:

  

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

		<application
			……			

			<receiver android:name=".BootCompleteReceiver" >
				<intent-filter>
					<action android:name="android.intent.action.BOOT_COMPLETED" />
				</intent-filter>
			</receiver>
		</application>

发送自定义广播

发送标准广播:

  1、新建一个广播接收器

  新建一个 MyBroadcastReceiver继承自 BroadcastReceiver

  2、在 AndroidManifest.xml 中对这个广播接收器进行注册

<receiver android:name=".MyBroadcastReceiver">

		<intent-filter>
			<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
		</intent-filter>
	</receiver>

  

  3、定义按钮发送广播

public void onClick(View v) {
		Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
		sendBroadcast(intent);
	}

发送有序广播:

  1~2跟标准广播一样

  4、

Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
sendOrderedBroadcast(intent, null);

  sendOrderedBroadcast()方法接收两个参数,第一个参数仍然是Intent,第二个参数是一个与权限相关的字符串,这里传入 null 就行了

  android:priority 属性给广播接收器设置了优先级,优先级比较高的广播接收器就可以先收到广播

<receiver android:name=".MyBroadcastReceiver">

		<intent-filter android:priority="100" >
			<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
		</intent-filter>
	</receiver>

  abortBroadcast()方法,就表示将这条广播截断,在广播接收器中使用

使用本地广播:

  前面我们发送和接收的广播全部都是属于系统全局广播,

  即发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播

  本地广播的用法并不复杂,主要就是使用了一个 LocalBroadcastManager 来对广播进行管理

1、接收器

class LocalReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			Toast.makeText(context, "received local broadcast",Toast.LENGTH_SHORT).show();
		}
	}

2、

public class MainActivity extends Activity {

		private IntentFilter intentFilter;
		private LocalReceiver localReceiver;
		private LocalBroadcastManager localBroadcastManager;
		

		@Override
		protected void onCreate(Bundle savedInstanceState) {
			。。。
			//获取localBroadcastManager
			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);
		}

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

原理:

点击强制下线按钮,发送一条”强制下线“的广播,在广播接收器里弹出一个不可取消的对话框,点击对话框确定按钮,调用 ActivityCollector 的 finishAll()方法来销毁掉所有活动,并重新启动 LoginActivity 这个活动

强制用户下线的逻辑并不是写在 MainActivity 里的,而是应该写在接收这条广播的广播接收器里面这样强制下线的功能就不会依附于任何的界面,不管是在程序的任何地方,只需要发出这样一条广播,就可以完成强制下线的操作了。

注意:

由于我们是在广播接收器里启动活动的,因此一定要给Intent 加入 FLAG_ACTIVITY_NEW_TASK 这个标志。

还需要把对话框的类型设为 TYPE_SYSTEM_ALERT,不然它将无法在广播接收器里弹出

1、发送广播:

public void onClick(View v) {
      Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE ");
      sendBroadcast(intent);
}

  

2、接收广播:

public class ForceOfflineReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(final Context context, Intent intent) {
        Toast.makeText(context, "received in ForceOfflineReceiver", Toast.LENGTH_SHORT).show();


        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
        dialogBuilder.setTitle("Warning");
        dialogBuilder.setMessage("You are forced to be offline. Please try to login again.");
        dialogBuilder.setCancelable(false);

        //点击对话框OK
        dialogBuilder.setPositiveButton("OK",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCollector.finishAll(); // 销毁所有活动
                        Intent intent = new Intent(context,MainActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        context.startActivity(intent); // 重新启动LoginActivity
                    }
                });

        AlertDialog alertDialog = dialogBuilder.create();
        // 需要设置AlertDialog的类型,保证在广播接收器中可以正常弹出
        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST);
        alertDialog.show();

       // context.unregisterReceiver(this);
    }
}

 

3、注册广播接收器,声明权限 android.permission.SYSTEM_ALERT_WINDOW 

问题:

1、Broadcast的onReceive方法中弹出AlertDialog

使用静态注册广播,只能弹出System Alert类型的Dialog。但google在 6.0加入了运行时权限的概念,需要在java代码中进行动态申请。为了防止旧的应用程序崩溃,只对targetSDK为23及以上的程序使用新的权限机制。 

相应的解决方法有如下几种。 

第一种方式是:targetSDK设为23以下就可以规避问题。 

第二种方式是:设置里面给了这个调试应用【允许悬浮窗】 但是得手动的打开(我是没有找到这个悬浮窗到底在哪里)。 

第三种方式是:使用无需权限显示悬浮窗。

a 修改广播接收类中的import包 将

import android.support.v7.app.AlertDialog;

修改为:

import android.app.AlertDialog;

b 修改AlertDialog的弹窗类型为:TYPE_TOAST 将

alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

修改为:

alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST);

这样编译运行就可以正常弹出AlertDialog了。

2、在广播中弹出对话框与在Activiity中弹出对话框有所不同,在Activiity中弹出对话框,我们需要用到当前Activiity的Context,而在广播中并没有,如果一定要在广播中弹出一个对话框,我需要定义这个对话框是一个系统级别的。

首先需要有弹出系统对话框的权限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

在show()之前,要先设置Dialog的类型为TYPE_SYSTEM_ALERT。

alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

最后,最最重要的,AlertDialog要使用android.app.AlertDialog,不要用v7包下的Dialog,会报错。

相关推荐