android广播注册方式_安卓广播接收器

android广播注册方式_安卓广播接收器前面分析了Android系统的广播机制,从本质来说,它是一种消息订阅/发布机制。因此,使用这种消息驱动模型的第一步便是订阅消息;而对Android应用程序来说,订阅消息其实就是注册广播接收器。       接下来,我们继续分析Android应用程序是如何注册广播接收器的,以及把广播接收器注册到哪里去的。       在Android的广播机制中,ActivityManagerServi

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

        前面分析了Android系统的广播机制,从本质来说,它是一种消息订阅/发布机制。因此,使用这种消息驱动模型的第一步便是订阅消息;而对Android应用程序来说,订阅消息其实就是注册广播接收器。

        接下来,我们继续分析Android应用程序是如何注册广播接收器的,以及把广播接收器注册到哪里去的。

        在Android的广播机制中,ActivityManagerService扮演着广播中心的角色,负责系统中所有广播的注册和分发操作。因此,Android应用程序注册广播接收器的过程就是把广播接收器注册到AMS的过程。Android应用程序是通过调用ContextWrapper类的registerReceiver方法来把广播接收器BroadcastReceiver注册到AMS中去的,而ContextWrapper类本身又借助ContextImpl类来注册广播接收器。

        在Android应用程序框架中,Activity和Service类都继承了ContextWrapper类(ContextWrapper类是Context类的包装类,ContextImpl类是Context类的实现类,调用ContextWrapper类的方法也就会调用到ContextImpl类中的方法),因此,我们可以在Activity或Service的子类中调动registerReceiver方法来注册广播接收器。

        下面我们进入主题:

        静态广播直接在AndroidManifest.xml中注册一下即可。

        动态广播的注册在代码中需要调用Context类的registerReceiver方法,最终调用到ContextImpl类中的registerReceiver方法中。

ContextImpl.registerReceiverInternal

我们看下方法调用:

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        return registerReceiver(receiver, filter, null, null);
    }

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext());//broadcastPermission为null,scheduler为null
    }
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {//mPackageInfo是LoadedApk对象,在创建ContextImpl对象的时候指定了LoadedApk对象
                if (scheduler == null) {//没有设置Handler时,使用主线程的Handler
                    scheduler = mMainThread.getHandler();//这个handler用来分发AMS发送过来的广播用的
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);//获取IIntentReceiver对象,传递到AMS,用于接收广播(广播分发器)
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);//这里调用到AMS中的registerReceiver方法
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

        通过两个函数的中转,最终调用到ContextImpl.registerReceiverInternal()。我们先看一下这几个参数,receiver表示用户注册广播接收器;userId注册广播应用程序的userID;filter为广播接收器的接收条件;broadcastPermission广播接收器的权限信息,广播发送者必须带上这个权限信息,它所发出的广播才能被注册者接收到;scheduler表示接收广播的线程的Handler。

        方法中使用到了成员变量mPackageInfo是一个LoadedApk实例,它是用来负责处理广播的接收的,在后面一篇讲到广播的发送时(sendBroadcast),会详细描述。参数broadcastPermission和scheduler都为null,而参数context是上面的方法通过调用getOuterContext()得到的,这里它就是指向注册广播的Activity了,因为Activity是继承于Context类的,因此,这里用Context类型来引用。

        由于条件mPackageInfo != null和context != null都成立,而且条件scheduler == null也成立,于是就调用mMainThread.getHandler来获得一个Handler了,这个Hanlder是后面用来分发ActivityManagerService发送过的广播用的。这里的成员变量mMainThread是一个ActivityThread实例。我们先来看看ActivityThread.getHandler函数的实现,然后再回过头来继续分析ContextImpl.registerReceiverInternal函数。

ActivityThread.getHandler()

public final class ActivityThread {
	......

	final H mH = new H();

	private final class H extends Handler {
		......

		public void handleMessage(Message msg) {
			......

			switch (msg.what) {
			......
			}

			......
		}

		......

	}

	......

	final Handler getHandler() {
		return mH;
	}

	......

}

        有了这个Handler之后,就可以分发消息给应用程序处理了。

        再回到上一步的ContextImpl.registerReceiverInternal函数中,它通过mPackageInfo.getReceiverDispatcher函数获得一个IIntentReceiver接口对象rd,这是一个Binder对象,接下来会把它传给ActivityManagerService,ActivityManagerService在收到相应的广播时,就是通过这个Binder对象来通知MainActivity来接收的。

        我们也是先来看一下mPackageInfo.getReceiverDispatcher函数的实现,然后再回过头来继续分析ContextImpl.registerReceiverInternal函数。

LoadedApk.getReceiverDispatcher

    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) {//上面传递的参数为true                map = mReceivers.get(context);//根据key值获取对应的value,key为context对象                if (map != null) {                    rd = map.get(r);//根据key值,获取对应的value;key为BroadcastReceiver,value为ReceiverDispatcher                }            }            if (rd == null) {                rd = new ReceiverDispatcher(r, context, handler,                        instrumentation, registered);//创建LoadedApk.ReceiverDispatcher                if (registered) {                    if (map == null) {                        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();                        mReceivers.put(context, map);                    }                    map.put(r, rd);//将创建的LoadedApk.ReceiverDispatcher保存到map数组中                }            } else {                rd.validate(context, handler);            }            rd.mForgotten = false;            return rd.getIIntentReceiver();//获取IIntentReceiver.stub对象(InnerReceiver)        }    }
    private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers        = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

     在LoadedApk.getReceiverDispatcher函数中,首先看一下参数r是不是已经有相应的ReceiverDispatcher存在了,如果有,就直接返回了;否则就新建一个ReceiverDispatcher,并且以r为Key值保在一个HashMap中,而这个HashMap以Context(注册广播接收器的Activity)为Key值保存在LoadedApk的成员变量mReceivers中。这样,只要给定一个Activity和BroadcastReceiver,就可以查看LoadedApk里面是否已经存在相应的广播接收分发器ReceiverDispatcher了。

final class LoadedApk {	......	 static final class ReceiverDispatcher {		 final static class InnerReceiver extends IIntentReceiver.Stub {			 final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;			......			 InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {				 mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);				......			 }			......		}		......		final IIntentReceiver.Stub mIIntentReceiver;//接收AMS发送的广播		final Handler mActivityThread;//用于分发广播		......		ReceiverDispatcher(BroadcastReceiver receiver, Context context,				Handler activityThread, Instrumentation instrumentation,				boolean registered) {			......			mIIntentReceiver = new InnerReceiver(this, !registered);//创建InnerReceiver对象			mActivityThread = activityThread;					......		}		......		IIntentReceiver getIIntentReceiver() {//getIIntentReceiver方法获取IIntentReceiver.Stub对象			return mIIntentReceiver;		}	}	......}

        在新建广播接收分发器ReceiverDispatcher时,会在构造函数里面创建一个InnerReceiver实例,这是一个Binder对象,实现了IIntentReceiver接口,可以通过ReceiverDispatcher.getIIntentReceiver函数来获得,获得后就会把它传给ActivityManagerService,以便接收广播。在ReceiverDispatcher类的构造函数中,还会把传进来的Handle类型的参数activityThread保存下来,以便后面在分发广播的时候使用。       现在,再回到ContextImpl.registerReceiverInternal函数,在获得了IIntentReceiver类型的Binder对象后,就开始要把它注册到ActivityManagerService中去了。

ActivityManagerService.registerReceiver()

        这个方法逻辑比较清楚,但是涉及到很多变量,我们先梳理一下这几个变量:

        mStickyBroadcasts:保存了当前系统中所有用户的Sticky广播,key为广播的action,value为该action对应的所有广播Intents;

    /**
     * State of all active sticky broadcasts per user.  Keys are the action of the每个用户的所有sticky广播
     * sticky Intent, values are an ArrayList of all broadcasted intents with内层key值是sticky广播的action,value是action对应的所有Intents
     * that action (which should usually be one).  The SparseArray is keyed
     * by the user ID the sticky is for, and can include UserHandle.USER_ALL
     * for stickies that are sent to all users.最外层的key值是userID(包括UserHandle.USER_ALL)
     */
    final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
            new SparseArray<ArrayMap<String, ArrayList<Intent>>>();

  mRegisteredReceivers:保存了已经注册的所有广播;

    mReceiverResolver:保存当前系统注册的所有的BroadcastFilter;

    /**
     * Resolver for broadcast intents to registered receivers.已注册广播的Intents解析器
     * Holds BroadcastFilter (subclass of IntentFilter).保存BroadcastFilter
     */
    final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
            = new IntentResolver<BroadcastFilter, BroadcastFilter>()

        BroadcastRecord:需要发送的一条广播记录,里面的receivers成员存储了需要接收当前广播的所有接收器。

        下面我们分块来分析registerReceiver方法。

过滤出与当前注册的IntentFilter中的action匹配的所有的Intent

        第一步就是先找出与注册action相匹配的所有的Intent。

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        enforceNotIsolatedCaller("registerReceiver");
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        int callingUid;
        int callingPid;
        synchronized(this) {
            if (caller != null) {
                callerApp = getRecordForAppLocked(caller);//获取ProcessRecord对象
                ......
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);//获取用户ID

            Iterator<String> actions = filter.actionsIterator();//获取当前IntentFilter中所有的actions
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

            // Collect stickies of users收集与注册用户(userId)相关的所有已经被广播过的Intents,存储在stickyIntents中
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };//包括所有用户,以及注册进程所对应的用户
            while (actions.hasNext()) {//第一层:遍历actions
                String action = actions.next();
                for (int id : userIds) {//第二层:遍历与调用进程相关的用户id
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);//用户id发送过的所有的sticky广播
                    if (stickies != null) {
                        ArrayList<Intent> intents = stickies.get(action);//获取action对应的所有广播Intents
                        if (intents != null) {//如果已经发送的Intents里面包含上面的action,则保存在stickyIntents中
                            if (stickyIntents == null) {
                                stickyIntents = new ArrayList<Intent>();
                            }
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }

        从mStickyBroadcasts里面找出发送给所有用户和调用者userID的所有的Sticky广播的Intents,然后和当前注册的IntentFilter中所有的action逐个比较,找出符合的所有的intent,存储在stickyIntents里面。注意,这里仅仅通过action进行了一次筛选。

从stickyIntents里面找出与当前注册的IntentFilter匹配的Intent

        上面第一步找出了与注册的action相匹配的Intent,然后在这些Intent中再进行精确匹配,找出精确匹配的Intent。

        上面根据action对相应用户的stickyIntents进行了一次过滤,下面根据IntentFilter.match方法对筛选出来的stickyIntents进行精确的匹配,包括action、type、scheme、data、categories等,将最终的匹配结果存放在allSticky里面。如果调用registerReceiver方法传递的receiver是null,就返回allSticky中的第一个匹配的Intent,或者返回null。

        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++) {//再把stickyIntents里面的intent逐个比较
                Intent intent = stickyIntents.get(i);
                // 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) {//match方法中会对action、type、scheme、data等进行比较
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    allSticky.add(intent);
                }
            }
        }

        // The first sticky in the list is returned directly back to the client.查找的是stickyIntent,
        Intent sticky = allSticky != null ? allSticky.get(0) : null;//如果存在满足action条件的已经广播的intent,立即发送一次广播
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
        if (receiver == null) {
            return sticky;
        }

       
上面的两部分主要从sticky广播的历史记录中筛选出与当前注册的IntentFilter匹配的历史Intent,将最终的结果保存在allSticky中。如果注册的广播是非sticky广播,一般来说allSticky为null,但是对于网络切换这种发送的时候是以sticky广播形式发送的,这里allSticky就不是null。

在注册记录中查找当前的receiver

        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());//以receiver为key值,获取对应的IntentFilter列表,初始为null
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);//创建ReceiverList对象
                if (rl.app != null) {//ProcessRecord
                    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);//保存receiver对应的ReceiverList
            } else if (rl.uid != callingUid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for uid " + callingUid
                        + " was previously registered for uid " + rl.uid);
            } else if (rl.pid != callingPid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for pid " + callingPid
                        + " was previously registered for pid " + rl.pid);
            } else if (rl.userId != userId) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for user " + userId
                        + " was previously registered for user " + rl.userId);
            }

        首先判断当前进程是否还活着;然后从当前系统所有已经动态注册的mRegisteredReceivers中查找当前注册的receiver,返回对应的ReceiverList,该类继承了ArrayList<BroadcastFilter>,语义就是一个receiver可以对应着一串BroadcastFilter,所以说对于同一个receiver对象而言,可以多次调用registerReceiver方法注册不同的广播条件BroadcastFilter,这里面是注册动作中最关键的地方,将receiver以及对应的ReceiverList存储到AMS.mRegisteredReceivers中

将当前注册的广播过滤器放到ReceiverList里面

            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);//根据filter创建BroadcastFilter(IntentFilter)广播过滤器
            rl.add(bf);//将BroadcastFilter加入ReceiverList中
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            mReceiverResolver.addFilter(bf);//将bf添加到mReceiverResolver中,当AMS接收到广播时就可以从mReceiverResolver中找到接收者

        根据当前的IntentFilter创建BroadcastFilter对象,BroadcastFilter继承于IntentFilter,基本上和IntentFilter没有太大的区别,里面还包含对ReceiverList的引用。

        创建完BroadcastFilter后,将其加到mReceiverResolver中,内部用的是一个ArraySet,说明重复添加完全相同的BroadcastFilter对象不会多次添加。到这里没有匹配的sticky广播已经注册结束了。

        我们这里再看一下mReceiverResolver这个变量,mReceiverResolver变量用来存储系统应用进程中所有receiver动态注册的所有的BroadcastFilter,也就是说动态注册的BroadcastReceiver最终保存到了这里。

发送匹配过滤器的sticky广播

            // Enqueue broadcasts for all existing stickies that match
            // this filter.如果存在满足条件的sticky intents,需要立即发送这些广播
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                final int stickyCount = allSticky.size();
                for (int i = 0; i < stickyCount; i++) {//遍历allSticky,逐个发送Intent
                    Intent intent = allSticky.get(i);
                    BroadcastQueue queue = broadcastQueueForIntent(intent);//获取BroadcastQueue
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1);//创建BroadcastRecord
                    queue.enqueueParallelBroadcastLocked(r);//将BroadcastRecord对象条件到BroadcastQueue.mParallerlBraodcasts数组中
                    queue.scheduleBroadcastsLocked();//尝试推动一次发送广播的行为,但是如果目前有广播还在发送的处理过程中,本次尝试失败
                }
            }

            return sticky;

        上面注册结束以后,如果筛选出与当前注册的IntentFilter匹配的sticky广播的Intent,就将所有匹配的Intent逐条发送广播给当前的注册者receiver,可以看到这里的接收者receivers里面就只有当前创建的一个BroadcastFilter,也就是当前的注册者。

        上面就是动态广播注册的整个过程,主要就是将当前注册的动态广播接收器以及对应的广播过滤器BroadcastFilter添加到AMS.mRegisteredReceivers和AMS.mReceiverResolver中,同时处理了与sticky广播的相关逻辑从这里可以看到对于注册sticky广播而言,在注册结束以后,系统会立马发送与之匹配的sticky广播。

        下面继续分析广播发送流程:Android广播管理三–广播发送(sendBroadcast)流程分析

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/194949.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 谷歌浏览器驱动国内镜像下载地址[通俗易懂]

    谷歌浏览器驱动国内镜像下载地址[通俗易懂]谷歌驱动(driverchrome.exe)国内镜像下载地址:http://npm.taobao.org/mirrors/chromedriver/Windows、Linux、MAC系统均可下载下载解压,放到C:\ProgramFiles(x86)\Google\Chrome\Application(自己的谷歌浏览器安装路径)下即可…

  • 详解 Pytorch 实现 MNIST[通俗易懂]

    MNIST虽然很简单,但是值得我们学习的东西还是有很多的。项目虽然简单,但是个人建议还是将各个模块分开创建,特别是对于新人而言,模块化的创建会让读者更加清晰、易懂。CNN模块:卷积神经网络的组成;train模块:利用CNN模型对MNIST数据集进行训练并保存模型test模块:加载训练好的模型对测试集数据进行测试cnn.pt:train的CNN模型注意!有GPU的小伙伴尽量使用GPU训练,GPU的训练速度比CPU的训练速度高许多倍,可以节约大量训练时间CNN模块MN

  • macbook如何安装双系统_双系统安装方法

    macbook如何安装双系统_双系统安装方法1.官网https://www.microsoft.com/zh-cn/software-download/windows10ISO进入官网下载win10(因为win10仅支持win10专业版不要下家庭中文版)语言:简体中文64位2.用迅雷下否则直接下载时间会很长Mac迅雷-轻体验,大改变(拖动迅雷至文件夹,即可安装,macOS10.15安装后请到应用程序找到图标,右键打开)点击迅雷的加号新建任务在浏览器复制之后自动弹出迅雷下载对话保存位置至桌面点击下载打开浏览器.

  • 程序员喜欢穿格子衫的原因_为什么程序员喜欢穿格子衬衫

    程序员喜欢穿格子衫的原因_为什么程序员喜欢穿格子衬衫点击上方关注我们,让小care关爱你!在互联网时代下,今天的你不用在路边拦车,不用带现金出门,用一个手机解决生活所有的问题。这一切都离不开”程序员”的夜以继日传说,中文互联网有三大杀器:处女座、五仁月饼和格子衬衫,只要集齐它们就可以破碎山河撕裂人伦纵横四海。不知道从什么时候起,格子衬衫已经成为了程序员的代名词,有些时候,就连“程序媛”也无法幸免。(你猜此刻的小姐姐是怎样的表情)那其实你知…

  • mysql 联合索引生效的条件、索引失效的条件

    mysql 联合索引生效的条件、索引失效的条件

  • Java数组转List的三种方式及对比

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 作者:大脑补丁 blog.csdn.net/x541211190/article/details/79597236 …

发表回复

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

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