Android广播管理三--广播发送(sendBroadcast)流程分析

本文深入剖析了Android广播发送流程,从ContextWrapper.sendBroadcast开始,经过ContextImpl.sendBroadcast、ActivityManagerProxy.broadcastIntent,直至ActivityManagerService的处理,包括对广播的权限检查、粘性广播的处理、有序广播的调度。接着,详细阐述了广播接收器的处理过程,包括动态广播、串行化广播和静态广播的接收流程,以及如何避免广播引发的ANR问题。
摘要由CSDN通过智能技术生成

        前面我们分析了Android应用程序注册广播接收器的过程,接下来它还要等待ActivityManagerService将广播分发过来。ActivityManagerService是如何得到广播并把它分发出去的呢?

         广播的发送者将广播发送到ActivityManagerService,ActivityManagerService接收到这个广播以后,就会在自己的注册中心查看有哪些广播接收器订阅了该广播,然后把这个广播逐一发送到这些广播接收器中,但是ActivityManagerService并不等待广播接收器处理这些广播就返回了,因此,广播的发送和处理是异步的。概括来说,广播的发送路径就是从发送者到ActivityManagerService,再从ActivityManagerService到接收者,这中间的两个过程都是通过Binder进程间通信机制来完成的

发送广播

ContextWrapper.sendBroadcast

        这个函数定义在frameworks/base/core/java/android/content/ContextWrapper.java文件中:

public class ContextWrapper extends Context {  
    Context mBase;  
  
    ......  
  
    @Override  
    public void sendBroadcast(Intent intent) {  
        mBase.sendBroadcast(intent);  
    }  
  
    ......  
  
} 
         我们上文介绍了ContextWrapper类是Context类的封装类,ContextImpl是Context类的实现类。这里的成员变量mBase是一个ContextImpl实例,这里只简单地调用ContextImpl.sendBroadcast进一行操作。

ContextImpl.sendBroadcast

         这个函数定义在frameworks/base/core/java/android/app/ContextImpl.java文件中:

public void sendBroadcast(Intent intent) {
    ............
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        //StrictMode下,对一些Action需要进行检查
        intent.prepareToLeaveProcess(this);

        //调用AMS中的接口
        ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    } catch(RemoteException e) {
        ..............
    }
}
        这里的resolvedType表示这个Intent的MIME类型,如果没有设置这个Intent的MIME类型,resolvedType为null。接下来就调用ActivityManagerService的远程接口ActivityManagerProxy把这个广播发送给ActivityManagerService了。

ActivityManagerProxy.broadcastIntent

        这个函数定义在frameworks/base/core/java/android/app/ActivityManagerNative.java文件中:

    class ActivityManagerProxy implements IActivityManager  
    {  
        ......  
      
        public int broadcastIntent(IApplicationThread caller,  
            Intent intent, String resolvedType,  IIntentReceiver resultTo,  
            int resultCode, String resultData, Bundle map,  
            String requiredPermission, boolean serialized,  
            boolean sticky) throws RemoteException  
        {  
            Parcel data = Parcel.obtain();  
            Parcel reply = Parcel.obtain();  
            data.writeInterfaceToken(IActivityManager.descriptor);  
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);  
            intent.writeToParcel(data, 0);  
            data.writeString(resolvedType);  
            ......
            mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);  
            reply.readException();  
            int res = reply.readInt();  
            reply.recycle();  
            data.recycle();  
            return res;  
        }  
      
        ......  
      
    }  
        这里主要是把要传递的参数封装好,然后通过Binder驱动程序进入到ActivityManagerService的broadcastIntent()方法中。

ActivityManagerService.broadcastIntent

         这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

    public final class ActivityManagerService extends ActivityManagerNative  
            implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
        ......  
      
        public final int broadcastIntent(IApplicationThread caller,  
                Intent intent, String resolvedType, IIntentReceiver resultTo,  
                int resultCode, String resultData, Bundle map,  
                String requiredPermission, boolean serialized, boolean sticky) {  
            synchronized(this) {//verifyBroadcastLocked方法检查Broadcast中intent携带的信息是否有问题(不能携带文件描述符,避免安全隐患)
                intent = verifyBroadcastLocked(intent);//同时检查intent的flag
      
                final ProcessRecord callerApp = getRecordForAppLocked(caller);//获取调用方(发送广播)的ProcessRecord
                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, map, requiredPermission, serialized,  
                    sticky, callingPid, callingUid);  
                Binder.restoreCallingIdentity(origId);  
                return res;  
            }  
        }  
      
        ......  
    }  
        这里注意几个参数:

        serialized:表示当前广播是否是order广播,true代表order广播(有序广播);

        sticky:表示当前广播是否是sticky广播,true代表sticky广播(粘性广播);

        broadcastIntent中对Broadcast对应的Intent进行一些检查后,调用broadcastIntentLocked进行实际的处理。

ActivityManagerService.broadcastIntentLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中,代码量较大,分段分析。

Part I

final int broadcastIntentLocked(.....) {
    intent = new Intent(intent);

    // By default broadcasts do not go to stopped apps.
    // Android系统对所有app的运行状态进行了跟踪
    // 当应用第一次安装但未使用过,或从程序管理器被强行关闭后,将处于停止状态

    // 在发送广播时,不管是什么广播类型,系统默认增加了FLAG_EXCLUDE_STOPPED_PACKAGES的flag
    // 使得对于所有BroadcastReceiver而言,如果其所在进程的处于停止状态,该BroadcastReceiver将无法接收到广播
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

    //大量条件检查,例如:
    //有些Action只能由系统来发送;
    //有些Action需要发送方申明了对应权限
    ...................

    //某些Action,例如Package增加或移除、时区或时间改变、清除DNS、代理变化等,需要AMS来处理
    //AMS需要根据Action,进行对应的操作
    ..................
}

1、排除Stopped状态的应用

        // By default broadcasts do not go to stopped apps.
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
        所有的广播Intent在这里都会默认加上这个标记,表示 所有的广播都不会发送到Stopped状态的应用,应该在发送的时候会检查应用当前的状态。

2、系统升级广播

        // If we have not finished booting, don't allow this to launch new processes.
        if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        }
         Intent.FLAG_RECEIVER_BOOT_UPGRADE是系统升级的flag,允许在系统启动前发送。只有注册了的接收者被调用,所有的BroadcastReceiver组件不会被加载。

3、处理受保护广播

        // Verify that protected broadcasts are only being sent by system code,受保护的广播只能有系统发送
        // and that system code is only sending protected broadcasts.
        final String action = intent.getAction();
        final boolean isProtectedBroadcast;
        try {
            isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
        } catch (RemoteException e) {
            Slog.w(TAG, "Remote exception", e);
            return ActivityManager.BROADCAST_SUCCESS;
        }
        受保护广播是通过查询PMS得到的,如果是受保护广播,抛出异常后直接返回ActivityManager.BROADCAST_SUCCESS,不会进行下面的动作。

4、判断发送者是否是系统进程

        final boolean isCallerSystem;
        switch (UserHandle.getAppId(callingUid)) {//如果是root、system、phone、bluetooth、nfc等的UID,则广播不受限制
            case Process.ROOT_UID:
            case Process.SYSTEM_UID:
            case Process.PHONE_UID:
            case Process.BLUETOOTH_UID:
            case Process.NFC_UID:
                isCallerSystem = true;
                break;
            default:
                isCallerSystem = (callerApp != null) && callerApp.persistent;
                break;
        }
        isCallerSystem表示系统级用户发送的广播,这部分广播不受限制。

5、处理特定系统广播

        if (action != null) {
            switch (action) {
                case Intent.ACTION_UID_REMOVED:
                case Intent.ACTION_PACKAGE_REMOVED:
                case Intent.ACTION_PACKAGE_CHANGED:
                case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
                case Intent.ACTION_PACKAGES_SUSPENDED:
                case Intent.ACTION_PACKAGES_UNSUSPENDED:
                    // Handle special intents: if this broadcast is from the package
                    // manager about a package being removed, we need to remove all of
                    // its activities from the history stack.
        对于一些来自PMS的包状态的变化,AMS需要及时的处理相关的Activity,这里是因为AMS兼顾了所有的4大组件,当包的状态发生变化,AMS作为总管需要第一时间内处理完总管要做的事情,然后将对应的广播再转发给对应的应用。

        发送广播的第一阶段主要工作有:

        (1).根据广播对应的Intent中的信息,判断发送方是否有发送该广播的权限;

        (2).检查发送的广播是否是一些特殊的系统广播,特别是从PackageManagerService中发出的有个安装的应用移除的广播,如果检测到,需要将这些包中的Activity从AMS的Activity栈中移除。

Part II

..............
// Add to the sticky list if requested.
// 处理粘性广播相关的内容
if (sticky) {
    //检查是否有发送权限,在AndroidManifest.xml中必须声明android.permission.BROADCAST_STICKY权限
    if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
            callingPid, callingUid)
            != PackageManager.PERMISSION_GRANTED) {
        ..................//抛出SecurityException异常
    }

    //粘性广播不能指定接收权限,即发送时不能有requiredPermission权限信息
    if (requiredPermissions != null && requiredPermissions.length > 0) {
        ..............
        return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
    }

    if (intent.getComponent() != null) {
        //发送的intent里面不能指定接收者,即粘性广播不能指定接收方
        ............
    }

    // We use userId directly here, since the "all" target is maintained
    // as a separate set of sticky broadcasts.
    //当粘性广播是针对特定userId时,判断该粘性广播与系统保存的是否冲突
    if (userId != UserHandle.USER_ALL) {

        //取出发送给所有user的粘性广播,关于mStickyBroadcasts的介绍可参考上一篇博客
        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                UserHandle.USER_ALL);
        if (stickies != null) {
            ArrayList<Intent> list = stickies.get(intent.getAction());
            if (list != null) {
                int N = list.size();
                int i;
                for (i=0; i<N; i++) {
                    //发送给特定user的粘性广播,与发送给所有user的粘性广播,action一致时,发生冲突
                    if (intent.filterEquals(list.get(i))) {
                        //抛出异常
                        .............
                    }
                }
            }
        }

        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
        if (stickies == null) {
            stickies = new ArrayMap<>();
            //按userId保存粘性广播
            //即一个user,可能有多个粘性广播
            mStickyBroadcasts.put(userId, stickies);
        }

        //按照Action保存粘性广播
        //即一个Action,可能对应中多个广播
        ArrayList<Intent> list = stickies.get(intent.getAction());
        if (list == null) {
            //list为null时,直接加入
            list = new ArrayList<>();
            stickies.put(intent.getAction(), list);
        }
        final int stickiesCount = list.size();
        int i;
        for (i = 0; i < stickiesCount; i++) {
            //新的粘性广播与之前的重复,则保留新的
            //即当发送多个相同的粘性广播时,系统仅会保留最新的
            if (intent.filterEquals(list.get(i))) {
                // This sticky already exists, replace it.
                list.set(i, new Intent(intent));
                break;
            }
        }
        if (i >= stickiesCount) {
            //不重复时,直接加入
            list.add(new Intent(intent));
        }
    }
}
...............

        broadcastIntentLocked方法第二部分主要是处理粘性广播的,判断发送粘性广播的条件是否满足,然后就粘性广播保存起来,将其保存到AMS的mStickyBroadcasts变量里面,上一篇博客中分析了mStickyBroadcasts是以用户id作为key保存的,首先取出当前用户的所有sticky广播,然后根据当前广播的action保存到action对应的List里面即可。里面还有一个细节,就是如果当前intent和list中的某个intent用filterEquals()比较相等,就直接替换掉以前的,否则直接添加到list末尾。

Part III

.............
// Figure out who all will receive this broadcast.弄清楚谁会接收这个广播
//receivers主要用于保存匹配当前广播的静态注册的BroadcastReceiver
//若当前广播是有序广播时,还会插入动态注册的BroadcastReceiver
List receivers = null;

//registeredReceivers用于保存匹配当前广播的动态注册的BroadcastReceiver 
//BroadcastFilter中有对应的BroadcastReceiver的引用
List<BroadcastFilter> registeredReceivers = null;

// Need to resolve the intent to interested 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值