Android-App Components之BroadcastReceive

本知识点只是个人见解,具体知识及使用请查阅官网,以免被误导,同时大家可以对此文发表自己的见解。

App Components即android的组件,按照官网上面的API Guides来看,基础组件包括:

Intents and Intent Filters

Activities

Services

Content Providers

App Widgets

Processes and Threads

以上组件,组成了android的基础组件,了解可以完善我们的android知识体系

此处的知识点来自自己的笔记,如若有误请及时告知,此处只包含BroadcastReceive的知识点,后续会更新


BroadcastReceive基础知识点

Android应用程序可以从Android系统和其他Android应用程序发送或接收广播消息,类似于 发布-订阅 设计模式。

原理图:


两种注册方式

系统广播

系统在发生各种系统事件时自动发送广播,例如系统切入和切出飞行模式。系统广播被发送到订阅接收事件的所有应用程序。广播消息本身包装在一个Intent 对象中

系统广播的变化
Android 7.0及更高版本不再发送以下系统广播。此优化会影响所有应用,而不仅仅是针对Android 7.0的应用。
  • ACTION_NEW_PICTURE
  • ACTION_NEW_VIDEO
定位到Android 7.0(API级别24)及更高版本的应用必须通过以下方式注册以下广播registerReceiver(BroadcastReceiver, IntentFilter)。在清单中声明接收者不起作用。
  • CONNECTIVITY_ACTION
从Android 8.0(API级别26)开始,系统对清单声明的接收方施加额外的限制。如果您的应用定位到API级别26或更高,则无法使用清单来声明大多数隐式广播的接收方(专门针对您的应用的广播)

定位API级别26或更高级别的应用程序无法再在其清单中为隐式广播注册广播接收器。但是,目前有几种广播可以免除这些限制。无论应用目标的API级别如何,应用都可以继续为以下广播注册侦听器。

TION_LOCKED_BOOT_COMPLETEDACTION_BOOT_COMPLETED
由于这些广播只能在第一次启动时只发送一次,并且许多应用程序需要接收此广播以安排作业,警报等等,因此可以豁免。

ACTION_USER_INITIALIZE"android.intent.action.USER_ADDED""android.intent.action.USER_REMOVED"
这些广播受到特权许可的保护,因此大多数普通应用程序无论如何都无法接收它们。

"android.intent.action.TIME_SET"ACTION_TIMEZONE_CHANGEDACTION_NEXT_ALARM_CLOCK_CHANGED
时钟应用程序可能需要接收这些广播以在时间,时区或警报发生更改时更新警报。

ACTION_LOCALE_CHANGED
只有在语言环境发生变化时才会发送,这种情况并不常见。应用程序可能需要在区域设置更改时更新其数据。

ACTION_USB_ACCESSORY_ATTACHEDACTION_USB_ACCESSORY_DETACHEDACTION_USB_DEVICE_ATTACHEDACTION_USB_DEVICE_DETACHED
如果应用程序需要了解这些与USB相关的事件,目前还没有一个好的选择来注册广播。

ACTION_CONNECTION_STATE_CHANGEDACTION_CONNECTION_STATE_CHANGEDACTION_ACL_CONNECTEDACTION_ACL_DISCONNECTED
如果应用程序接收这些蓝牙事件的广播,用户体验不太可能受到影响。

ACTION_CARRIER_CONFIG_CHANGEDTelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED"TelephonyIntents.SECRET_CODE_ACTION"ACTION_PHONE_STATE_CHANGEDACTION_PHONE_ACCOUNT_REGISTEREDACTION_PHONE_ACCOUNT_UNREGISTERED
OEM电话应用程序可能需要接收这些广播。

LOGIN_ACCOUNTS_CHANGED_ACTION
某些应用程序需要知道登录帐户的更改,以便他们可以为新帐户和更改的帐户设置预定的操作。

ACTION_PACKAGE_DATA_CLEARED
只有在用户明确清除了“设置”中的数据时才会发送,因此广播接收器不会显着影响用户体验。

ACTION_PACKAGE_FULLY_REMOVED
某些应用程序可能需要在另一个包被移除时更新其存储的数据; 对于这些应用程序,没有好的选择来注册此广播。
注意: 其他与包相关的广播(例如 ACTION_PACKAGE_REPLACED 不受 新限制的限制。这些广播已经足够普遍,因此可能会对免除这些广播产生影响。

ACTION_NEW_OUTGOING_CALL
针对拨打电话的用户采取行动的应用程序需要接收此广播。

ACTION_DEVICE_OWNER_CHANGED
这个广播不经常发送; 一些应用程序需要接收它,以便他们知道设备的安全状态已更改。

ACTION_EVENT_REMINDER
日历提供者 发送以向日历应用发布事件提醒。由于日历提供程序不知道日历应用程序是什么,因此此广播必须是隐含的。

ACTION_MEDIA_MOUNTEDACTION_MEDIA_CHECKINGACTION_MEDIA_UNMOUNTEDACTION_MEDIA_EJECTACTION_MEDIA_UNMOUNTABLEACTION_MEDIA_REMOVEDACTION_MEDIA_BAD_REMOVAL
这些广播是由于用户与设备的物理交互(安装或移除存储卷)或作为引导初始化的一部分(因为可用卷被挂载)而发送的,所以它们不是常见的并且通常在用户的控制之下。

SMS_RECEIVED_ACTIONWAP_PUSH_RECEIVED_ACTION
这些广播是由SMS收件人应用程序所依赖的。

接收广播
应用程序可以通过两种方式接收广播:通过清单声明的接收器和上下文注册的接收器。

清单声明的接收者
如果您在清单中声明广播接收器,系统会在广播发送时启动您的应用程序(如果应用程序尚未运行)。意图过滤器指定您的接收器订阅的广播操作。
注意: 如果您的应用程序的目标API级别为26或更高,则不能使用清单来声明 隐式 广播的接收器(专门不针对您的应用程序的广播),但可以 免除该限制 的一些隐式广播除外。在大多数情况下,您可以使用 预定作业

上下文注册的接收器
系统创建一个新的 BroadcastReceiver 组件对象来处理它收到的每个广播。此对象仅在调用期间有效 onReceive(Context, Intent) 。一旦您的代码从此方法返回,系统会认为该组件不再处于活动状态。

onReceive(Context, Intent)
当BroadcastReceiver正在接收Intent广播时调用此方法,系统允许超时10秒

注意这句话的理解: If this BroadcastReceiver was launched through a <receiver> tag, then the object is no longer alive after returning from this function.
这意味着你不应该执行任何异步返回结果的操作。如果您需要执行任何跟进后台工作,使用 JobService 的  JobScheduler 。如果您希望与已在运行且以前使用过的服务进行交互,之前若使用 bindService() ,则 可以使用 peekService(Context, Intent)

对过程状态的影响
onReceive() ,系统可以随时终止进程以回收内存,并且这样做会终止进程中运行的衍生线程。为避免这种情况,您应该调用 goAsync() (如果您希望有更多时间在后台线程中处理广播)或者 JobService 使用接收者从接收者调度  JobScheduler ,因此系统知道该过程继续执行主动工作。

goAsync
由应用程序调用 onReceive(Context, Intent) 以允许它在从该函数返回后保持广播活动。
请记住,您在此处所做的工作将阻止进一步的广播直至完成,因此充分利用此功能可能会适得其反,并且会导致后续事件的收到速度更慢。
返回
BroadcastReceiver.PendingResult
返回 BroadcastReceiver.PendingResult 代表活动广播结果的a 。BroadcastRecord本身不再有效; 所有数据和其他交互都必须通过 BroadcastReceiver.PendingResult  API。 PendingResult.finish() 一旦广播处理完成,该方法必须被调用。

下面的代码片段显示了一个 BroadcastReceiver 使用 goAsync() 到它需要更多的时间来完成后旗 onReceive() 完成。如果你想在你的工作中完成的工作 onReceive() 足够长,导致UI线程错过一帧(> 16ms),这使得它更适合后台线程,这是特别有用的。

public class MyBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver" ;

    @Override
    public void onReceive ( final Context context , final Intent intent ) {
        final PendingResult pendingResult = goAsync ();
        AsyncTask < String , Integer , String > asyncTask = new AsyncTask < String , Integer , String >() {
            @Override
            protected String doInBackground ( String ... params ) {
                StringBuilder sb = new StringBuilder ();
                sb . append ( "Action: " + intent . getAction () + "\n" );
                sb . append ( "URI: " + intent . toUri ( Intent . URI_INTENT_SCHEME ). toString () + "\n" );
                Log . d ( TAG , log );
                // Must call finish() so the BroadcastReceiver can be recycled.
                pendingResult . finish ();
                return data ;
            }
        };
        asyncTask . execute ();
    }
}



发送广播
Android为应用程序发送广播提供了三种方式:
  • sendOrderedBroadcast(Intent, String) 方法一次向一个接收器发送广播。当每个接收器依次执行时,它可以将结果传播到下一个接收器,或者它可以完全中止广播,使其不会传递给其他接收器。运行的订单接收器可以通过匹配意图过滤器的android:priority属性进行控制; 具有相同优先级的接收器将以任意顺序运行。
  • 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 );
要接收广播,接收应用程序必须请求权限,如下所示:
<uses-permission android:name = "android.permission.SEND_SMS" />
您可以指定现有的系统权限 SEND_SMS 或使用该 <permission> 元素定义自定义权限 。有关一般权限和安全性的信息,请参阅 系统权限
注意: 自定义权限是在安装应用程序时注册的。定义自定义权限的应用程序必须安装在使用它的应用程序之前。

接收权限
如果您在注册广播接收机时指定一个权限参数(无论是在清单 registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) 中的 <receiver> 标签还是在 标签中),那么只有 <uses-permission> 在其清单中通过标签请求权限的广播公司 (如果它是危险的,随后被授予权限)可以发送接收者的意图。
例如,假设您的接收应用程序具有清单声明的接收方,如下所示:
<receiver android:name = ".MyBroadcastReceiver"
          android:permission = "android.permission.SEND_SMS" >
    <intent-filter>
        <action android:name = "android.intent.action.AIRPLANE_MODE" />
    </intent-filter>
</receiver>
或者您的接收应用程序有一个上下文注册的接收器,如下所示:
IntentFilter filter = new IntentFilter ( Intent . ACTION_AIRPLANE_MODE_CHANGED );
registerReceiver ( receiver , filter , Manifest . permission . SEND_SMS , null );
然后,为了能够向这些接收者发送广播,发送应用程序必须请求权限,如下所示:
<uses-permission android:name = "android.permission.SEND_SMS" />

安全考虑和最佳实践
以下是发送和接收广播的一些安全考虑事项和最佳做法:
  • 如果您不需要将广播发送到您的应用以外的组件,则LocalBroadcastManager可以使用支持库中的可用 广播发送和接收本地广播。的LocalBroadcastManager效率要高得多(不需要进程间通信),并可以让你避免考虑与其他应用程序能够接收或发送你的广播任何安全问题。本地广播可以在您的应用程序中用作通用的发布/订阅事件总线,而无需系统广播的任何开销。
  • 如果许多应用程序已注册接收清单中的相同广播,则可能会导致系统启动大量应用程序,从而对设备性能和用户体验产生重大影响。为了避免这种情况,优先使用清单声明上的注册。有时,Android系统本身会强制使用上下文注册的接收器。例如,CONNECTIVITY_ACTION广播仅传送给上下文注册的接收器。
  • 不要使用隐含的意图广播敏感信息。任何注册的应用程序都可以读取信息以接收广播。有三种方法可以控制谁可以接收您的广播:
  • 您可以在发送广播时指定权限。
  • 在Android 4.0及更高版本,可以指定一个 与 setPackage(String)发送广播时。系统将广播限制为与包匹配的一组应用程序。
  • 您可以发送本地广播LocalBroadcastManager
  • 当您注册接收器时,任何应用程序都可能将潜在的恶意广播发送到您应用的接收器。有三种方法可以限制您的应用收到的广播:
  • 您可以在注册广播接收机时指定权限。
  • 对于清单声明的接收者,您可以在清单中将 android:exported 属性设置为“false”。接收器不接收来自应用程序外部的广播。
  • 你可以限制自己只有本地广播LocalBroadcastManager
  • 广播操作的命名空间是全局的。确保动作名称和其他字符串被写入您拥有的名称空间中,否则您可能会无意中与其他应用程序发生冲突。
  • 因为接收者的onReceive(Context, Intent)方法在主线程上运行,所以它应该快速执行并返回。如果您需要执行长时间运行的工作,请注意产卵线程或启动后台服务,因为系统可能会在onReceive()返回后终止整个进程 。有关更多信息,请参阅对进程状态的影响要执行长时间运行的工作,我们建议:
  • 调用goAsync()接收者的onReceive()方法并传递BroadcastReceiver.PendingResult给后台线程。这使广播从返回后保持活动状态onReceive()。但是,即使采用这种方法,系统也希望您能够很快完成广播(不到10秒)。它确实允许您将工作移至另一个线程以避免妨碍主线程。
  • JobScheduler。安排工作。有关更多信息,请参阅智能作业计划
  • 不要从广播接收机开始活动,因为用户体验很刺耳; 特别是如果有多个接收器的话。相反,请考虑显示通知

参考自:

猜你喜欢

转载自blog.csdn.net/yang1349day/article/details/80107895