Android官方文档翻译-Broadcasts

Android官方文档翻译-Broadcasts原文链接:https://developer.android.com/guide/components/broadcasts.html广播Android应用可以向Android系统和其他Android应用发送或从它们那接收广播消息,这类似于发布-订阅设计模式。当需要关注的事件发生时这些广播就会发送出去。例如Android系统在许多系统事件发生时会发送广播,比如当系统启动或设备开始充电时。应用也可以发

大家好,又见面了,我是你们的朋友全栈君。

原文链接:https://developer.android.com/guide/components/broadcasts.html

广播

Android应用可以向Android系统和其他Android应用发送或从它们那接收广播消息,这类似于发布-订阅设计模式。当需要关注的事件发生时这些广播就会发送出去。例如Android系统在许多系统事件发生时会发送广播,比如当系统启动或设备开始充电时。应用也可以发送自定义广播,例如通知其他APP他们关注的某些事(例如,下载了一些新数据)。

应用可以通过注册接收特定的一些广播。当广播发送出去,系统会自动将广播路由(routes)到订阅了该特定类型广播的应用程序。
一般来说,广播可以用作应用程序和非常规用户流之间的消息传递系统。但是,你必须注意不要滥用这个响应广播的机会,在后台运行一些会导致系统变慢的任务,正如下面这个视频描述的(译者注:略)。

系统广播

系统在许多系统事件发生时会自动发送广播,比如,当系统打开或关闭飞行模式。系统广播会发送给所有订阅了该事件的应用程序。
广播消息本身封装在一个Intent对象中,它的action字符串区分发生的事件(例如android.intent.action.AIRPLANE_MODE)。intent可能也包含其他包裹在扩展字段(extra field)中的信息。例如,飞行模式的itent包含一个boolean的扩展,它表明了飞行模式是否打开。
更多关于如何解读intents和从intent中获取action字符串,参见Intents and Intent Filterslink.
对于一个完整的系统广播action列表,参见Android SDK里的BROADCAST_ACTIONS.TXT文件。每个广播action有一个与之关联的常量字段。例如常量ACTION_AIRPLANE_MODE_CHANGED的值是android.intent.action.AIRPLANE_MODE。每个广播action的文档在其关联的常量字段中是可用的。

系统广播的变化

Android7.0以上不再发送以下系统广播。这项优化影响所有应用程序,不只针对那些Android7.0.
+ ACTION_NEW_PICTURE
+ ACTION_NEW_VIDEO

面向Android7.0以上的应用程序,下面这条广播必须使用registerReceiver(BroadcastReceiver, IntentFilter)注册,在manifest声明无效。
+ CONNECTIVITY_ACTION

从Android8.0(API 26)开始,系统对manifest声明的广播施加了其他限制。如果你的APP面向API26以上版本,对于大多数隐式广播(不是专门针对你的应用的广播),你不能使用manifest声明接收者。

接收广播

应用程序接收广播有两种途径:通过manifest声明接收者和context注册接收者。

Manifest声明的接收者

如果你在manifest中声明了一个广播接收者,系统会在该广播发送时启动你的应用(如果你的应用还没有运行).

注意:如果你的APP面向API26以上,你不能使用manifest为隐式广播(不是专门针对你的应用的广播)声明接收,除了一些不受限制的隐式广播。在大多数情况下,你可以计划任务代替。

按如下步骤在manifest中声明广播接收者:
1. 在你的应用的manifest中定义单元.

    <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>
  1. 定义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)期间有效。一旦你的代码从该方法返回,系统就认为该组件不再活跃。

经Context注册的接收者

按如下步骤使用context注册接收者:
1. 创建[BroadcastReceiver](https://developer.android.com/reference/android/content
/BroadcastReceiver.html)的一个实例。

 BroadcastReceiver br = new MyBroadcastReceiver();

2. 创建一个IntentFilter,然后通过调用registerReceiver(BroadcastReceiver, IntentFilter)注册接收者。

    IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
    intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    this.registerReceiver(br, filter);

注意:要注册本地广播,调用LocalBroadcastManager.registerReceiver(BroadcastReceiver, IntentFilter) 代替。

Context注册的接收者在他们注册用的context有效期间接受广播。例如,如果在Activity context中注册,Activity没有销毁时你可以接收广播。如果你用Application context注册,你在整个应用运行期间可以接收广播。

3. 要停止接收广播,调用unregisterReceiver(android.content.BroadcastReceiver)。请确保在你不再需要它或者context不再有效的时候注销接收者。
注意你注册和注销接收者的地方,例如如果你使用activity的context在onCreate(Bundle)中注册接收者,你应该在onDestroy()中注销以防止将接收者泄漏在activity context之外。如果你在onResume()中注册接收者,你应该在onPause()中注销它以防止重复注册(如果你不想在paused时接收广播,这可以减少不必要的系统开销)。不要在onSaveInstanceState(Bundle)中注销,因为如果用户从历史栈中返回,该方法不会调用。

对进程状态的影响

广告接收者的状态(不管他有没有在运行)对它所在进程的状态有影响,这反过来又会影响它被系统杀死的可能性。例如,进程执行接收者(即,正在运行onReceive()中的代码时),它被视为一个前台进程。系统保持进程运行,除非在极度的内存压力情形下。
但是一旦你的代码重onReceive()返回,广播接收者就不再活跃。接收者的宿主进程变得和正在其中运行的其他app组件一样重要( The receiver’s host process becomes only as important as the other app components that are running in it).如果那个进程只持有一个manifest声明的接收者(应用程序的常见情形,用户从未或最近未与之交互),那么当从onReceive()返回时,系统认为它的进程是一个低优先级的进程,并且可能会将其杀死,以便为其他更重要的进程提供资源。
由于这个原因,你不应该在广播接收者中开启长时间运行的后台线程。onReceiver()之后,系统可能在任何时候杀死进程回收内存,并且在这过程中,他会终止运行在该进程中的线程。为了避免这样,你应该调用goAsync()(如果你想在后台线程中多花点时间处理处理广播)或者使用JobScheduler从接收者中安排一个JobService,这样系统知道该进程在继续执行积极的工作。更多信息请查看Processes and Application LifeCycle.

下面这个片段展示了一个使用goAsync()BroadcastReceiver.它表明在onReceive()完成之后,它需要更多时间去完成工作。这在onReceive()中你要完成的工作时间长到足以引发UI线程从框架中断开(miss a frame >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)方法发送广播一次给一个接收者。由于每个接收者按顺序执行,它可以将一个结果传递给下一位接收者,或者完全废弃这个广播,从而不会传给其他接收者。接收者的运行顺序可以通过对应的intent-filter的android:priority属性控制;相同优先级的接收者将以任意顺序运行。
+ sendBroadcast(Intent)方法以无序的形式给所有接收者发送广播。这称为普通广播。这效率更高,但意味着接收者不能从其他接收者那读取结果,传递从广播接收的数据,或者废弃广播。
+ LocalBroadcastManager.sendBroadcast方法发送广播给与发送者同一APP中的接收者。如果你不需要跨越APP发送广播,可使用本地广播。该实现更加高效,并且你无需担心由于其他app可以接收或发送你的广播而引起的安全问题。
下面的代码片段描述了怎样通过创建intent和调用sendBroadcast(Intent)发送一个广播。

Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice me senpai!");
sendBroadcast(intent);

广播消息封装在Intent对象中。intent的action字符串必须提供app的java包名语法,并唯一标志广播事件。你可以使用putExtra(String, Bundle)给intent附加其他信息。你也可以通过在intent上调用setPackage(String)将广播限制在同一组织的app集合。

注意:虽然intents同时用于发送广播和使用startActivity(Intent)启动activity,但这些action完全无关。广播接收者无法看到货捕捉启动一个activity的intent;同样地,当你广播一个intent,你无法找到或者无法启动一个activity.

使用权限限制广播

权限允许你将广播限制在持有特定权限的app集合。你可以对广播的发送者和接收者实施限制。

使用权限发送

当你调用sendBroadcast(Intent,String) 或者 sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle),你可以指定一个权限参数。只有使用该标签在manifest中申请了该权限(如果是危险权限,还要经过允许)的接收者可以接收该广播,例如,下面的代码发送一条广播:

sendBroadcast(new Intent("com.example.NOTIFY"),Manifest.permission.SEND_SMS);

要接收该广播,接收方app必须像下面这样申请该权限:

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

你可以指定像SEND_SMS已经存在的系统权限或者使用自定义一个权限。有关权限和一般安全,请查看System Permissions.

注意:自定义权限在app安装时注册。定义自定义权限的app必须在使用它的app之前安装。

使用权限接收

如果你在注册广播接收者时指定了一个权限参数(registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)或manifest里的标签),那么只有在manifest中使用标签请求了该权限(如果是危险权限还要经过允许)的广播才能发送intent到该接收者。
例如,假设你的接收者app有一个manifest声明的接收者如下所示:

<receiver android:name=".MyBroadcastReceiver" android:permission="android.permission.SEND_SMS">
    <intent-filter>
        <action android:name="android.intent.action.AIRPLANE_MODE"/>
    </intent-filter>
</receiver>

或者你的接收者app有一个context注册的接收者如下所示:

IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );

然后,为了能够发送广播给那些接收者,发送者app必须请求该权限,如下所示:

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

安全考虑和最佳实践

这里是一些安全考虑和发送和接收广播最佳实践:

  • 如果你不需要发送广播到你的app意外的组件,那么你可以使用LocalBroadcastManager发送和接收本地广播,这在Support Library中也是可用的。LocalBroadcastManager效率更高(不需要跨进程通信)且让你不必思考有关其他app可能接受和发送你的广播的任何安全问题。在你的应用程序中,本地广播可用作通用的pub/sub事件总线而无需任何系统范围的广播。
  • 如果许多app在它们的app中注册了相同广播的接收者,这会导致系统启动太多app,对设备性能和用户体验造成实质性影响。为了避免如此,优先使用context注册而不是manifest声明。某些条件下,Android系统本身会强制使用context注册接收者。例如,CONNECTIVITY_ACTION广播只传递给context注册的接收者。
  • 不要使用隐式intent广播敏感信息。任意注册了接收该广播的app都能读到这些信息。有三个途径可以控制谁能接收你的广告:
    • 发送广播时你可以指定一个权限
    • Android4.0以上,发送广播时你可以使用setPackage(String)指定一个package.系统会将广播限制在符合包名的一类app.
    • 你可以使用LocalBroadcastManager发送本地广播。
  • 当你注册了一个接收者,任意app都可能会发送恶意的广播给你的app的接收者。有三个途径可以限制你的app接收的广播:
    • 注册广播接收者时你可以指定权限
    • 对于清单声明的接收者,你可以在manifest中设置android:exported属性为“false”。这样接收者不会接收该app意外来源的广播。
    • 使用LocalBroadcastManager你可以限制自身只使用本地广播。
  • 广播action的命名空间是全局的。确保action名和其他字符串写在一个你自己的命名空间,否则可能在无意中与其他app发生冲突。
  • 因为接收者的onReceive(Context, Intent)方法运行在主线程中,它应该快速执行和返回。如果你需要执行耗时任务,注意多线程和启动后台服务,因为当onReceive()返回时,整个进程都可能被系统杀掉。更多信息,请查看Effect on process state.要执行耗时任务,我们建议:
    • 在你的接收者的onReceive()方法中调用goAsync(),并且将BroadcastReceiver.PendingResult传到后台线程。这可以让广播从onReceive()返回之后仍然保持活跃。但是即使使用了该方法,系统仍然希望你能尽快完成广播(10秒钟以下)。它允许你将工作移交给另一个线程以防阻塞主线程。
    • 使用JobScheduler安排任务。更多信息,请查看Intelligent Job Scheduling.
  • 不要从广播接收者那启动activity,因为这样的用户体验很糟糕;如果接收者不止一个更是如此。相反,可以考虑显示一个通知。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/148734.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • mysql字符串函数:FIND_IN_SET()使用方法详解

    mysql字符串函数:FIND_IN_SET()使用方法详解

    2021年11月10日
  • Map集合实例练习一

    Map集合实例练习一    java基础是关键,当你掌握一定的知识量的时候,但感觉其实还是基础是关键,很多框架都是固定的,只要掌握框架的配置,再加基础,相信你也就可以入这行了。选择有很多,要么及早的地放弃,不要浪费青春时光与金钱,要么坚持不放弃,一直干下去….失败不可怕,可怕的是不敢面对失败。好了,加油!!!引导语,就简单的说到这里。      Map概念Map集合的特点,如是否可重复,…

  • pytest运行_python缓存机制

    pytest运行_python缓存机制前言pytest运行完用例之后会生成一个.pytest_cache的缓存文件夹,用于记录用例的ids和上一次失败的用例。方便我们在运行用例的时候加上–lf和–ff参数,快速运行上一

  • java深拷贝的实现方式_接口可以创建对象吗

    java深拷贝的实现方式_接口可以创建对象吗Cloneable接口与Serializable接口都是定义接口而没有任何的方法。Cloneable可以实现对象的克隆复制,Serializable主要是对象序列化的接口定义。很多时候我们涉及到对象的复制,我们不可能都去使用setter去实现,这样编写代码的效率太低。JDK提供的Cloneable接口正是为了解决对象复制的问题而存在。Cloneable结合Serializable接口可以实现JVM对象的深度复制。

    2022年10月14日
  • 整理:FPGA选型[通俗易懂]

    整理:FPGA选型[通俗易懂]针对性整理下FPGA选型问题一、获取芯片资料:要做芯片的选型,首先就是要对有可能要面对的芯片有整体的了解,也就是说要尽可能多的先获取芯片的资料。现在FPGA主要有4个生产厂家,ALTERA,XIL

  • gcc编译成动态库_gcc动态库

    gcc编译成动态库_gcc动态库gcc常用编译选项: 选项 含义 -static 链接静态库,禁止使用动态库 -shared 进行动态库编译,链接动态库 -Ldir 在动态库的搜索路径中增加dir目录 -lname 链接静态库(libname.a)或动态库(libname.so)的库文件 -fPIC(或fpic) 生成使用相对地址无关的目标代码 方法一…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号