IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android S动态广播注册流程(广播1) -> 正文阅读

[移动开发]Android S动态广播注册流程(广播1)

1. 动态广播注册的流程

先看一下大致文件和流程如下(activity中动态注册广播):
在这里插入图片描述

2. 新建一个动态广播接收者

广播好久之前就准备写了,最近有时间就先把广播这部分写完

这篇文章就先将动态广播注册的流程

先看一下,在一个activity中注册一个亮屏的动态广播

    //新建一个BroadcastReceiver,用于接收广播
    public BroadcastReceiver mDynamicReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //onReceive是广播处理的时候的接收方法
            Log.e("yunhen", "MyReceiver dynamic intent = " + intent.getAction());
        }
    };

	//在需要注册广播的时候调用,写下类似下面的代码
	//新建一个IntentFilter,意图过滤器
	IntentFilter filter = new IntentFilter();
	//增加亮屏的行为action
	filter.addAction(Intent.ACTION_SCREEN_ON);
	//注册广播,广播接受者是mDynamicReceiver,意图过滤器是filter
	mContext.registerReceiver(mDynamicReceiver, filter);

3. App部分的registerReceiver

  1. activity中的registerReceiver,其调用的是ContextWrapper的registerReceiver
//ContextWrapper.java
    public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) {
        return mBase.registerReceiver(receiver, filter);
    }

1.1) 至于mBase就是ContextImpl,这个又是在哪里设置的呢?
一个是在构造函数里面设置,一个是调用attachBaseContext设置,
如果是activity的mBase则是调用attachBaseContext设置

//ContextWrapper.java
    public ContextWrapper(Context base) {
        mBase = base;
    }
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

1.2) 在ActivityThread.java的performLaunchActivity(启动应用时发出的LaunchActivityItem会执行activity生命周期的内容)
会新建new ContextImpl,并进行mBase赋值
//新建new ContextImpl流程
ActivityThread.java: performLaunchActivity->createBaseContextForActivity
ContextImpl.java: ->createActivityContext->new ContextImpl

得到ContextImpl之后通过activity.attach进行mBase下一步赋值

//ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//...
//创建ContextImpl
ContextImpl appContext = createBaseContextForActivity(r);
//...
//将ContextImpl的mOuterContext设置成activity
appContext.setOuterContext(activity);
//将appContext赋值给Activity(Activity是ContextWrapper的子类)的mBase对象,
//在这里上面2个(mBase/mOuterContext)是一样的
activity.attach(appContext, this, getInstrumentation(), r.token,
        r.ident, app, r.intent, r.activityInfo, title, r.parent,
        r.embeddedID, r.lastNonConfigurationInstances, config,
        r.referrer, r.voiceInteractor, window, r.configCallback,
        r.assistToken, r.shareableActivityToken);
//...
}

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
    final int displayId = ActivityClient.getInstance().getDisplayId(r.token);
    //此处在ActivityThread将this传入ContextImpl的mMainThread
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
    //...
    return appContext;
}

//ContextImpl.java
static ContextImpl createActivityContext(ActivityThread mainThread,
        LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
        Configuration overrideConfiguration) {
    if (packageInfo == null) throw new IllegalArgumentException("packageInfo");

    String[] splitDirs = packageInfo.getSplitResDirs();
    //这里是LoadedApk的getClassLoader
    ClassLoader classLoader = packageInfo.getClassLoader();

    if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
        try {
            classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
            splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
        } catch (NameNotFoundException e) {
            // Nothing above us can handle a NameNotFoundException, better crash.
            throw new RuntimeException(e);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }

    final String attributionTag;
    if (activityInfo.attributionTags != null && activityInfo.attributionTags.length > 0) {
        attributionTag = activityInfo.attributionTags[0];
    } else {
        attributionTag = null;
    }

    //新建new ContextImpl,注意传入参数mainThread、classLoader等,还有createActivityContext函数的一些resources初始化
	//传入的UserHandle user是null
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
            attributionTag, null, activityInfo.splitName, activityToken, null, 0, classLoader,
            null);
    context.mContextType = CONTEXT_TYPE_ACTIVITY;
    context.mIsConfigurationBasedContext = true;

    // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
    displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;

    final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
            ? packageInfo.getCompatibilityInfo()
            : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;

    final ResourcesManager resourcesManager = ResourcesManager.getInstance();

    // Create the base resources for which all configuration contexts for this Activity
    // will be rebased upon.
    context.setResources(resourcesManager.createBaseTokenResources(activityToken,
            packageInfo.getResDir(),
            splitDirs,
            packageInfo.getOverlayDirs(),
            packageInfo.getOverlayPaths(),
            packageInfo.getApplicationInfo().sharedLibraryFiles,
            displayId,
            overrideConfiguration,
            compatInfo,
            classLoader,
            packageInfo.getApplication() == null ? null
                    : packageInfo.getApplication().getResources().getLoaders()));
    context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
            context.getResources());
    return context;
}

1.3) 进行mBase赋值的流程
ActivityThread.java: performLaunchActivity
Activity.java: ->attach->attachBaseContext
attachBaseContext: Activity/ContextThemeWrapper/ContextWrapper (extends继承关系)
最终在ContextWrapper中设置mBase为activity的ContextImpl

得到ContextImpl之后通过activity.attach进行mBase下一步赋值

//Activity.java
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {
        attachBaseContext(context);
        //...
    }

    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        if (newBase != null) {
            newBase.setAutofillClient(this);
            newBase.setContentCaptureOptions(getContentCaptureOptions());
        }
    }

//ContextThemeWrapper.java
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }

//ContextWrapper.java
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
  1. ContextImpl的动态注册广播流程registerReceiver

2.1) ContextImpl

//ContextImpl.java
    //我们调用的是只有2个参数的registerReceiver,第一个参数是BroadcastReceiver接受者
	//第二个参数是filter过滤器(需要接收哪个广播)
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        //将广播权限broadcastPermission设置成null,
        //scheduler调度动态广播的线程设置成null
        return registerReceiver(receiver, filter, null, null);
    }

    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        //userId当前的用户id
		//传入mOuterContext
        //flags是0
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), 0);
    }
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        //IIntentReceiver rd才是系统system_server需要发送广播到app的接口对象
        IIntentReceiver rd = null;
        //receiver如果接受者不为null,则会新建IIntentReceiver rd
        //我们例子里面是存在receiver的,至于不存在receiver的情况也是有使用场景的,这个就后面点讲
        if (receiver != null) {
            //在本例中LoadedApk mPackageInfo不为null,context也不为null
            if (mPackageInfo != null && context != null) {
                //传入的广播调度器scheduler是null
                if (scheduler == null) {
                    //那就直接使用主线程ActivityThread的handler(mH)作为调度器
                    scheduler = mMainThread.getHandler();
                }
                //从mPackageInfo中取得IIntentReceiver,并将该receiver放入LoadedApk mPackageInfo的mReceivers中
				//LoadedApk的mReceivers(可以作为检测app注册多少个广播的其中一个插装的地方)
                //这里我们先记住LoadedApk.ReceiverDispatcher.mIIntentReceiver这个东西,后面分发广播的时候有用到
                //registered = true代表是注册接受者
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                //如果mPackageInfo或者context有一个为null
                //scheduler为null的时候直接使用主线程mMainThread
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                //此处直接new一个LoadedApk.ReceiverDispatcher得到mIIntentReceiver,赋值给IIntentReceiver rd(这种没有记录在LoadedApk的mReceivers)
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            //实际调用的是AMS的registerReceiverWithFeature
            final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
                    mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
                    AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
                    flags);
            if (intent != null) {
                //如果intent不为null,设置mExtras的ClassLoader
                intent.setExtrasClassLoader(getClassLoader());
                // TODO: determine at registration time if caller is
                // protecting themselves with signature permission
                // 设置注册的是否保护的广播,还有AttributionSource
                intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),
                        getAttributionSource());
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

//LoadedApk.java
    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
            Context context, Handler handler,
            Instrumentation instrumentation, boolean registered) {
        synchronized (mReceivers) {
            LoadedApk.ReceiverDispatcher rd = null;
            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
            if (registered) {
                //从mReceivers取出对于该context的ReceiverDispatcher map
                map = mReceivers.get(context);
                if (map != null) {
                    //从map取出对于该BroadcastReceiver的ReceiverDispatcher
                    //注意: 一个BroadcastReceiver对应一个ReceiverDispatcher
                    rd = map.get(r);
                }
            }
            //如果没有BroadcastReceiver对应的ReceiverDispatcher
            if (rd == null) {
                //根据BroadcastReceiver r新建一个ReceiverDispatcher
                rd = new ReceiverDispatcher(r, context, handler,
                        instrumentation, registered);
                //此处registered是true
                if (registered) {
                    //将ReceiverDispatcher rd放入map中,而map是存储在mReceivers中的
                    if (map == null) {
                        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                        mReceivers.put(context, map);
                    }
                    map.put(r, rd);
                }
            } else {
                rd.validate(context, handler);
            }
            rd.mForgotten = false;
            //获取mIIntentReceiver
            return rd.getIIntentReceiver();
        }
    }

	ReceiverDispatcher(BroadcastReceiver receiver, Context context,
			Handler activityThread, Instrumentation instrumentation,
			boolean registered) {
		if (activityThread == null) {
			throw new NullPointerException("Handler must not be null");
		}
		//传递给AMS的就是mIIntentReceiver(IIntentReceiver.Stub对象,实现了IIntentReceiver接口)
		mIIntentReceiver = new InnerReceiver(this, !registered);
		mReceiver = receiver;
		mContext = context;
		mActivityThread = activityThread;
		mInstrumentation = instrumentation;
		mRegistered = registered;
		mLocation = new IntentReceiverLeaked(null);
		mLocation.fillInStackTrace();
	}

	//返回mIIntentReceiver
	IIntentReceiver getIIntentReceiver() {
		return mIIntentReceiver;
	}

2.2) 扩展:mReceivers广播泄露

这种泄露比较常见(registerReceiver、unregisterReceiver没有成对使用),日志如类似
*** has leaked IntentReceiver *** that was originally registered here. Are you missing a call to unregisterReceiver()

具体判断的地方是LoadedApk的removeContextRegistrations

    public void removeContextRegistrations(Context context,
            String who, String what) {
        final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
        synchronized (mReceivers) {
		//在进入这个函数之前mReceivers中这个context的内容需要情况,不然就属于没有成对出现的泄露问题
		ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
                    mReceivers.remove(context);
            if (rmap != null) {
                for (int i = 0; i < rmap.size(); i++) {
                    //取出每一个ReceiverDispatcher rd,打印日志,是不是忘记调用unregisterReceiver啦?
                    //这样可能会导致泄露哦
                    LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
                    IntentReceiverLeaked leak = new IntentReceiverLeaked(
                            what + " " + who + " has leaked IntentReceiver "
                            + rd.getIntentReceiver() + " that was " +
                            "originally registered here. Are you missing a " +
                            "call to unregisterReceiver()?");
                    leak.setStackTrace(rd.getLocation().getStackTrace());
                    Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
                    if (reportRegistrationLeaks) {
                        StrictMode.onIntentReceiverLeaked(leak);
                    }
                    try {
                        //系统代码手动帮我们调用了unregisterReceiver的方法,挺好的
                        //不过我们自己的逻辑不建议托管给系统
                        ActivityManager.getService().unregisterReceiver(
                                rd.getIIntentReceiver());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
            //这个是BroadcastReceiver setDebugUnregister设置了mDebugUnregister的时候才会有作用
            //mUnregisteredReceivers是用于判断一个BroadcastReceiver是否存在多次调用unregisterReceiver的情况
            //调试功能用,输出类似"Originally unregistered here"的日志,也挺有用的
            mUnregisteredReceivers.remove(context);
        }

4. system_server侧的广播注册

  1. 从上面的registerReceiverWithFeature接着分析,先看一下传入的参数
//ActivityManagerService.java
    //ContextImpl(registerReceiver/registerReceiverWithFeature) -> AMS registerReceiverWithFeature
	//本例中的caller: mMainThread.getApplicationThread()应用的ApplicationThread
    //callerPackage: 是调用者的包名
    //callerFeatureId: 是getAttributionTag返回的attributionTag
    //receiverId: 是获取的call name + id的string(如果是PendingIntent的话,得到的是PendingIntentRecord的lastTag)
    //receiver: 就是LoadedApk.ReceiverDispatcher用户接受广播的地方
    //filter: 是意图过滤器
    //permission: 是发送这个广播需要的权限,一般不设置,
    //如果设置了如VpnManagerService注册广播有使用NETWORK_STACK,则发送者需要有这个权限才能发送到VpnManagerService的接受者
    //userId: 是用户的组别id,如USER_OWNER/USER_SYSTEM(0)、USER_ALL(-1)、USER_CURRENT(-2)、USER_CURRENT_OR_SELF(-3)、USER_NULL(-10000)
    //而这里是activity的Process.myUserHandle()->Os.getuid(),此处是0
    //flags: 此处默认是0
    public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
            String callerFeatureId, String receiverId, IIntentReceiver receiver,
            IntentFilter filter, String permission, int userId, int flags) {
  1. 权限判断,callerApp、instantApp等的初始化
        //uid是Isolated的应用是不能调用registerReceiverWithFeature的
        enforceNotIsolatedCaller("registerReceiver");
        //stickyIntents粘性广播的意图
        ArrayList<Intent> stickyIntents = null;
        //callerApp调用者
        ProcessRecord callerApp = null;
        //visibleToInstantApps是否允许发送到InstantApps即时app
        final boolean visibleToInstantApps
                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
        //调用者的uid和pid
        int callingUid;
        int callingPid;
        //是否instantApp(即时app),在安装的时候就已经确定,PMS识别安装带有INSTALL_INSTANT_APP flag的应用
        boolean instantApp;
        synchronized(this) {
            if (caller != null) {
                //如果存在caller(IApplicationThread),则从mProcessList中取出caller对应的callerApp
                callerApp = getRecordForAppLOSP(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.getPkgList().containsKey(callerPackage)
                        && !"android".equals(callerPackage)) {
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                }
                //通过callerApp取得callingUid、callingPid
                callingUid = callerApp.info.uid;
                callingPid = callerApp.getPid();
            } else {
                //否则callerPackage设置为null, callingUid、callingPid设置成binder调用者的uid和pid
                //如MonkeyNetworkMonitor.java中 am.registerReceiverWithFeature(null, null,
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            // Android Instant App 正是这一理念的集中体现——这是一种用户无需安装即可运行 Android 应用的全新方式
            // 由于不需要事先安装应用,Instant App 能在任何场合直接抵达用户。“瞬间抵达用户” 这个概念
            // Android Instant App 需要由大小不超过 4MB 的可通过 URL 寻址的模块构建而成。
            // 如果应用大小超过 4MB,开发者就需要将应用重构为可下载的、响应 URL 导航独立运行的较小的模块
            instantApp = isInstantApp(callerApp, callerPackage, callingUid);

            //多用户广播动态注册,如果是callingUid、userId同一个用户组,则直接返回userId
            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
  1. 获取filter的action,看一下是否粘性广播stickyIntents(粘性广播这个东西也很有意思,经常听到),
    获取其filter对应的所有所有粘性广播allSticky
    粘性广播:只要该粘性广播发送过(就算是之前发送的),注册接收粘性广播的时候可以马上收到该广播,不用担心注册在发送广播之前就收不到的问题
            //遍历IntentFilter中包含的所有action
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                //如果没有action,则放入一个null的noAction到actions中,确保actions不为null
                actions = noAction.iterator();
            }

            // Collect stickies of users
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };

            //遍历所有的action
            while (actions.hasNext()) {
                String action = actions.next();
                for (int id : userIds) {
                    //遍历mStickyBroadcasts已经发送了的粘性广播列表stickies
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                    if (stickies != null) {
                        //获取这个动态注册接受者action的粘性广播
                        ArrayList<Intent> intents = stickies.get(action);
                        //如果系统发送过这个action的粘性广播
                        if (intents != null) {
                            if (stickyIntents == null) {
                                stickyIntents = new ArrayList<Intent>();
                            }
                            //则将粘性广播intents全部放入stickyIntents
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }

        //粘性广播的列表,默认是null
        ArrayList<Intent> allSticky = null;

        //如果之前已经发送过这个action的粘性广播,则stickyIntents不为null,否则为null
        if (stickyIntents != null) {
            //获取内容解析器ContentResolver
            final ContentResolver resolver = mContext.getContentResolver();
            // Look for any matching sticky broadcasts...
            //遍历粘性广播的Intent:stickyIntents
            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                //遍历粘性广播的Intent
                Intent intent = stickyIntents.get(i);
                // Don't provided intents that aren't available to instant apps.
                //如果是instantApp,而且没有单独设置接受粘性广播FLAG_RECEIVER_VISIBLE_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.
                //匹配一下注册广播的filter,是否和intent一致,如果大于0则表示匹配成功
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    //如果匹配成功则将该粘性广播intent保存在allSticky
                    allSticky.add(intent);
                }
            }
        }
  1. 如果receiver == null,而且其注册的是粘性广播,那么将粘性广播的Intent返回给app,
    如获取粘性广播的一些信息(比如电池信息),可以将receiver设为空,可以在注册的时候获取一次信息,
    这种使用场景不会有后续监听
        // The first sticky in the list is returned directly back to the client.
        //取得匹配成功后的第一个粘性广播sticky
        Intent sticky = allSticky != null ? allSticky.get(0) : null;
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);

        // 判断receiver是否为空,如果为空则直接返回找到的对应Sticky Intent。
        // 正常情况下receiver是不为空的,但是有时候为了获取粘性广播的一些信息(比如电池信息),可以将receiver设为空,
        // 只为了从返回的Sticky Intent中获取这些信息。
        // 这时的注册广播可以写成这种形式:mBatteryBroadcast = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        // 但是注意,这种形式只能在注册的时候获得一次信息,而不会有后续的继续监听。
        if (receiver == null) {
            //只在粘性广播这种注册方式才有价值,用于单次更新
            return sticky;
        }

        // SafetyNet logging for b/177931370. If any process other than system_server tries to
        // listen to this broadcast action, then log it.
        //非system_server系统进程
        if (callingPid != Process.myPid()) {
            //如果filter包含了SNOOZE_WARNING、SNOOZE_RAPID,则输入日志
            if (filter.hasAction("com.android.server.net.action.SNOOZE_WARNING")
                    || filter.hasAction("com.android.server.net.action.SNOOZE_RAPID")) {
                EventLog.writeEvent(0x534e4554, "177931370", callingUid, "");
            }
        }
  1. 构建接受者列表ReceiverList rl, mRegisteredReceivers 保存了所有动态注册的receiver.asBinder
    使用IntentFilter filter, 构建接受者列表ReceiverList rl构建BroadcastFilter bf广播过滤器,
    并把bf到mReceiverResolver中去,该变量用于在broadcastIntentLocked分发广播的时候, 查询符合条件的动态注册的广播
    这里注意:mReceiverResolver就是动态广播最终存放的地方,后面发送广播的时候就从这里找到动态注册的接受者
        synchronized (this) {
            IApplicationThread thread;
            // 取得callerApp的IApplicationThread应用线程
            if (callerApp != null && ((thread = callerApp.getThread()) == null
                    || thread.asBinder() != caller.asBinder())) {
                // Original caller already died
                //如果callerApp没有IApplicationThread(或者caller和callerApp的IApplicationThread不一致),
                // 则代表进程之前已经死掉了
                return null;
            }
            //获取这个接受者receiver,是否已经保存在AMS的mRegisteredReceivers动态注册者列表里面
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());

            //如果之前没有注册过(没有在mRegisteredReceivers中)
            if (rl == null) {
                //如果ReceiverList rl是null,则新建一个ReceiverList
                //传入参数是: AMS, callerApp, callingPid, callingUid, userId, receiver(接受者)
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    //如果callerApp不为null,获取一下当前mReceivers
                    final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
                    //一个进程最多1000个接受者(动态)
                    if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                        throw new IllegalStateException("Too many receivers, total of "
                                + totalReceiversForApp + ", registered for pid: "
                                + rl.pid + ", callerPackage: " + callerPackage);
                    }
                    //将ReceiverList rl添加到mReceivers(ProcessReceiverRecord.java)中去(有点循环的感觉)
                    rl.app.mReceivers.addReceiver(rl);
                } else {
                    //callerApp == null的情况, 如MonkeyNetworkMonitor
                    try {
                        //持有receiver的进程死亡之后会回调ReceiverList rl的binderDied
                        //binderDied会设置linkedToDeath = false;和调用AMS的unregisterReceiver(receiver),里面会有
                        // rl.receiver.asBinder().unlinkToDeath(rl, 0);的操作
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;receivers = collectReceiverComponents
                    }
                    //设置有死亡监听
                    rl.linkedToDeath = true;
                }
                //将以receiver为key,ReceiverList rl为value,保存在mRegisteredReceivers中
                //mRegisteredReceivers保存了所有的动态注册的receiver
                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);
            }

            // ReceiverList rl接收列表 (ReceiverList extends ArrayList<BroadcastFilter>),它的元素是BroadcastFilter bf
            // BroadcastFilter bf广播过滤器
            // 上面只是把广播接收着receiver的一些信息保存在ReceiverList rl中,但是还没有把它和filter关联起来,
            // 这里就创建一个BroadcastFilter(bf)来把广播接收器列表rl和filter关联起来
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                    receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps);

            //如果ReceiverList rl已经包含这个filter,则不再加入
            if (rl.containsFilter(filter)) {
                Slog.w(TAG, "Receiver with filter " + filter
                        + " already registered for pid " + rl.pid
                        + ", callerPackage is " + callerPackage);
            } else {
                //如果ReceiverList rl之前没添加过BroadcastFilter(bf),则加入接收列表中
                rl.add(bf);
                if (!bf.debugCheck()) {
                    Slog.w(TAG, "==> For Dynamic broadcast");
                }
                //同时添加bf到mReceiverResolver中去
                //该变量用于在broadcastIntentLocked分发广播的时候, 查询符合条件的动态注册的广播
                mReceiverResolver.addFilter(bf);
            }
  1. 最后就是粘性广播发送的地方, 将所有满足条件的粘性广播allSticky,发送给BroadcastFilter bf(我们认为是IIntentReceiver receiver就行了)
            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            // 粘性广播的处理:如果allSticky不为空,
            // 广播接受者注册的广播的是粘性广播。
            // 所以就将这个粘性广播添加到mParallelBroadcasts平行队列中等待调度(具体的可参考后面广播发送流程的最后几步)。
            // 最后再返回sticky
            // (如果非粘性的,则为空;反之,sticky不为空,附带着粘性广播里的一些数据)。
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                //BroadcastFilter bf才是实际的接受者,接受者receivers只有一个BroadcastFilter bf,
                //也就是单发给BroadcastFilter bf
                receivers.add(bf);

                final int stickyCount = allSticky.size();
                //遍历匹配成功的粘性广播列表allSticky
                for (int i = 0; i < stickyCount; i++) {
                    Intent intent = allSticky.get(i);
                    //取出intent对应的广播队列
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    //新建BroadcastRecord广播记录对象,_initialSticky = true, 粘性广播
                    //包含receivers(BroadcastFilter bf)
                    //_timeoutExempt = false
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, null, -1, -1, false, null, null, null, OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1, false, null,
                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                    //粘性广播接受者注册后, 马上之前发送过的粘性广播构建的BroadcastRecord r,就放入mParallelBroadcasts中
                    queue.enqueueParallelBroadcastLocked(r);
                    //这里会马上处理刚才的平行广播队列,也就是达到注册了之后马上执行的效果
                    queue.scheduleBroadcastsLocked();
                }
            }
            return sticky;
        }
    }

5. 总结一下

  1. 动态广播注册其实最终构建的是BroadcastFilter bf,并放入mReceiverResolver中去
  2. 粘性广播注册的时候就会收到之前已经发送的粘性广播
  3. 粘性广播注册可以不传入IIntentReceiver receiver,而获取一次信息
  4. 广播分发通过LoadedApk.ReceiverDispatcher.getIIntentReceiver获得的IIntentReceiver对象来实现的
  5. APP进程的mReceivers就是正常注册的时候ReceiverDispatcher保存的地方,
    配合mUnregisteredReceivers可以查看广播泄露相关信息
  6. Activity的mBase就是ContextImpl,通过createBaseContextForActivity创建
  7. 广播处理如果没有特殊设定广播处理的Handler,默认在ActivityThread的主线程(mH)中执行,
    主线程卡住会导致广播处理的延迟(超时发生广播anr)
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 12:00:36  更:2022-04-28 12:01:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 23:46:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码