说说Android的广播(1) - 普通广播,有序广播和粘性广播

说说Android的广播

对于Activity的启动流程,我们已经有了几个版本的分析了。这里我们分析一个更容易一些的,四大组件中最简单的Broadcast Receiver。

关于Broadcast,有几点需要了解。首先是广播的类型,然后是广播的发送方法,最后是广播是如何被接收的。这三者相辅相承的,比如普通广播和有序广播只有在详细了解了广播的接收过程了之后,才能真正明白它的含义。

广播的类型:普通广播,有序广播和粘性广播

普通的广播是不在意顺序的,最简单的理解是同时可以收到这个广播。如果应用是动态注册这个广播的,且广播发送时这个进程还活着,那么当然可以并发的把广播尽快地传送出去是最好的。
但是,如果是通过AndroidManifest.xml静态注册的情况,也就是说这个广播首先要把一个进程启动起来,这时并发启动很多进程就是个问题了。Android目前的做法是,对这种静态的广播接收者,自动按有序广播的方式来串行处理。但是这对应用是透明的,应用不能假设系统已经把静态的无序广播当成有序广播来处理。

这个时候讲粘性广播有福了,因为从Android 5.0(API 21)开始,因为安全性的问题,官方已经正式废弃了粘性广播。

普通广播的发送

Context类提供两个方法可以用于发送普通广播:

  • sendBroadcast(Intent intent);
  • sendBroadcast(Intent intent, String receiverPermission);

差别是第二个设置权限。

发给特定的用户:

  • sendBroadcastAsUser(Intent intent, UserHandle user);
  • sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission);

有序广播的发送

有序广播因为要处理消息的处理结果,所以要复杂一些。

  • sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);

如果只是想让广播可以按优先级来收取,并不在意处理的结果,可以用下面的版本:

  • sendOrderedBroadcast(Intent intent, String receiverPermission);

同样,在多用户环境下,也可以选择给哪个用户发广播:

  • sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);

过时的粘性广播

不管是普通的还是有序的广播都对应有粘性的版本:

  • sendStickyBroadcast(Intent intent);
  • sendStickyBroadcastAsUser(Intent intent, UserHandle user);
  • sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);
  • sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);

以上的API都是定义于Context类中:https://developer.android.com/reference/android/content/Context.html

首先我们先看看发送端是如何发送的。
我们首先先放一个大图,让大家先有一个直观的印象,不管普通广播、有序广播、粘性广播如何组合,最终都汇集到一个大方法中。

应用发送广播的过程

普通广播的发送

我们先看应用发送普通广播的一个简单的例子:

Intent intent = new Intent("android.intent.action.TestAction");
intent.putExtra(EXTRA_TIME, System.currentTimeMillis());
MainActivity.this.sendBroadcast(intent);

非常简单,调用ContentWrapper的sendBroadcast方法就可以了。
然后我们顺藤摸瓜就好了。
Activity中的sendBroadcast,实际上调用的是:

void android.content.ContextWrapper.sendBroadcast(Intent intent);

ContextWrapper.sendBroadcast

我们来看frameworks/base/core/java/android/content/ContextWrapper.java中对sendBroadcast的定义:

    @Override
    public void sendBroadcast(Intent intent) {
        mBase.sendBroadcast(intent);
    }

ContextWrapper只是一个包装,真正的实现在ContextImpl中

ContextImpl.sendBroadcast

我们来看/frameworks/base/core/java/android/app/ContextImpl.java中真正实现sendBroadcast的功能:

    @Override
    public void sendBroadcast(Intent intent) {
...
        try {
...
            ActivityManagerNative.getDefault().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
    }

它会通过IPC去调用AMS的broadcastIntent。由于我们这个普通的广播的方法参数最少,所以好多都是传null。

ActivityManagerService.broadcastIntent

public final int broadcastIntent(IApplicationThread caller,
        Intent intent, String resolvedType, IIntentReceiver resultTo,
        int resultCode, String resultData, Bundle resultExtras,
        String[] requiredPermissions, int appOp, Bundle options,
        boolean serialized, boolean sticky, int userId) {
    enforceNotIsolatedCaller("broadcastIntent");
    synchronized(this) {
        intent = verifyBroadcastLocked(intent);

        final ProcessRecord callerApp = getRecordForAppLocked(caller);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        int res = broadcastIntentLocked(callerApp,
                callerApp != null ? callerApp.info.packageName : null,
                intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                requiredPermissions, appOp, null, serialized, sticky,
                callingPid, callingUid, userId);
        Binder.restoreCallingIdentity(origId);
        return res;
    }
}

加锁,定参数,然后调用真正的逻辑的实现。

有序广播的发送

我们先把broadcastIntentLocked的真正逻辑放一下,先看看有序广播是如何发送的。

ContextWrapper.sendOrderedBroadcast

Context是abstract方法,调用的是ContextWrapper的实现:

@Override
public void sendOrderedBroadcast(Intent intent,
        String receiverPermission) {
    mBase.sendOrderedBroadcast(intent, receiverPermission);
}

跟普通广播一样,还是会调用到ContextImpl.sendOrderedBroadcast

public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    String[] receiverPermissions = receiverPermission == null ? null
            : new String[] {receiverPermission};
    try {
        intent.prepareToLeaveProcess();
        ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                null, true, false, getUserId());
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}

有序广播调用broadcastIntent的区别在于serialized参数,普通广播为false,有序广播为true.

原型为:

public final int broadcastIntent(IApplicationThread caller,
        Intent intent, String resolvedType, IIntentReceiver resultTo,
        int resultCode, String resultData, Bundle resultExtras,
        String[] requiredPermissions, int appOp, Bundle options,
        boolean serialized, boolean sticky, int userId) {

多参数的版本

前面讲过带有回调的版本,我们看看它是如何实现的:

@Override
public void sendOrderedBroadcast(
    Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,
    Handler scheduler, int initialCode, String initialData,
    Bundle initialExtras) {
    mBase.sendOrderedBroadcast(intent, receiverPermission,
            resultReceiver, scheduler, initialCode,
            initialData, initialExtras);
}

当然还是调用ContextImpl.sendOrderedBroadcast

@Override
public void sendOrderedBroadcast(Intent intent,
        String receiverPermission, BroadcastReceiver resultReceiver,
        Handler scheduler, int initialCode, String initialData,
        Bundle initialExtras) {
    sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE,
            resultReceiver, scheduler, initialCode, initialData, initialExtras, null);
}

这次变成只是一个封装了,它会调用一个更多参数的版本:

void sendOrderedBroadcast(Intent intent,
        String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
        Handler scheduler, int initialCode, String initialData,
        Bundle initialExtras, Bundle options) {
    warnIfCallingFromSystemProcess();
    IIntentReceiver rd = null;
    if (resultReceiver != null) {
        if (mPackageInfo != null) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            rd = mPackageInfo.getReceiverDispatcher(
                resultReceiver, getOuterContext(), scheduler,
                mMainThread.getInstrumentation(), false);
        } else {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            rd = new LoadedApk.ReceiverDispatcher(
                    resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
        }
    }
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    String[] receiverPermissions = receiverPermission == null ? null
            : new String[] {receiverPermission};
    try {
        intent.prepareToLeaveProcess();
        ActivityManagerNative.getDefault().broadcastIntent(
            mMainThread.getApplicationThread(), intent, resolvedType, rd,
            initialCode, initialData, initialExtras, receiverPermissions, appOp,
                options, true, false, getUserId());
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}

这次是一个全参数调用broadcastIntent的版本了,除了sticky就齐了

粘性广播

我们也不绕圈子了,直接看ContextImpl.sendStickyBroadcast.

@Override
@Deprecated
public void sendStickyBroadcast(Intent intent) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        intent.prepareToLeaveProcess();
        ActivityManagerNative.getDefault().broadcastIntent(
            mMainThread.getApplicationThread(), intent, resolvedType, null,
            Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
            getUserId());
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}
时间: 2024-09-05 14:10:40

说说Android的广播(1) - 普通广播,有序广播和粘性广播的相关文章

android系统-android中一个桌面上的一个widget就是一个广播接收者吗,还是一个广播接收器

问题描述 android中一个桌面上的一个widget就是一个广播接收者吗,还是一个广播接收器 android中一个桌面上的一个widget就是一个广播接收者吗,还是一个广播接收器 我觉得是一个广播接收者,因为它是一个对象 解决方案 广播接收者和广播接收器难道不是同一概念么....AppWidgetProvider类,继承自BroadcastReceiver,可以接收并处理广播事件

BroadcastReceiver广播接收者(六)——粘性广播(StickyBroadcast)使用示例

MainActivity如下: package cc.com; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.content.Intent; /** * Demo描述: * 粘性广播(StickyBroadcast)使用示例 * 粘性广播也叫等待广播. * * 使用场景: * 发送一个粘性广播(StickyBroadcast),但是现在并没

远程广播的调用-在用远程广播接收消息的过程中有时候收到广播的信息 有时候收不到是什么原因?求大哥们指点

问题描述 在用远程广播接收消息的过程中有时候收到广播的信息 有时候收不到是什么原因?求大哥们指点 在用远程广播接收消息的过程中有时候收到广播的信息 有时候收不到是什么原因?也就是 有时候没有调用OnReceive方法 . 解决方案 看看是不是网络不太稳定造成的. 解决方案二: 没有经过 网络请求 只是在本地,只不过是做系统软件的

Android:广播BroadcastReceiver

 什么是BroadcastReceiver? BroadcastReceiver,广播接收者,它是一个系统全局的监听器,用于监听系统全局的Broadcast消息,所以它可以很方便的进行系统组件之间的通信. BroadcastReceiver虽然是一个监听器,但是它和之前用到的OnXxxListener不同,那些只是程序级别的监听器,运行在指定程序的所在进程中,当程序退出的时候,OnXxxListener监听器也就随之关闭了,但是BroadcastReceiver属于系统级的监听器,它拥有自己的进

Android BroadcastReceiver广播机制概述_Android

Android广播机制概述Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器).广播作为Android组件间的通信方式,可以使用的场景如下: 1.同一app内部的同一组件内的消息通信(单个或多个线程之间): 2.同一app内部的不同组件之间的消息通信(单个进程):  3.同一app具有多个进程的不同组件之间的消息通信:  4.不同app之间的组件之间消息通信:  5.Android系统在特定情况下与App之间的消息

Android开发22——广播接收者BroadcastReceiver的原理和注册方式

一.广播机制的基本概念 当某个事件产生时(如一条短信发来或一个电话打来),android操作系统会把这个事件广播给所有注册的广播接收者,需要处理这个事件的广播接收者进行处理.其实这就是日常生活中的广播.发生一个新闻后,广播电台会广播这个新闻给打开收音机的人,对这个新闻感兴趣的人会关注,可能会拿笔记下.新闻就是事件,广播电台就是android系统,打开收音机的人就是广播接收者,感兴趣的人就是需要处理该事件的广播接收者,拿笔记下就是对该事件进行的操作.   二.广播的分类--普通广播和有序广播 ①普

Android学习_广播

广播接收器也是运行在UI线程,因此,onReceive方法中不能执行太耗时的操作.否则将因此ANR.一般情况下,根据实际业务需求,onReceive方法中都会涉及到与其他组件之间的交互,如发送Notification.启动service等. 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中. 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播.  根据广播的发送方式,可以将其分为以下几种类型: 1.Norma

Android组件系列----BroadcastReceiver广播接收器

[正文] 一.广播的功能和特征 广播的生命周期很短,经过调用对象-->实现onReceive-->结束,整个过程就结束了.从实现的复杂度和代码量来看,广播无疑是最迷你的Android 组件,实现往往只需几行代码.广播对象被构造出来后通常只执行BroadcastReceiver.onReceive方法,便结束了其生命周期.所以有的时候我们可以把它当做函数看也未必不可. 和所有组件一样,广播对象也是在应用进程的主线程中被构造,所以广播对象的执行必须是要同步且快速的.也不推荐在里面开子线程,因为往往

Android入门:广播发送者与广播接收者详细介绍_Android

一.广播发送者&广播接收者介绍 1.广播接收者 广播接收者简单地说就是接收广播意图的Java类,此Java类继承BroadcastReceiver类,重写: public void onReceive(Context context,Intent intent),其中intent可以获得传递的数据: 广播意图就是通过Context.sendBroadcast(Intent intent)或Context.sendOrderedBroadcast(Intent intent)发送的意图,通过这个语