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)


相关推荐

  • 网络安全工具使用集锦手册下载_网络安全科普书籍

    网络安全工具使用集锦手册下载_网络安全科普书籍常用工具:Nmap使用详解 Sqlmap使用详解 CobaltStrike的使用 MetasploitFramework(MSF)的使用 CobaltStrike上线微信提醒 CobaltStrike的argue参数污染绕AV CobaltStrike证书修改躲避流量审查 CobaltStrike上线Linux主机(CrossC2)域内工具:Linux下使用ldapsearch进行域信息查询 ADExplorer和TheLDAPExplorer工具的用法 ADSI(Act

    2022年10月19日
  • 滴滴开源夜莺Nightingale:企业级监控解决方案「建议收藏」

    滴滴开源夜莺Nightingale:企业级监控解决方案「建议收藏」导读:滴滴开源又双叒发布新开源项目啦——夜莺(Nightingale)是滴滴基础平台联合滴滴云研发和开源的企业级监控解决方案。旨在满足云原生时代企业级的…

  • 你没见过的中文编程工具,用中文开发android安卓应用,安卓开发的初体验![通俗易懂]

    如果你对中文开发有一定的了解,那么你一定会知道易语言。易语言是仿VB制作的一款中文编程工具。它可以用来开发Windows软件。那有没有用于android安卓开发的中文编程工具呢?有!那就是猎码安卓中文开发工具&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;小编身为一个程序员,在使用猎码后,感觉还是不错的!&nbsp;&nbsp;&nb…

  • 二项分布方差的详细证明

    二项分布方差的详细证明##前置技能从组合数公式可以直接推出:$k\mathrm{C}_n^k=n\mathrm{C}_{n-1}^{k-1}$同样地,你可以得到$(k-1)\mathrm{C}_{n-1}^{

  • python检验多重共线性_利用Python进行VIF检验[通俗易懂]

    python检验多重共线性_利用Python进行VIF检验[通俗易懂]在统计学中,多重共线性(共线性)是指多元线性回归模型中的某个预测变量(自变量/解释变量)可以以相当大的准确度通过其他预测变量线性预估。在这种情况下,模型或数据的微小变化就可能导致多元回归模型的系数估计值出现不规律地改变,可能造成如下后果:回归系数的普通最小二乘估计量可靠度降低。如图1与2所示,随着多重共线性程度的提高,参数方差(表示变量与的相关度)会急剧上升到很大的水平,理论上使最小二…

  • awk变量

    awk变量一、awk字段变量变量名属性$0当前记录$1~$n当前记录的第n个字段二、awk内置变量变量名属性FS输入字段分割符,默认是空格RS输入记录分割符,默认是换行符NR已经读处的记录数,就是行号,从1开始NF当前记录的字段个数,就…

发表回复

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

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