redis广播模式_广播表

redis广播模式_广播表介绍BroadcastReceiver是Android的四大组件之一,它作用于应用内、进程间重要的一种通信方式,能够将某个消息通过广播的形式传递给订阅的广播接收器中,下面我们就来分析一下广播注册到接收到消息Android源码到底做了些什么?源码解析registerReceiver时序图代码讲解我们跟着上面时序图来讲解代码先在MainActivityregisterR…

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

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

介绍

BroadcastReceiver 是 Android 的四大组件之一,它作用于应用内、进程间重要的一种通信方式,能够将某个消息通过广播的形式传递给订阅的广播接收器中,下面我们就来分析一下 广播注册到接收到消息 Android 源码到底做了些什么?

源码解析

registerReceiver
时序图

redis广播模式_广播表

代码讲解

我们跟着上面时序图来讲解代码

先在 MainActivity registerReceiver 广播接收者
public class MainActivity extends Activity { 
   
      @Override
    protected void onCreate(Bundle savedInstanceState) { 
   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
          //注册广播接收者
                registerReceiver(new ReceiverB(),filterB);
    }
...代码省略...
}

点击 registerReceiver 我们发现并不是 Activity 里面的方法,而是 Activity 的父类 ContextWrapper

ContextWrapper registerReceiver
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback,
        AutofillManager.AutofillClient { 
   

          ....代码省略...

        }
public class ContextThemeWrapper extends ContextWrapper { 
   
  ....代码省略...
}
public class ContextWrapper extends Context { 
   
      @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) { 
   
        return mBase.registerReceiver(receiver, filter);
    }
}

这里的成员变量 mBase 是 Context,看过 Application 应用启动那块的源码知道 ContextImp 继承了 Context ,那么我们看继承类具体的 registerReceiver 方法吧。

ContextImp registerReceiver
class ContextImpl extends Context { 
      
@Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler, int flags) { 
   
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), flags);
    }
}
继续看 registerReceiverInternal
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) { 
   
                    //获取 ActivityThread H 
                    scheduler = mMainThread.getHandler();
                }
                //获取 IIntentReceiver 对象,通过它与 AMS 交互,并且通过 Handler 传递消息
                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 { 
   
            //调用 AMS 的 registerReceiver 
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            if (intent != null) { 
   
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) { 
   
            throw e.rethrowFromSystemServer();
        }
    }

注册广播接收器的函数最终进入到了 ContextImpl 的 registerReceiverInternal 这个函数,这里的成员变量 mPackageInfo 是一个 LoadApk 实例,它是用来负责处理广播的接收。

ActivityThread H

mMainThread.getHandler() 获取的这个 Handler 是用来分发 AMS 发过来的广播。

 private class H extends Handler { 
   
   ...
   public void handleMessage(Message msg) { 
   
   case RECEIVER:                
        handleReceiver((ReceiverData)msg.obj);
                    maybeSnapshot();

     break;
   }

   ....
 }
继续看 handleReceiver
    private void handleReceiver(ReceiverData data) { 
   
                ....

        //应用 Application 局部变量
        Application app;
        //广播局部变量
        BroadcastReceiver receiver;
        //应用 Context
        ContextImpl context;
        try { 
   
            //制作 Applicaiton
            app = packageInfo.makeApplication(false, mInstrumentation);
            //拿到上下文
            context = (ContextImpl) app.getBaseContext();
            if (data.info.splitName != null) { 
   
                context = (ContextImpl) context.createContextForSplit(data.info.splitName);
            }
            java.lang.ClassLoader cl = context.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            //通过反射进行实例化广播
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
        } catch (Exception e) { 
   
        try { 
   
            if (localLOGV) Slog.v(
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            //onReceive 进行调用
        receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
        } catch (Exception e) { 
   
           ...
        } finally { 
   
            sCurrentBroadcastIntent.set(null);
        }
                ....
    }
mPackageInfo.getReceiverDispatcher 函数实现
//LoadeApk
    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) { 
   
                map = mReceivers.get(context);
                if (map != null) { 
   
                    rd = map.get(r);
                }
            }
            if (rd == null) { 
   
                rd = new ReceiverDispatcher(r, context, handler,
                        instrumentation, registered);
                if (registered) { 
   
                    if (map == null) { 
   
                        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                        //以Context 为 key,map 为 values 存储到 mReveivers 中
                        mReceivers.put(context, map);
                    }
                    map.put(r, rd);
                }
            } else { 
   
                rd.validate(context, handler);
            }
            rd.mForgotten = false;
            return rd.getIIntentReceiver();
        }
    }

在 LoadedAPK 类中的 getReceivcerDispatcher 函数中,首先看下 r 是不是已经实例化了,如果没有就创建一个,并且以 r 为 key 值保存在一个 HM 集合中,而这个 map 又被存储在了 mReceivers 中,这样只要给定一个 Activity 和 BroadcastReceiver ,就可以查看 LoadedAPK 里面是否已经存在相应的广播接收发布器了。现在在回到 ContextImpl.registerReceiverInternal 函数,获得了 IIntentReceiver 类型的 Binder 对象后,就开始注册到 AMS 中了,具体代码看下面小点。

AMS registerReceiver
    /** * 这里是通过 Binder 通知调用 * @return Intent */
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            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;
        int callingUid;
        int callingPid;
        boolean instantApp;
        synchronized(this) { 
   
            if (caller != null) { 
   
                //1. 获取进ProcessRecord
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) { 
   
                            ...代码省略...
                }

                      ...代码省略...

            //2. 根据 Action 查找匹配的 sticky 接收器
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) { 
   
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

              ...代码省略...

            //3. 获取 ReceiverList
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) { 
   
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) { 
   
                    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) { 
   
                       ...代码省略...
            }

            //4. 构建 BroadcastFilter 对象并且添加到 ReceiverList 中
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            rl.add(bf);
            if (!bf.debugCheck()) { 
   
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            mReceiverResolver.addFilter(bf);

                 ...代码省略...

            return sticky;
        }
    }
  • 根据上面注释 1 可知,获取进程对应的 pid,uid;
  • 注释 2 获取 IntentFilter 的所有 Action;
  • 注释 3 把广播接收器的 receiver 保存到了一个 ReceiverList 中,这个列表的宿主进程是 rl.app,就是 MainActivity 所在的进程。
  • 注释 4 只是把广播接收器保存起来,但是还没有和 filter 关联起来,这里就创建一个 BroadcastFilter 来把广播接收器列表 rl 和 filter 关联起来,然后保存在 AMS 成员变量 mReceiverResolver 中,这样,就将广播接收器和要接收广播类型的接收器 filter 保存在 AMS 中了,以后就能接到到相应的广播并做处理了。

接下来我们看下 sendBroadcast() 。

onReceive
时序图

redis广播模式_广播表

在 Activity 通过 sendBroadcast 发送一个广播最后 Binder 发送给 AMS , AMS 根据这个广播的 Action 类型找到相应的广播接收器,然后把这个广播放进自己的消息队列中,完成第一部分广播异步分发。

final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, 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 userId) { 
   
        intent = new Intent(intent);

            ......代码省略....

        if (intent.getComponent() == null) { 
   

              ......代码省略....

            } else { 
   
                //查询到该 Intent 对应的 BroadcastFilter 也就是接收器列表
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);
            }
        }

          ......代码省略....

            if (!replaced) { 
   
                queue.enqueueParallelBroadcastLocked(r);
                //处理广播分发
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }

                 ......代码省略....

        return ActivityManager.BROADCAST_SUCCESS;
    }

AMS 在消息循环中处理这个广播,并通过 Binder 机制把这个广播分发给注册的 ReceiverDispatch ,ReceiverDispatch 把这个广播放进 MainActivity 所在进程的消息队列中,完成第二部分异步消息分发。

public void scheduleBroadcastsLocked() { 
   
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) { 
   
            return;
        }
        //通过 Handler 分发
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }
    private final class BroadcastHandler extends Handler { 
   
            ......
        @Override
        public void handleMessage(Message msg) { 
   
            switch (msg.what) { 
   
                case BROADCAST_INTENT_MSG: { 
   
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                    //处理下一个广播
                    processNextBroadcast(true);
                } break;
               ....
            }
        }
    }

ReceiverDispatch 的内部类 Args 在 MainActivity 所在的线程消息循环中处理这个广播,最终是将这个广播分发给注册的 Receiver 实例的 onReceiver 处理。

public final class LoadedApk { 
   
  ...
     static final class ReceiverDispatcher { 
   

           final class Args extends BroadcastReceiver.PendingResult { 
   

             ....

            public final Runnable getRunnable() { 
   
                return () -> { 
   
              ....
                    try { 
   
                        ClassLoader cl = mReceiver.getClass().getClassLoader();
                        intent.setExtrasClassLoader(cl);
                        intent.prepareToEnterProcess();
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        //回调到接收广播的 onReceiver
                        receiver.onReceive(mContext, intent);
                    } catch (Exception e) { 
   
                      ...
                };
            }
        }
     }

  ...
}

总结

注册跟接收源码分析就到这里差不多了,简单来说广播就是一个订阅 – 发布的过程,通过一些 map 存储 BroadcastReceiver ,key 就是封装了这些广播的信息类,如 Action 之类的,当发布一个广播时通过 AMS 到这个 map 中查询注册了这个广播的 IntentFilter 的 BroadcastReceiver , 然后通过 ReceiverDispatch 将广播分发给各个订阅的对象,从而完成了整个通信过程。
《如何成为一位Android架构师?(架构视频+面试专题文档+学习笔记)》

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/197284.html原文链接:https://javaforall.net

(0)
上一篇 2025年9月6日 下午10:01
下一篇 2025年9月6日 下午10:43


相关推荐

  • max31865C语言程序,max31865温度传感器通信驱动代码「建议收藏」

    max31865C语言程序,max31865温度传感器通信驱动代码「建议收藏」最近研究maximmax31865温度传感器,贴出通信驱动代码和大牛门一起学习,指点我一二max31865是专门用来测量电阻类温度传感器的,ad分辨率为15位,可以做PT100高精度测量,通信接口为SPI。通过评估板CN7的PC10,PC11,PC12(SPI3)连接芯片模块,PD2做片选。下面贴上驱动代码:1.///////////////////////////////////////…

    2022年6月14日
    40
  • MATLAB图像识别_多模态图像配准

    MATLAB图像识别_多模态图像配准基于SIFT特征的图像配准(附Matlab源代码) 本文先给出了采用SIFT方法进行图像配准的实验原图以及实验结果,最后附上Matlab源代码。 实验一:      实验一的图像(见图1.1)是本人自己拍摄的,然后由软件裁剪成400×400像素而成,其中参考图像和待配准图像之间有重叠部分,且具有一定的旋转。这是一般难度的图像配准。

    2025年5月29日
    4
  • ucGUI入门介绍

    ucGUI入门介绍ucGUI 功能 ucgUI 是一个图形库 API 你可以使用 LCD 底层的画点函数绘制一个图形 但是如果要绘制复杂的窗口 就可以使用这个图形库 nbsp nbsp nbsp nbsp nbsp nbsp nbsp ucgUI 是一种嵌入式应用中的图形支持系统 它设计用于为任何使用 LCD 图形显示的应用提供高效的独立于处理器及 LCD 控制器的图形用户接口 它适用单任务或是多任务系统环境 nbsp 并适用于任意 LCD 控制器和 CPU 下任何尺寸的真实显示或虚拟显示

    2026年3月18日
    2
  • 数组转LIst的几种方法

    数组转LIst的几种方法第一种方式/***针对数组类型转换*分别是int[]、long[]、double[],其他数据类型比如short[]、byte[]、char[],在JDK1.8中暂不支持*/List<Integer>intList=Arrays.stream(newint[]{1,2,3,}).boxed().collect(Collectors.toList());List<Long>longList=Arrays.stream(newlong[]

    2022年6月21日
    37
  • 给一组数据 怎么用matlab花折线图,matlab 画折线图 实例「建议收藏」

    给一组数据 怎么用matlab花折线图,matlab 画折线图 实例「建议收藏」数据:某地区近50年雷暴发生次数(1957~2006雷暴日):画如下的年际变化图。———————————————————————————%将数据载入数组a中a=[…..(省略)];x=1957:2006;x=x’;%ma中放平均值:ma=zeros(50,1)+mean(a);p…

    2022年5月23日
    37
  • java trylock_lock.tryLock()方法的使用

    java trylock_lock.tryLock()方法的使用packageconcurrent;importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassTestTryLock{privateListlist=…

    2022年10月16日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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