1.广播
Android应用可以发送或者接收来自系统或者其他应用的广播信息,这种模式神似设计模式中的观察者模式。
- Android系统广播消息:
当各种系统事件发生时,例如系统启动或者设备开始充电。 - 应用程序还可以发送自定义广播,以通知其他应用。例如一些数据已经下载完毕。
应用程序可以注册接收特定的广播。当发送广播时,系统自动将广播路由到已订阅接收该特定类型的广播的应用。
2.接收广播
应用程序可以通过以下两种方式接收广播:通过清单声明接收器和上下文注册接收器。
i.通过清单声明的接收器
a)在androidmanifest中声明:
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
b).实现BroadcastReceiver的 onReceive(Context, Intent):
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
StringBuilder sb = new StringBuilder();
sb.append("Action: " + intent.getAction() + "\n");
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
String log = sb.toString();
Log.d(TAG, log);
Toast.makeText(context, log, Toast.LENGTH_LONG).show();
}
}
安装应用程序时,系统软件包管理器会注册接收器。接收器然后成为到应用程序的单独入口点,这意味着如果应用程序当前未运行,系统可以启动应用程序,并且传递广播。
系统创建一个新的BroadcastReceiver组件对象来处理它接收的每个广播。此对象仅在调用的持续时间内有效onReceive(Context, Intent)。一旦代码从此方法返回,系统将认为组件不再处于活动状态。
ii.上下文注册的接收器
a).实例化一个广播接收者:
BroadcastReceiver br = new MyBroadcastReceiver ();
b)实例化一个IntentFilter 对象然后调用 registerReceiver(BroadcastReceiver, IntentFilter)函数:
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
this.registerReceiver(br, filter);
上下文注册的接收器接收广播,只要其注册上下文有效即可。例如,如果在Activity上下文中注册 ,只要Activity未被销毁,就会收到广播。如果使用应用程序上下文注册,只要应用程序正在运行,就会收到广播。
c)如果要停止接收广播,调用unregisterReceiver(android.content.BroadcastReceiver)。
确保不再需要接收器或上下文不再有效的时候取消注册接收器。
3.发送广播
Android提供了三种应用程序发送广播的方式:
- sendOrderedBroadcast(Intent, String) 方法一次向一个接收器发送广播。随着每个接收器依次执行,它可以将结果传播到下一个接收器,或者它可以完全中止广播,使得它不会被传递给其他接收器。命令接收器运行可以用匹配的 intent-filter的android:priority属性控制; 具有相同优先级的接收器将以任意顺序运行。
- sendBroadcast(Intent)方法以未定义的顺序向所有接收机发送广播。这称为正常广播。这是更有效的,但意味着接收机不能从其他接收机读取结果,传播从广播接收的数据,或中止广播。
- LocalBroadcastManager.sendBroadcast方法向与发送者在同一应用程序中的接收者发送广播。如果您不需要通过应用程序发送广播,请使用本地广播。实现更高效(无需进程间通信),您不需要担心与其他应用程序相关的任何安全问题能够接收或发送您的广播。
以下代码段演示了如何通过创建Intent和调用来发送广播sendBroadcast(Intent)。
Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice me senpai!");
sendBroadcast(intent);
4.需要的权限
调用sendBroadcast(Intent, String)或 sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)时,可以指定权限参数。只有接收者请求的许可与标签正确才可以接收广播。
例如,以下代码发送广播:
sendBroadcast(new Intent("com.example.NOTIFY"),
Manifest.permission.SEND_SMS);
要接收广播,接收应用程序必须请求权限,如下所示:
<uses-permission android:name="android.permission.SEND_SMS"/>
5.注意事项
如果不需要向应用程序以外的组件发送广播,请使用支持库中的LocalBroadcastManager 发送和接收本地广播。LocalBroadcastManager更高效(无需进程间通信),并且允许避免考虑与其他应用程序相关的任何安全问题能够接收或发送广播。本地广播可以在应用程序中用作通用pub / sub事件总线,没有系统范围广播的任何开销。
如果许多应用在Androidmanifest中注册了相同的广播,这可能会到导致系统启动大量的程序,从而度设备性能和用户体验造成极大影响。为了避免这种情况的发生,优先使用上下文注册。
不要使用隐式intent来广播敏感信息。有三种方法可以限制那些广播接收者可以接收你的广播:
- 在发送广播前指定权限;
- 在Android4.0以上,可以通过setPackage(String)指定接收者;
- 使用LocalBroadcastManager发送本地广播;
- 任何应用程序都有可能将恶意广播传送到你的应用上。可以通过以下三种方法限制自己应该接收到的广播:
- 在注册广播接收者的时候指定权限;
- 对于使用清单声明的接收者,可以将清单中的 android:exported 属性设置为“false”。接收器不接收来自应用程序外部的广播。
- 使用 LocalBroadcastManager限制自己只接收本地广播;
广播操作的命名空间是全局的。确保操作名称和其他字符串写在您拥有的命名空间中,否则您可能会无意中与其他应用程序冲突;
因为接收者的onReceive(Context, Intent)方法在主线程上运行,它应该快速执行和返回。如果您需要执行长时间运行的工作,请小心产生线程或启动后台服务,因为系统可以在onReceive()返回后终止整个进程 。
不要从广播接收者启动activity,这样会造成很差的用户体验。
6.一个简单的自定义广播发送与接收的例子
应用A给用户B广播一条消息,应用B接收到消息之后Toast出来
i.应用A的代码
MainActivity
public class MainActivity extends AppCompatActivity {
private EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText) findViewById(R.id.editText);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.geekp.broadcastsender.MY_BROADCAST");
intent.putExtra("data", editText.getText().toString());
sendBroadcast(intent);
}
});
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.geekp.broadcastsender.MainActivity">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="Name"
android:ems="10"
android:id="@+id/editText"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="58dp"
android:layout_marginEnd="58dp"
android:layout_marginTop="38dp" />
<Button
android:text="发送广播"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="85dp"
android:id="@+id/button"
android:layout_marginLeft="15dp"
android:layout_marginStart="15dp"
android:layout_below="@+id/editText"
android:layout_alignLeft="@+id/editText"
android:layout_alignStart="@+id/editText" />
</RelativeLayout>
ii.应用B的代码
mainactivity
package com.example.geekp.broadcastrs;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private BReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
//想接收什么广播就在这里向相应的action
intentFilter.addAction("com.example.geekp.broadcastsender.MY_BROADCAST");
receiver = new BReceiver();
registerReceiver(receiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
class BReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "广播接收程序接收到消息啦!!!!!,消息是: "+intent.getStringExtra("data"), Toast.LENGTH_SHORT).show();
}
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.geekp.broadcastrs.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是广播接收者!" />
</RelativeLayout>
iii.效果图