安卓广播发送接收流程

本文基于Andorid 11。

一、registerReceiver

registerReceiver(new MyRecevier(), new IntentFilter("com.example.broadcast"));
  • 动态注册广播接收器,参数:BroadcastReceiver, IntentFilter。
<receiver android:name=".MyReceiver">
    <intent-filter>
        <action android:name="com.test.broadcast"/>
    </intent-filter>
</receiver>
  • 静态注册广播,安卓8以后除几个特殊的广播外,静态注册方式只能接收显示广播。

查看动态注册广播流程:

1.2 ContextImpl.registerReceiverInternal

// ContextImpl.java    
	private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
                    mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
                    filter, broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

传入IIntentReceiver对象作为参数,调用AMS.registerReceiverWithFeature方法注册。

1.3 AMS.registerReceiverWithFeature

// ActivityManagerService.java
	public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
            String callerFeatureId, IIntentReceiver receiver, IntentFilter filter,
            String permission, int userId, int flags) {
        enforceNotIsolatedCaller("registerReceiver");
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        final boolean visibleToInstantApps
                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
        //	1. 获取Uid, Pid, isInstantApp, actions
        int callingUid;
        int callingPid;
        boolean instantApp;
        synchronized(this) {
            if (caller != null) {
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when registering receiver " + receiver);
                }
                if (callerApp.info.uid != SYSTEM_UID &&
                        !callerApp.pkgList.containsKey(callerPackage) &&
                        !"android".equals(callerPackage)) {
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                }
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            instantApp = isInstantApp(callerApp, callerPackage, callingUid);
            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }
            
			//	2. 收集当前用户下的粘性广播
            // Collect stickies of users
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            while (actions.hasNext()) {
                String action = actions.next();
                for (int id : userIds) {
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                    if (stickies != null) {
                        ArrayList<Intent> intents = stickies.get(action);
                        if (intents != null) {
                            if (stickyIntents == null) {
                                stickyIntents = new ArrayList<Intent>();
                            }
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }

        ArrayList<Intent> allSticky = null;
        if (stickyIntents != null) {
            final ContentResolver resolver = mContext.getContentResolver();
            // Look for any matching sticky broadcasts...
            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                Intent intent = stickyIntents.get(i);
                // Don't provided intents that aren't available to instant apps.
                if (instantApp &&
                        (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
                    continue;
                }
                // If intent has scheme "content", it will need to acccess
                // provider that needs to lock mProviderMap in ActivityThread
                // and also it may need to wait application response, so we
                // cannot lock ActivityManagerService here.
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    allSticky.add(intent);
                }
            }
        }

        // The first sticky in the list is returned directly back to the client.
        Intent sticky = allSticky != null ? allSticky.get(0) : null;
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
        if (receiver == null) {
            return sticky;
        }

        // 3. 创建ReceiverList,BroadcastFilter对象,添加到AMS类变量mRegisteredReceivers, mReceiverResolver中维护。
        synchronized (this) {
            if (callerApp != null && (callerApp.thread == null
                    || callerApp.thread.asBinder() != caller.asBinder())) {
                // Original caller already died
                return null;
            }
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    final int totalReceiversForApp = rl.app.receivers.size();
                    if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                        throw new IllegalStateException("Too many receivers, total of "
                                + totalReceiversForApp + ", registered for pid: "
                                + rl.pid + ", callerPackage: " + callerPackage);
                    }
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } else if (rl.uid != callingUid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for uid " + callingUid
                        + " was previously registered for uid " + rl.uid
                        + " callerPackage is " + callerPackage);
            } else if (rl.pid != callingPid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for pid " + callingPid
                        + " was previously registered for pid " + rl.pid
                        + " callerPackage is " + callerPackage);
            } else if (rl.userId != userId) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for user " + userId
                        + " was previously registered for user " + rl.userId
                        + " callerPackage is " + callerPackage);
            }
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            if (rl.containsFilter(filter)) {
                Slog.w(TAG, "Receiver with filter " + filter
                        + " already registered for pid " + rl.pid
                        + ", callerPackage is " + callerPackage);
            } else {
                rl.add(bf);
                mReceiverResolver.addFilter(bf);
            }

            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                final int stickyCount = allSticky.size();
                for (int i = 0; i < stickyCount; i++) {
                    Intent intent = allSticky.get(i);
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, null, -1, -1, false, null, null, OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1, false,
                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }

            return sticky;
        }
    }
  1. 获取Uid, Pid, userId, isInstantApp, actions,做一些校验工作
  2. 收集当前用户下的粘性广播
  3. 创建ReceiverList,BroadcastFilter对象,添加到AMS类变量mRegisteredReceivers, mReceiverResolver中维护。
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
            = new IntentResolver<BroadcastFilter, BroadcastFilter>()

mRegisteredReceivers保存了每个IIntentReceiver注册的广播列表,IIntentReceiver对象在1.2小节被contextImple传入,对应一个BroadcastReceiver对象,BroadcastReceiver可能监听多个广播(添加了多个IntentFilter),ReceiverList代表其监听的广播。

ReceiverList类继承ArrayList<BroadcastFilter>,BroadcastFilter类继承IntentFilter,关注一下BroadcastFilter的构造方法:

BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);

应用程序调用registerReceiver()方法时注册广播接收器传递的IntentFilter参数filter被传递给BroadcastFilter对象,通过此参数指明感兴趣的广播;创建的BroadcastFilter对象被添加到ReceiverList中:rl.add(bf);;添加到mReceiverResolver中:mReceiverResolver.addFilter(bf);,mReceiverResolver是一个IntentResolver对象,从命名就可以看出来IntentResolver负责Intent的处理解析工作,Intent在安卓各个组件中传递消息,IntentResolver负责将消息交给对应的组件。

IntentResolver维护几个Filter对象处理Intent解析工作:

  • // All of the MIME types that have been registered, such as "image/jpeg",
    private final ArrayMap<String, F[]> mTypeToFilter = new ArrayMap<String, F[]>();
    
  • // All of the URI schemes (such as http) that have been registered.
    private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>();
    
  • // All of the actions that have been registered, but only those that did not specify data.
    private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>();
    

这里负责广播Intent解析工作的就是mActionToFiltermActionToFilter使用ArrayMap<String, F[]>()数据结构保存action和IntentFilter[]的对应关系,发送广播时便从mActionToFilter对象维护的ArrayMap中找到对应的IntentFilter,将信息封装成ResolveInfo对象就好了。

二、sendBroadcast

2.1 AMS.broadcastIntentLocked

// ActivityManagerService.java
@GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
        @Nullable String callerFeatureId, Intent intent, String resolvedType,
        IIntentReceiver resultTo, int resultCode, String resultData,
        Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
        boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
        int realCallingPid, int userId, boolean allowBackgroundActivityStarts,
        @Nullable int[] broadcastWhitelist) {
    
    intent = new Intent(intent);
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
    
    // 1. 处理安全问题,ProtectedBroadcast
    final String action = intent.getAction();
    final boolean isProtectedBroadcast;
    isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
    
    final boolean isCallerSystem;
    switch (UserHandle.getAppId(callingUid)) {
        case ROOT_UID:
        case SYSTEM_UID:
        case PHONE_UID:
        case BLUETOOTH_UID:
        case NFC_UID:
        case SE_UID:
        case NETWORK_STACK_UID:
            isCallerSystem = true;
            break;
        default:
            isCallerSystem = (callerApp != null) && callerApp.isPersistent();
            break;
    }
    
    if (!isCallerSystem) {
        if (isProtectedBroadcast) {
            throw new SecurityException(msg);
        } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
            //...
        }
    }
    
    // 2. 粘性广播,安卓5.0后被弃用,不关注。
    if (sticky) {
    }

    
    // 3. 计算广播接收者
    List receivers = null;
    List<BroadcastFilter> registeredReceivers = null;
    // 3.1 FLAG_RECEIVER_REGISTERED_ONLY指定必须动态注册广播接收器才能接收,如果未指定,通过PMS通过Intent参数的component或者package查找接收器
    if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)  == 0) {
        receivers = collectReceiverComponents(
                intent, resolvedType, callingUid, users, broadcastWhitelist);
    }
    // 3.2 隐式广播,查找动态注册广播接收器,mReceiverResolver维护了在应用程序调用registerReceiver()添加的对应接收器
    if (intent.getComponent() == null) {
        if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
            // ...
        } else {
            registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);
        }
    }
    
    final boolean replacePending =
            (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
    
    // 4. 获取BroadcastQueue, 创建BroadcastRecord对象,添加到mParallelBroadcasts发送。
    int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
    if (!ordered && NR > 0) {	// 无序广播
        // If we are not serializing this broadcast, then send the
        // registered receivers separately so they don't wait for the
        // components to be launched.
        final BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                resultCode, resultData, resultExtras, ordered, sticky, false, userId,
                allowBackgroundActivityStarts, timeoutExempt);
        final boolean replaced = replacePending
                && (queue.replaceParallelBroadcastLocked(r) != null);
        // Note: We assume resultTo is null for non-ordered broadcasts.
        if (!replaced) {
            queue.enqueueParallelBroadcastLocked(r);// 添加到mParallelBroadcasts队列。
            queue.scheduleBroadcastsLocked();		// 发送mParallelBroadcasts队列中的平行广播。
        }
        registeredReceivers = null;
        NR = 0;										// 将registeredReceivers和NR变量重置,避免后续合并到receivers中。
    }
    
    // 5. 合并receivers(静态注册),registeredReceivers(动态注册)到receivers变量中。
    if (receivers != null) {
        int NT = receivers != null ? receivers.size() : 0;
        int it = 0;
        ResolveInfo curt = null;
        BroadcastFilter curr = null;
        while (it < NT && ir < NR) {	// 动态注册无序广播 NR == 0。
            if (curt == null) {
                curt = (ResolveInfo)receivers.get(it);
            }
            if (curr == null) {
                curr = registeredReceivers.get(ir);
            }
            if (curr.getPriority() >= curt.priority) {
                // Insert this broadcast record into the final list.
                receivers.add(it, curr);
                ir++;
                curr = null;
                it++;
                NT++;
            } else {
                // Skip to the next ResolveInfo in the final list.
                it++;
                curt = null;
            }
        }
    }
    while (ir < NR) {
        if (receivers == null) {
            receivers = new ArrayList();
        }
        receivers.add(registeredReceivers.get(ir));
        ir++;
    }
    
    // 6. 将合并的静态注册广播根据优先级,添加到mOrderedBroadcasts发送。
    if ((receivers != null && receivers.size() > 0)
        || resultTo != null) {
	    BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                resultData, resultExtras, ordered, sticky, false, userId,
                allowBackgroundActivityStarts, timeoutExempt);
        final BroadcastRecord oldRecord =
                replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
        if (oldRecord != null) {
            if (oldRecord.resultTo != null) {
                final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
                oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
                                oldRecord.intent,
                                Activity.RESULT_CANCELED, null, null,
                                false, false, oldRecord.userId);
            }
        } else {
            queue.enqueueOrderedBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
    }
}

发送广播:

  1. 检查是否protected广播,system app发送隐式广播需要是protected的,否则抛出wtf异常,发送显示广播没有这个限制;非system app没有权限发送protected广播,否则crash。

  2. 粘性广播处理,安卓5.0后被弃用,不关注。

  3. 查找计算广播接收者,注册广播接收有静态和动态两种方式。

    1. 没有指定Intent.FLAG_RECEIVER_REGISTERED_ONLY,可以发送给静态注册的接收器,collectReceiverComponents计算静态广播接收器,保存到receivers变量。
    2. 隐式广播,查找动态注册广播接收器,从1.3小节中可以看到,mReceiverResolver变量维护了在应用程序调用registerReceiver()添加的对应接收器。
  4. 发送动态注册的无序广播,静态注册的广播都认为是有序广播,所以这里处理的是动态注册的广播接收器,获取BroadcastQueue, 创建BroadcastRecord对象,添加到mParallelBroadcasts,开始广播发送工作。将registeredReceivers和NR变量重置,避免后续合并到receivers中。

    BroadcastQueue分为前台广播队列,后台广播队列,指定了Intent.FLAG_RECEIVER_FOREGROUND被添加到前台广播队列,否则为后台队列。

    • 前台广播:前台广播数量较少,广播超时时间10S。
    • 后台广播:默认为后台广播,广播数量较多,超时时间60S,后台广播在当前用户后台启动服务超过阈值时会暂停广播,等待服务启动完成再开始发送。
  5. 合并receivers(静态注册),registeredReceivers(动态注册的有序广播)到receivers变量中。

  6. 将合并的静态注册广播根据优先级,添加到mOrderedBroadcasts发送。从4、5、6三个步骤可以看出,动态注册的无序广播优先级更高,优先处理接收工作,动态注册有序广播和静态注册广播优点级较低,在动态注册的无序广播(平行广播)处理完之后开始处理。

2.2 collectReceiverComponents

// ActivityManagerService.java
	private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
            int callingUid, int[] users, int[] broadcastWhitelist) {
        
        List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
                        .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
    }
// PackageManagerService.java
    @Override
    public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
            String resolvedType, int flags, int userId) {
        return new ParceledListSlice<>(
                queryIntentReceiversInternal(intent, resolvedType, flags, userId,
                        false /*allowDynamicSplits*/));
    }

    private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
            String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
        ComponentName comp = intent.getComponent();
        // 1. 显式广播,指定接收者ComponentName
        if (comp != null) {
            final List<ResolveInfo> list = new ArrayList<>(1);
            final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
            ResolveInfo ri = new ResolveInfo();
            ri.activityInfo = ai;
            list.add(ri);
            return applyPostResolutionFilter(
                    list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
                    intent);
        }
        // 2. 隐式广播,只能通过ComponentResolver查找接收者。
        synchronized (mLock) {
            String pkgName = intent.getPackage();
            if (pkgName == null) {
                final List<ResolveInfo> result =
                        mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
                return applyPostResolutionFilter(
                        result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
                        intent);
            }
            
            final AndroidPackage pkg = mPackages.get(pkgName);
            if (pkg != null) {
                final List<ResolveInfo> result = mComponentResolver.queryReceivers(
                        intent, resolvedType, flags, pkg.getReceivers(), userId);
                return applyPostResolutionFilter(
                        result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
                        intent);
            }
            return Collections.emptyList();
        }
    }

收集广播接收者:

  1. 显示广播,Intent setComponent()方法指定接收者。
  2. 隐式广播,需要PMS通过IntentResolver查找广播接受者。

看下IntentResolver的处理过程:

2.2.1 queryIntent
// IntentResolver.java    
	public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
            int userId) {
        String scheme = intent.getScheme();
        ArrayList<R> finalList = new ArrayList<R>();
        F[] firstTypeCut = null;
        F[] secondTypeCut = null;
        F[] thirdTypeCut = null;
        F[] schemeCut = null;
        
        // 1.处理MIME type,资源类型包括文本、图片、音视频等等。// Intent.setType(“image/*”);
        // If the intent includes a MIME type, then we want to collect all of
        // the filters that match that MIME type.
        if (resolvedType != null) {
            int slashpos = resolvedType.indexOf('/');
            if (slashpos > 0) {
                final String baseType = resolvedType.substring(0, slashpos);
                if (!baseType.equals("*")) {
                    if (resolvedType.length() != slashpos+2
                            || resolvedType.charAt(slashpos+1) != '*') {
                        // Not a wild card, so we can just look for all filters that
                        // completely match or wildcards whose base type matches.
                        firstTypeCut = mTypeToFilter.get(resolvedType);
                        secondTypeCut = mWildTypeToFilter.get(baseType);
                    } else {
                        // We can match anything with our base type.
                        firstTypeCut = mBaseTypeToFilter.get(baseType);
                        secondTypeCut = mWildTypeToFilter.get(baseType);
                    }
                    // Any */* types always apply, but we only need to do this
                    // if the intent type was not already */*.
                    thirdTypeCut = mWildTypeToFilter.get("*");
                } else if (intent.getAction() != null) {
                    // The intent specified any type ({@literal *}/*).  This
                    // can be a whole heck of a lot of things, so as a first
                    // cut let's use the action instead.
                    firstTypeCut = mTypedActionToFilter.get(intent.getAction());
                }
            }
        }
        // 2.处理scheme, 指定特定的模式,例如:content,http。// Intent.setData(Uri.fromFile(new File("/sdcard/test.3gp")));
        // If the intent includes a data URI, then we want to collect all of
        // the filters that match its scheme (we will further refine matches
        // on the authority and path by directly matching each resulting filter).
        if (scheme != null) {
            schemeCut = mSchemeToFilter.get(scheme);
        }
        // 3.没有指定任何数据,通过action查找。// Intent.setAction("com.test.broadcast");
        // If the intent does not specify any data -- either a MIME type or
        // a URI -- then we will only be looking for matches against empty
        // data.
        if (resolvedType == null && scheme == null && intent.getAction() != null) {
            firstTypeCut = mActionToFilter.get(intent.getAction());
        }

        // 4.处理category,category代表类别,定义了Activity的类别,Activity可以设置一个或者多个category标签。DEFAULT,HOME,LAUNCHER
        FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
        if (firstTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                    scheme, firstTypeCut, finalList, userId);
        }
        if (secondTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                    scheme, secondTypeCut, finalList, userId);
        }
        if (thirdTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                    scheme, thirdTypeCut, finalList, userId);
        }
        if (schemeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                    scheme, schemeCut, finalList, userId);
        }
        filterResults(finalList);
        sortResults(finalList);
        return finalList;
    }

总结一下:对Intent的处理主要分为4个部分:MIME,scheme,action,category。

  1. 处理MIME type,资源类型包括文本、图片、音视频等等。// Intent.setType(“image/*”);
  2. 处理scheme, 指定特定的模式,例如:content,http。// Intent.setData(Uri.fromFile(new File("/sdcard/test.3gp")));
  3. 处理action,没有指定任何数据,通过action查找。// Intent.setAction("com.test.broadcast");
  4. 处理category,category代表了Activity的类别,Activity可以设置一个或者多个category标签。DEFAULT,HOME,LAUNCHER。// Intent.addCategory(Intent.CATEGORY_LAUNCHER);

隐式广播在这个过程中找到之前已经注册的广播接收器。

2.3 processNextBroadcastLocked

整理好广播接收者后,BroadcastQueue.scheduleBroadcastsLocked()执行广播发送。

// BroadcastQueue.java
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    
    // 1. mParallelBroadcasts,并行广播立即执行,不需要等待上个广播接收完成,目前只针对动态注册的广播接受者。
    while (mParallelBroadcasts.size() > 0) {
        r = mParallelBroadcasts.remove(0);
        r.dispatchTime = SystemClock.uptimeMillis();
        r.dispatchClockTime = System.currentTimeMillis();
        
        final int N = r.receivers.size();
        for (int i=0; i<N; i++) {
            Object target = r.receivers.get(i);
            deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
        }
    }
    
    // 2.mPendingBroadcast,如果存在mPendingBroadcast,且进程正在启动过程中,return等待进程启动完成接收广播,完成接收后使mPendingBroadcast=null。
    if (mPendingBroadcast != null) {
        boolean isDead;
        if (mPendingBroadcast.curApp.pid > 0) {
            synchronized (mService.mPidsSelfLocked) {
                ProcessRecord proc = mService.mPidsSelfLocked.get(
                        mPendingBroadcast.curApp.pid);
                isDead = proc == null || proc.isCrashing();
            }
        } else {
            final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
                    mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
            isDead = proc == null || !proc.pendingStart;
        }
        if (!isDead) {
            // It's still alive, so keep waiting
            return;
        } else {
            mPendingBroadcast.state = BroadcastRecord.IDLE;
            mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
            mPendingBroadcast = null;
        }
    }
    
    // 3. 从mOrderedBroadcasts获取下一个BroadcastRecord对象
    do {
        final long now = SystemClock.uptimeMillis();
        r = mDispatcher.getNextBroadcastLocked(now);
    } while (r == null);
    
    // 4.发送广播
    ResolveInfo info =
        (ResolveInfo)nextReceiver;
    ComponentName component = new ComponentName(
            info.activityInfo.applicationInfo.packageName,
            info.activityInfo.name);
    final int receiverUid = info.activityInfo.applicationInfo.uid;
    String targetProcess = info.activityInfo.processName;
    ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
            info.activityInfo.applicationInfo.uid, false);
    r.manifestCount++;
    r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
    r.state = BroadcastRecord.APP_RECEIVE;
    r.curComponent = component;
    r.curReceiver = info.activityInfo;
    // 4.1 广播接收进程已启动,processCurBroadcastLocked
    if (app != null && app.thread != null && !app.killed) {
        app.addPackage(info.activityInfo.packageName,
                info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
        maybeAddAllowBackgroundActivityStartsToken(app, r);
        processCurBroadcastLocked(r, app, skipOomAdj);
        return;
    }
    // 4.2 广播接收进程未启动,startProcessLocked,mPendingBroadcast = r;
    if ((r.curApp=mService.startProcessLocked(targetProcess,
        info.activityInfo.applicationInfo, true,
        r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
        new HostingRecord("broadcast", r.curComponent),
        isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
        (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                == null) {
        // 启动失败
    }
    // 赋值mPendingBroadcast
    mPendingBroadcast = r;
}
  1. 处理动态注册的广播接收者mParallelBroadcasts,并行广播立即执行,不需要等待上个广播接收完成,目前只针对动态注册的广播接收者,因为动态注册说明这个接收者的应用进程已经启动了。
  2. 处理mPendingBroadcast,如果存在mPendingBroadcast,且进程正在启动过程中,return等待进程启动完成接收广播,一些静态注册的广播接收者在广播发送后可能应用进程还未启动,所以先拉起应用进程,在应用进程启动后再执行广播接收,完成接收后使mPendingBroadcast=null。
  3. 从mOrderedBroadcasts获取下一个BroadcastRecord对象。
  4. 发送广播,发送广播分为两种情况:a.接受者应用进程已经启动(常见于动态注册);b.接收者进程未启动(常见于静态注册),如果进程未启动,需要先拉起应用进程,将广播赋值给mPendingBroadcast变量记录下来,然后回到上面说的第2种情况。应用进程启动后会调用AMS的attachApplicationLocked方法通知应用启动完成,然后调用sendPendingBroadcastsLocked处理待接收的广播,同样Activity和Service也是这样处理,处理顺序是Activity->Service->Broadcast。

2.4 processCurBroadcastLocked

// BroadcastQueue.java    
	private final void processCurBroadcastLocked(BroadcastRecord r,
            ProcessRecord app, boolean skipOomAdj) throws RemoteException {
        r.receiver = app.thread.asBinder();
        r.curApp = app;
        app.curReceivers.add(r);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
        mService.mProcessList.updateLruProcessLocked(app, false, null);
        // Tell the application to launch this receiver.
        r.intent.setComponent(r.curComponent);
        
        mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
                                  PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
        app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
                r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                app.getReportedProcState());
    }

更新AMS和PMS中的一些进程状态:ProcessState,Lru,PackageUsage等。

2.4.1 handleReceiver

app.thread.scheduleReceiver()通知应用进程回调onReceive()方法。

// ActivityThread.java
private void handleReceiver(ReceiverData data) {
    Application app;
    BroadcastReceiver receiver;
    ContextImpl context;
    app = packageInfo.makeApplication(false, mInstrumentation);
    context = (ContextImpl) app.getBaseContext();
        java.lang.ClassLoader cl = context.getClassLoader();
    data.intent.setExtrasClassLoader(cl);
    data.intent.prepareToEnterProcess();
    data.setExtrasClassLoader(cl);
    receiver = packageInfo.getAppFactory()
            .instantiateReceiver(cl, data.info.name, data.intent);

    receiver.setPendingResult(data);
    receiver.onReceive(context.getReceiverRestrictedContext(),
            data.intent);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/550284.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

强化学习(三)基于动态规划 Dynamic Programming 的求解方法

文章目录 1. 动态规划与强化学习的联系2. 利用动态规划求解最优价值函数2.1 案例背景2.2 策略评估&#xff08;预测&#xff09;2.3 策略迭代&#xff08;控制&#xff09; 在前文《强化学习的数学框架&#xff1a;马尔科夫决策过程 MDP》中&#xff0c;我们用马尔可夫过程抽象…

STM32 软件I2C方式读取MT6701磁编码器获取角度例程

STM32 软件I2C方式读取MT6701磁编码器获取角度例程 &#x1f4cd;相关篇《STM32 软件I2C方式读取AS5600磁编码器获取角度例程》&#x1f33f;《Arduino通过I2C驱动MT6701磁编码器并读取角度数据》&#x1f530;MT6701芯片和AS5600从软件读取对比&#xff0c;只是读取的寄存器和…

Adobe AE(After Effects)2024下载地址及安装教程

Adobe After Effects是一款专业级别的视觉效果和动态图形处理软件&#xff0c;由Adobe Systems开发。它被广泛用于电影、电视节目、广告和其他多媒体项目的制作。 After Effects提供了强大的合成和特效功能&#xff0c;可以让用户创建出令人惊艳的动态图形和视觉效果。用户可以…

【OpenGL实验】在python、Qt5、pyOpenGL程序的若干要点

实验效果图&#xff1a; 代码 目录 一、说明二、关于QGLWidget2.1 三个方便的虚函数2.2 析构函数2.3 QGLWidget析构函数 三、关于QGLWidget的三个虚函数分工3.1 initializeGL&#xff1a;数据准备、数据绑定分离3.2 resizeGL&#xff1a;视角改变函数3.3 paintGL&#xff1a;…

苍穹外卖分类管理

分析 需求分析 SQL的写法 1 在mapper中写 2 在xml中写 Mapper public interface CategoryMapper {/*** 插入数据* param category*/AutoFill(OperationType.INSERT)Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, upd…

docker安装并跑通QQ机器人实践(2)-签名服务器bs-qsign搭建

在前文中&#xff0c;我们详尽阐述了QQ机器人的搭建过程及其最终实现的各项功能展示。接下来&#xff0c;我们将转向探讨该项目基于Docker构建服务的具体实践。本篇将以QQ机器人签名服务——qsign为起点&#xff0c;逐步展开论述。 1 获取和运行 xzhouqd/qsign:8.9.63 镜像 1.…

【K8s】:在 Kubernetes 集群中部署 MySQL8.0 高可用集群(1主2从)

【K8s】&#xff1a;在 Kubernetes 集群中部署 MySQL8.0 高可用集群&#xff08;1主2从&#xff09; 一、准备工作二、搭建nfs服务器2.1 安装 NFS 服务器软件包&#xff08;所有节点执行&#xff09;2.2 设置共享目录2.3 启动 NFS 服务器2.4 设置防火墙规则&#xff08;可选&am…

【ROS2】搭建ROS2-Humble + Vscode开发流程

【ROS2】搭建ROS2-Humble Vscode开发流程 文章目录 【ROS2】搭建ROS2-Humble Vscode开发流程1.基本环境配置2.搭建Vscode开发环境 1.基本环境配置 基本的环境配置包括以下步骤&#xff1a; 安装ROS2-Humble&#xff0c;可以参考这里安装一些基本的工具&#xff0c;可以参考…

Webrtc 信令服务器实现

webrtc建联流程图 由上图可知&#xff0c;所谓的信令服务器其实就是将peer的offer/candidate/answer传给对端而已。这样的话实现方式就有很多种了&#xff0c;目前普遍的方式HTTP/HTTPS&#xff0c;WS/WSS。像webrtc-demo-peerconnection就是实现HTTP这种方式。本文使用WS&…

APIGateway的认证

APIGateway的支持的认证如下&#xff1a; 我们从表格中可以看到&#xff0c;HTTP API 不支持资源策略的功能&#xff0c;另外是通过JWT的方式集成Cognito的。 对于REST API则是没有显示说明支持JWT认证&#xff0c;这个我们可以通过Lambda 自定义的方式来实现。 所以按照这个…

LangChain-Chatchat 开源知识库来了

LangChain-Chatchat 开源知识库来了 LangChain-Chatchat 架构设计LangChain-ChatChat 具体实现过程 一键本地离线部署软件环境硬件环境支持三种部署方式 LangChain-Chatchat 是基于 ChatGLM 等大语言模型与 LangChain 等应用框架实现&#xff0c;开源、可离线部署的 RAG 检索增…

Django数据导出与导入问题

执行: python manage.py loaddata data.json 的常见错误: * 1. UnicodeDecodeError: utf-8 codec cant decode byte 0xff in position 0: invalid start byte* 2. raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",...django.core.serializer…

vue的就地更新与v-for的key属性

vue的就地更新 Vue中的就地更新到底是怎么回事&#xff0c;为什么会存在就地更新的现象&#xff1f; 注意下面的例子&#xff0c;使用v-for指令时&#xff0c;没有绑定key值&#xff0c;才有就地更新的现象&#xff0c;因为Vue默认按照就地更新的策略来更新v-for渲染的元素列表…

windows Webrtc +VS2019 (M124)下载编译以及调通测试demo

下载depot tools 设置梯子 git config --global http.proxy 127.0.0.1:10000 git config --global https.proxy 127.0.0.1:10000 下载 $ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git 设置depot_tools目录为环境变量 下载webrtc # 设置系统代…

CommunityToolkit.Mvvm笔记1---Instruction

CommunityToolkit.Mvvm是一个官方社区套件(Windows Community Toolkit)&#xff0c;延续了MVVMLight的风格&#xff0c;是一个现代、快速和模块化的 MVVM 库。 它是 .NET 社区工具包的一部分。 第一&#xff1a;入门安装 1&#xff0c;用NuGget安装&#xff0c;搜索Community…

.NET 发布,部署和运行应用程序

.NET应用发布 发布.Net应用有很多种方式&#xff0c;下面列举三种发布方式&#xff1a; 单文件发布跨平台发布Docker发布 单文件发布 右键工程&#xff0c;选择“发布”&#xff0c;部署模式选择“独立”&#xff0c;目标运行时选择自己想要部署到的系统&#xff0c;我这里用…

【Windows10】Anaconda3安装+pytorch+tensorflow+pycharm

文章目录 一、下载anaconda0.双击下载的文件1. 选择All users2. 安装路径3. 勾选环境变量和安装python4.安装完成5.添加环境变量6.测试是否安装成功 二、安装pytorch&#xff08;先看四&#xff01;先检查一下自己电脑是不是只能安装GPU版的1.查看conda图形化界面2.在安装pytor…

ES源码二:集群启动过程

命令行参数解析 Elasticsearch&#xff1a;在main里面创建了Elasticsearch实例&#xff0c;然后调用了main方法&#xff0c;这个main方法最终会调用到父类Command的main方法 这里做了几件事&#xff1a; 注册一个 ShutdownHook&#xff0c;其作用就是在系统关闭的时候捕获IO…

使用SpringBoot3+Vue3开发公寓管理系统

项目介绍 公寓管理系统可以帮助公寓管理员更方便的进行管理房屋。功能包括系统管理、房间管理、租户管理、收租管理、房间家具管理、家具管理、维修管理、维修师傅管理、退房管理。 功能介绍 系统管理 用户管理 对系统管理员进行管理&#xff0c;新增管理员&#xff0c;修改…

CentOS 7开机启动过程,引导和服务,密码的修改

开机启动过程&#xff1a; 引导过程&#xff1a;1.开机自检(BIOS)->2.MBR引导->GRUB菜单->加载内核kernel->systemd进程初始化 程序&#xff1a;执行特定任务的一串代码&#xff0c;静态&#xff0c;存在硬盘中。 进程&#xff1a;运行中的程序叫进程&#xff0…
最新文章