Context:
BroadcastReceiver:
1. Android 7.0 (API level 24) and higher don't send the following system broadcasts:
ACTION_NEW_PICTURE
ACTION_NEW_VIDEO
Also, apps targeting Android 7.0 and higher must register the CONNECTIVITY_ACTION
broadcast using registerReceiver(BroadcastReceiver, IntentFilter)
. Declaring a receiver in the manifest doesn't work.
也就是说7.0及以上系统不再发送ACTION_NEW_PICTURE和ACTION_NEW_VIDEO广播,而且注册
CONNECTIVITY_ACTION广播必须动态注册
2.
Beginning with Android 8.0 (API level 26), the system imposes additional restrictions on manifest-declared receivers.
If your app targets Android 8.0 or higher, you cannot use the manifest to declare a receiver for most implicit broadcasts (broadcasts that don't target your app specifically). You can still use a context-registered receiver when the user is actively using your app.
从8.0开始,不能用静态注册的方式注册隐式广播接收器
3.Beginning with Android 9 (API level 28), The NETWORK_STATE_CHANGED_ACTION
broadcast doesn't receive information about the user's location or personally identifiable data.
In addition, if your app is installed on a device running Android 9 or higher, system broadcasts from Wi-Fi don't contain SSIDs, BSSIDs, connection information, or scan results. To get this information, call getConnectionInfo()
instead.
从9.0开始, NETWORK_STATE_CHANGED_ACTION这个广播中的信息不再包含用户的位置和一些个人信息
package com.example.broadcastreceivertest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class MyReceiver extends BroadcastReceiver {
public MyReceiver() {
Log.i("TAG", "MyReceiver: ");
}
@Override
public void onReceive(Context context, Intent intent) {
Log.i("TAG", "onReceive: ");
Log.i("TAG", "data: " + intent.getStringExtra("data"));
}
}
BroadcastReceiver的状态影响其所在进程的状态,进而影响这个进程被系统杀死的可能性。例如,一个进程包含一个BroadcastReceiver,并且这个BroadcastReceiver的onReceive()方法正在执行,那么这个进程就被认为是一个前台进程,除了内存极度缺乏的情况下,这个进程将保持存活。
然而一旦这个BroadcastReceiver的onReceive()方法执行完毕,那么这个BroadcastReceiver就不再活跃了,持有这个reveiver的进程的优先级就依赖于其他组件了(如activity,service等)。如果这个进程仅有一个静态注册的receiver,并且用户没有与这个进程进行交互或者最近没有与这个进程进行交互,那么系统就会认为这个进程的优先级比较低,可能会杀死这个进程,腾出资源为其他更重要的进程提供服务。
因此,不应该在onReceive()方法中开启一个分线程去执行长时间的后台任务,系统随时可能杀死这个进程,并且结束掉依赖于这个进程的线程。为了避免这种情况,在需要更多时间去处理任务的时候,可以使用goAsync()或者使用JobScheduler从接收器调度JobService,以便系统知道该进程继续执行活动 工作。
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
final PendingResult pendingResult = goAsync();
Task asyncTask = new Task(pendingResult, intent);
asyncTask.execute();
}
private static class Task extends AsyncTask {
private final PendingResult pendingResult;
private final Intent intent;
private Task(PendingResult pendingResult, Intent intent) {
this.pendingResult = pendingResult;
this.intent = intent;
}
@Override
protected String doInBackground(String... strings) {
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);
return log;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// Must call finish() so the BroadcastReceiver can be recycled.
pendingResult.finish();
}
}
}
8.0以后这样注册隐式广播接收不到消息
IntentFilter intentFilter = new IntentFilter("android.intent.action.test_data");
if (myReceiver == null) {
myReceiver = new MyReceiver();
}
registerReceiver(myReceiver, intentFilter);
动态注册的广播必须在context(比如activity)死亡前反注册,否则应用会crash,如果使用applicationContext注册,则广播接收器的生命周期就是app存活的周期
@Override
protected void onDestroy() {
unregisterReceiver(myReceiver);
super.onDestroy();
}
Intent intent = new Intent("android.intent.action.test_data");
intent.putExtra("data","这是我的测试数据");
sendBroadcast(intent);
sendBroadcast(Intent)方法发送的广播是不带有顺序的,几乎所有有效注册了的广播接收器都能收到此广播,也被称为正常广播。
Intent intent = new Intent("android.intent.action.test_data");
intent.putExtra("data","这是我的测试数据");
sendBroadcast(intent);
sendOrderedBroadcast(Intent,String)方法一次向一个接收器发送广播。 当每个接收器依次执行时,它可以将结果传播到下一个接收器,或者它可以完全中止广播,以便它不会传递给其他接收器。 可以使用匹配的intent-filter的android:priority属性来控制运行的订单接收器;priority的值越大优先级越高, 具有相同优先级的接收器将以任意顺序运行。
Intent intent = new Intent("android.intent.action.test_data");
intent.putExtra("data","这是我的测试数据");
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent);
LocalBroadcastManager.sendBroadcast方法将广播发送到与发送方位于同一应用程序中的接收方。 如果不需要跨应用程序发送广播,最好使用本地广播。 实现效率更高(无需进程间通信),也不需要担心与其他应用程序能够接收或发送广播相关的任何安全问题。
权限允许将广播限制为具有特定权限的应用程序集, 可以对广播的发送者或接收者实施限制。
当调用sendBroadcast(Intent,String)或sendOrderedBroadcast(Intent,String,BroadcastReceiver,Handler,int,String,Bundle)时,您可以指定权限参数。 只有那些已经在其清单文件中申请了权限的接收者(并且如果它是危险的,则随后需要动态授权)可以接收广播。 例如,以下代码发送广播:
sendBroadcast(new Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS);
要接收广播,接收应用必须请求权限,如下所示:
可以指定现有系统权限(如SEND_SMS)或使用
注意:安装应用程序时会注册自定义权限。 必须在使用该应用程序之前安装定义自定义权限的应用程序。
如果在注册广播接收器时指定了权限参数(使用registerReceiver(BroadcastReceiver,IntentFilter,String,Handler)或清单文件中的
例如,接收应用程序具有清单声明的接收器,如下所示:
或者接收应用程序有像如下方式动态注册广播接收器:
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );
然后,为了能够向这些接收者发送广播,发送应用必须请求许可,如下所示:
也就是说无论是发送方还是接收方都必须像上面的代码这种方式,在清单文件中授权权限才能正常收发广播,如果是危险权限还需要动态授予才行。
不要使用隐式意图广播敏感信息。任何注册广播接收器的应用都可以读取该信息。有三种方法可以控制谁可以接收您的广播:
1. 您可以在发送广播时指定权限。
2. 在Android 4.0及更高版本中,您可以在发送广播时指定包含setPackage(String)的包。系统将广播限制为与包匹配的应用程序集。
3. 您可以使用LocalBroadcastManager发送本地广播。
当您注册接收器时,任何应用都可以向您的应用接收器发送潜在的恶意广播。有三种方法可以限制应用收到的广播:
1. 您可以在注册广播接收器时指定权限。
2. 对于清单声明的接收器,您可以在清单中将android:exported属性设置为“false”。接收方不接收来自应用程序之外的来源的广播。
3. 您可以将自己限制为仅使用LocalBroadcastManager进行本地广播。
广播操作的命名空间是全局的。确保操作名称和其他字符串都写在您拥有的命名空间中,否则您可能会无意中与其他应用程序发生冲突
因为接收者的onReceive(Context,Intent)方法在主线程上运行,所以它应该快速执行并返回。如果需要执行长时间运行的工作,请注意生成线程或启动后台服务,因为系统可能会在onReceive()返回后终止整个进程。
建议在接收者的onReceive()方法中调用goAsync()并将BroadcastReceiver.PendingResult传递给后台线程。这使得从onReceive()返回后广播保持活动状态。但是,即使采用这种方法,系统也希望您能够非常快速地完成广播(10秒以内)。它允许您将工作移动到另一个线程,以避免故障主线程。