当前位置:首页 >综合 >源码分析广播运行原理 比如电池的分析使用状态

源码分析广播运行原理 比如电池的分析使用状态

2024-06-30 17:11:09 [百科] 来源:避面尹邢网

源码分析广播运行原理

作者:Android开发编程 移动开发 Android 在 Android 里面有各种各样的源码运行原理广播,比如电池的分析使用状态,电话的广播接收和短信的接收都会产生一个广播,应用程序开发者也可以监听这些广播并做出程序逻辑的源码运行原理处理。

[[436974]]

前言

在 Android 里面有各种各样的分析广播,比如电池的广播使用状态,电话的源码运行原理接收和短信的接收都会产生一个广播,应用程序开发者也可以监听这些广播并做出程序逻辑的分析处理;

今天我们就来分析下广播的运行机制

源码分析广播运行原理 比如电池的分析使用状态

一、广播原理机制详解

1、广播静态广播注册流程分析

  • 静态广播是源码运行原理通过PackageManagerService在启动的时候扫描已安装的应用去注册的;
  • 在PackageManagerService的构造方法中,会去扫描应用安装目录,顺序是先扫描系统应用安装目录再扫描第三方应用安装目录;

PackageManagerService.scanDirLI就是用于扫描目录的方法:

源码分析广播运行原理 比如电池的分析使用状态

  1. private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {  
  2.    String[] files = dir.list(); 
  3.    if (files == null) {  
  4.        return; 
  5.    } 
  6.    int i; 
  7.    for (i=0; i<files.length; i++) {  
  8.        File file = new File(dir, files[i]); 
  9.        if (!isPackageFilename(files[i])) {  
  10.            continue; 
  11.        } 
  12.        PackageParser.Package pkg = scanPackageLI(file, 
  13.                flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null); 
  14.        if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && 
  15.                mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {  
  16.            file.delete(); 
  17.        } 
  18.    } 
  19. private static final boolean isPackageFilename(String name) {  
  20.    return name != null && name.endsWith(".apk"); 
  • 可以看到,它通过File.list方法列出目录下的所有后缀为".apk"的文件传给scanPackageLI去处理;
  • 而scanPackageLI(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user)内部会调用它的重载方法;
  1. scanPackageLI(PackageParser.Package pkg,int parseFlags, int scanMode, long currentTime, UserHandle user): 
  2. private PackageParser.Package scanPackageLI(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user) {  
  3.   ... 
  4.   final PackageParser.Package pkg = pp.parsePackage(scanFile,scanPath, mMetrics, parseFlags); 
  5.   ... 
  6.   PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user); 
  7.   ... 

在这个scanPackageLIl里面会解析Package并且将AndroidManifest.xml中注册的BroadcastReceiver保存下来:

源码分析广播运行原理 比如电池的分析使用状态

  1. ... 
  2. N = pkg.receivers.size(); 
  3. r = null; 
  4. for (i=0; i<N; i++) {  
  5.    PackageParser.Activity a = pkg.receivers.get(i); 
  6.    a.info.processName = fixProcessName(pkg.applicationInfo.processName, 
  7.            a.info.processName, pkg.applicationInfo.uid); 
  8.    mReceivers.addActivity(a, "receiver"); 
  9.    ... 
  10. ... 

静态广播的流程:

  • 系统应用的广播先于第三方应用的广播注册;
  • 而安装在同一个目录下的应用的静态广播的注册顺序是按照File.list列出来的apk的顺序注册的;
  • 他们的注册顺序就决定了它们接收广播的顺序;
  • 通过静态广播的注册流程,我们已经将静态广播注册到了PackageManagerService的mReceivers中;

而我们可以使用PackageManagerService.queryIntentReceivers方法查询intent对应的静态广播

  1. public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {  
  2.    if (!sUserManager.exists(userId)) return Collections.emptyList(); 
  3.    ComponentName comp = intent.getComponent(); 
  4.    if (comp == null) {  
  5.        if (intent.getSelector() != null) {  
  6.            intent = intent.getSelector(); 
  7.            comp = intent.getComponent(); 
  8.        } 
  9.    } 
  10.    if (comp != null) {  
  11.        List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); 
  12.        ActivityInfo ai = getReceiverInfo(comp, flags, userId); 
  13.        if (ai != null) {  
  14.            ResolveInfo ri = new ResolveInfo(); 
  15.            ri.activityInfo = ai; 
  16.            list.add(ri); 
  17.        } 
  18.        return list; 
  19.    } 
  20.    synchronized (mPackages) {  
  21.        String pkgName = intent.getPackage(); 
  22.        if (pkgName == null) {  
  23.            return mReceivers.queryIntent(intent, resolvedType, flags, userId); 
  24.        } 
  25.        final PackageParser.Package pkg = mPackages.get(pkgName); 
  26.        if (pkg != null) {  
  27.            return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers, 
  28.                    userId); 
  29.        } 
  30.        return null; 
  31.    } 

2、动态广播注册流程分析

我们调用Context.registerReceiver最后会调到ActivityManagerService.registerReceiver:

  1. public Intent registerReceiver(IApplicationThread caller,分析 String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {  
  2.   ... 
  3.   ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder()); 
  4.   ... 
  5.   BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId); 
  6.   ... 
  7.   mReceiverResolver.addFilter(bf); 
  8.   ... 

所以通过mReceiverResolver.queryIntent就能获得intent对应的动态广播了;

3、发送广播流程分析

ContextImpl.sendBroadcast中会调用ActivityManagerNative.getDefault().broadcastIntent()

  1. public void sendBroadcast(Intent intent) {  
  2.     warnIfCallingFromSystemProcess(); 
  3.     String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); 
  4.     try {  
  5.         intent.prepareToLeaveProcess(); 
  6.         ActivityManagerNative.getDefault().broadcastIntent( 
  7.             mMainThread.getApplicationThread(),广播 intent, resolvedType, null, 
  8.             Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,getUserId()); 
  9.     } catch (RemoteException e) {  
  10.     } 

实际是调用ActivityManagerService.broadcastIntent:

  1. public final int broadcastIntent(IApplicationThread caller, 
  2.         Intent intent, String resolvedType, IIntentReceiver resultTo, 
  3.         int resultCode, String resultData, Bundle map, 
  4.         String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) {  
  5.     enforceNotIsolatedCaller("broadcastIntent"); 
  6.     synchronized(this) {  
  7.         intent = verifyBroadcastLocked(intent); 
  8.         final ProcessRecord callerApp = getRecordForAppLocked(caller); 
  9.         final int callingPid = Binder.getCallingPid(); 
  10.         final int callingUid = Binder.getCallingUid(); 
  11.         final long origId = Binder.clearCallingIdentity(); 
  12.         int res = broadcastIntentLocked(callerApp, 
  13.                 callerApp != null ? callerApp.info.packageName : null, 
  14.                 intent, resolvedType, resultTo, 
  15.                 resultCode, resultData, map, requiredPermission, appOp, serialized, sticky, 
  16.                 callingPid, callingUid, userId); 
  17.         Binder.restoreCallingIdentity(origId); 
  18.         return res; 
  19.     } 

调用ActivityManagerService.broadcastIntentLocked,而broadcastIntentLocked中的关键代码如下:

  1. broadcastIntentLocked 
  2. // 静态广播 
  3. List receivers = null; 
  4. // 动态广播 
  5. List<BroadcastFilter> registeredReceivers = null; 
  6. if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) 
  7.       == 0) {  
  8.    // 查询静态广播 
  9.    receivers = collectReceiverComponents(intent, resolvedType, users); 
  10. if (intent.getComponent() == null) {  
  11.    // 查询动态广播 
  12.    registeredReceivers = mReceiverResolver.queryIntent(intent, 
  13.            resolvedType, false, userId); 
  14. final boolean replacePending = 
  15.        (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0; 
  16. int NR = registeredReceivers != null ? registeredReceivers.size() : 0; 
  17. if (!ordered && NR > 0) {  
  18.    final BroadcastQueue queue = broadcastQueueForIntent(intent); 
  19.    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, 
  20.            callerPackage, callingPid, callingUid, resolvedType, requiredPermission, 
  21.            appOp, registeredReceivers, resultTo, resultCode, resultData, map, 
  22.            ordered, sticky, false, userId); 
  23.    final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r); 
  24.    if (!replaced) {  
  25.        // 发送动态广播 
  26.        queue.enqueueParallelBroadcastLocked(r); 
  27.        queue.scheduleBroadcastsLocked(); 
  28.    } 
  29.    registeredReceivers = null; 
  30.    NR = 0; 
  31. ... 
  32. if ((receivers != null && receivers.size() > 0) 
  33.     || resultTo != null) {  
  34.    BroadcastQueue queue = broadcastQueueForIntent(intent); 
  35.    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, 
  36.            callerPackage, callingPid, callingUid, resolvedType, 
  37.            requiredPermission, appOp, receivers, resultTo, resultCode, 
  38.            resultData, map, ordered, sticky, false, userId); 
  39.    boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); 
  40.    if (!replaced) {  
  41.        // 发送静态广播 
  42.        queue.enqueueOrderedBroadcastLocked(r); 
  43.        queue.scheduleBroadcastsLocked(); 
  44.    } 
  • 动态广播会优先于静态广播,从上面的代码我们可以看到;
  • 实际上静态广播静态就是从PackageManagerService中查询的:

  1. private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, 
  2.             int[] users) {  
  3.     ... 
  4.     List<ResolveInfo> newReceivers = AppGlobals.getPackageManager() 
  5.                         .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user); 
  6.     ... 

4、广播队列

  • 从ActivityManagerService.broadcastIntentLocked中我们可以看到,源码运行原理实际上它不是直接将广播发送到BroadcastReceiver中;
  • 而是将他包装到BroadcastRecord中,再放进BroadcastQueue:
  1. BroadcastQueue queue = broadcastQueueForIntent(intent); 
  2. BroadcastRecord r = new BroadcastRecord(queue, intent, null, 
  3.         null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0, 
  4.         null, null, false, true, true, -1); 
  5. queue.enqueueParallelBroadcastLocked(r); 
  6. queue.scheduleBroadcastsLocked(); 
  7. enqueueParallelBroadcastLocked方法用于并发执行广播的发送.它很简单,就是将BroadcastRecord放到了mParallelBroadcasts中: 
  8. public void enqueueParallelBroadcastLocked(BroadcastRecord r) {  
  9.     mParallelBroadcasts.add(r); 

scheduleBroadcastsLocked方法同样很简单,就是向mHandler发送了个BROADCAST_INTENT_MSG消息:

  1. public void scheduleBroadcastsLocked() {  
  2.    if (mBroadcastsScheduled) {  
  3.        return; 
  4.    } 
  5.    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this)); 
  6.    mBroadcastsScheduled = true; 

mHandler在接收到BROADCAST_INTENT_MSG消息的时候会做些什么:

  1. final Handler mHandler = new Handler() {  
  2.    public void handleMessage(Message msg) {  
  3.        switch (msg.what) {  
  4.            case BROADCAST_INTENT_MSG: {  
  5.                processNextBroadcast(true); 
  6.            } break; 
  7.            case BROADCAST_TIMEOUT_MSG: {  
  8.                synchronized (mService) {  
  9.                    broadcastTimeoutLocked(true); 
  10.                } 
  11.            } break; 
  12.        } 
  13.    } 
  14. }; 

processNextBroadcast方法用于从队列中获取广播消息并发送给BroadcastReceiver,它内部有两个分支,并行处理和串行处理;

5、并行处理

动态注册的分析非有序广播等就是使用并行处理:

  1. final void processNextBroadcast(boolean fromMsg) {  
  2.   synchronized(mService) {  
  3.       BroadcastRecord r; 
  4.       mService.updateCpuStats(); 
  5.       if (fromMsg) {  
  6.          mBroadcastsScheduled = false; 
  7.       } 
  8.       while (mParallelBroadcasts.size() > 0) {  
  9.          r = mParallelBroadcasts.remove(0); 
  10.          r.dispatchTime = SystemClock.uptimeMillis(); 
  11.          r.dispatchClockTime = System.currentTimeMillis(); 
  12.          final int N = r.receivers.size(); 
  13.          for (int i=0; i<N; i++) {  
  14.              Object target = r.receivers.get(i); 
  15.              // 发送消息给Receiver 
  16.              deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false); 
  17.          } 
  18.          addBroadcastToHistoryLocked(r); 
  19.       } 
  20.       ... 
  21.    } 
  22.    ... 
  23. private final void deliverToRegisteredReceiverLocked(BroadcastRecord r, 
  24.             BroadcastFilter filter, boolean ordered) {  
  25.     ... 
  26.     // 获取BroadcastReceiver的Binder 
  27.     r.receiver = filter.receiverList.receiver.asBinder(); 
  28.     ... 
  29.     // 使用Binder机制将消息传递给BroadcastReceiver 
  30.     performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, 
  31.                     new Intent(r.intent), r.resultCode, r.resultData, 
  32.                     r.resultExtras, r.ordered, r.initialSticky, r.userId); 
  33.     ... 
  34. void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, 
  35.         Intent intent, int resultCode, String data, Bundle extras, 
  36.         boolean ordered, boolean sticky, int sendingUser) throws RemoteException {  
  37.     ...... 
  38.         //通过Binder将消息处理传到应用进程,应用进程内部再使用Handler机制,将消息处理放到主线程中 
  39.         app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, 
  40.                         data, extras, ordered, sticky, sendingUser, app.repProcState); 
  41.     ...... 
  42.     } 

6、串行处理

有序广播和静态广播等,广播会通过enqueueOrderedBroadcastLocked传给BroadcastQueue:

  1. public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {  
  2.    mOrderedBroadcasts.add(r); 

然后在processNextBroadcast里面会对mOrderedBroadcasts进行特殊处理;

广播队列传送广播给Receiver的原理其实就是将BroadcastReceiver和消息都放到BroadcastRecord里面,然后通过Handler机制遍历BroadcastQueue里面的BroadcastRecord,将消息发送给BroadcastReceiver;

二、广播流程总结

1、Android广播分为两个方面:广播发送者和广播接收者:

  • 广播作为Android组件间的通信方式,可以使用的场景如下:
  • 同一app内部的同一组件(Component)内的消息通信(单个或多个线程之间);
  • 同一app内部的不同组件之间的消息通信(单个进程);
  • 同一app具有多个进程的不同组件之间的消息通信;
  • 不同app之间的组件之间消息通信;
  • Android系统在特定情况下与App之间的消息通信;

2、实现原理

从实现原理上看,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。具体实现流程要点粗略概括如下:

  • 广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
  • 广播发送者通过binder机制向AMS发送广播;
  • AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver相应的消息循环队列中;
  • 消息循环队列拿到此广播后,回调BroadcastReceiver中的onReceive()方法;
  • 广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心;
  • 广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到;

总结

学如逆水行舟,不进则退。心似平原跑马,易放难收;

本文转载自微信公众号「Android开发编程」

 

责任编辑:姜华 来源: Android开发编程 Android广播运行原理源码分析

(责任编辑:综合)

    推荐文章
    • 优化产品结构 中国石油2022年第一季度实现良好开局

      优化产品结构 中国石油2022年第一季度实现良好开局近日,中国石油天然气股份有限公司宣布,2022年一季度,中国石油实现营业收入7793.7亿元,实现归属于母公司股东净利润390.6亿元,生产经营继续保持良好势头。今年以来,中国石油坚持稳字当头,统筹生 ...[详细]
    • 首发RTX 40+13代i9旗舰电竞本 ROG枪神7Plus超竞版到手价仅19999元

      首发RTX 40+13代i9旗舰电竞本 ROG枪神7Plus超竞版到手价仅19999元我们就来带大家了解一下全新的枪神7 Plus超竞版此次带来了什么升级。2023年年初,ROG推出多款旗舰级游戏本,其中最值得瞩目的就是18英寸顶级游戏本-ROG枪神7Plus超竞版,最高可选13代i9 ...[详细]
    • 镂空合金超跑 雷柏VT960S屏显双模游戏鼠标到手仅239元

      镂空合金超跑 雷柏VT960S屏显双模游戏鼠标到手仅239元雷柏VT960S屏显双模无线RGB游戏鼠标不但使用体验出众,在个性化彰显上也是相当不错,相当值得推荐。雷柏V+无线游戏技术,产品力、品牌力,硬实力。原相3370光学引擎加持、1毫秒响应速度、至高125 ...[详细]
    • 长续航FPS神器 雷柏VT350S双模无线游戏鼠标到手仅239元

      长续航FPS神器 雷柏VT350S双模无线游戏鼠标到手仅239元雷柏VT350S有线无线双模RGB游戏鼠标主打长续航FPS游戏鼠标,是相当值得推荐的一款游戏鼠标。雷柏近年来不断细化产品线,推出了不少针对各种使用环境的优秀产品。特别是在游戏外设领域,不断推陈出新,对 ...[详细]
    • 农村的房子可以抵押贷款吗 需要准备的材料有哪些?

      农村的房子可以抵押贷款吗 需要准备的材料有哪些?现在贷款的非常普遍的现象了,而现在的贷款类型一般分为两种,一种是信用贷款,不需提供抵押物。而另一种就是抵押贷款的,需要贷款人提供抵押物。那么,农村的房子可以抵押贷款吗?下面来了解一下。农村的房子也是可 ...[详细]
    • 2018主流智能门锁横评:出乎意料的结局

      2018主流智能门锁横评:出乎意料的结局PChome家电频道年终横评计划横评背景与产品简介智能手机开创性的将指纹识别功能内置其中,让广大消费者对于指纹识别的安全有了深刻而积极的认知。就想智能手机一样,随着近几年物联网相关技术的发展,智能门锁 ...[详细]
    • Ace 2 Pro立功了?一加位居2.5K

      Ace 2 Pro立功了?一加位居2.5K调研机构披露的数据显示,在W348月21日-8月27日)中国智能手机中的2.5K-4K价位段,一加手机的销量市占率为18%,位居行业第一。自从回归OPPO后,一加的定位就从“小而美”变为了主打高性价比 ...[详细]
    • 利民推新TL

      利民推新TL​利民现已推出了新款TL-B12 EXTREM LCP系列风扇,采用全LCP液晶高分子聚合材料,单个装首发到手价为99元。利民现已推出了新款TL-B12 EXTREM LCP系列风扇,采用全LCP液晶 ...[详细]
    • 学校的社保卡要注销吗 注销必须要到窗口吗?

      学校的社保卡要注销吗 注销必须要到窗口吗?在如今,很多人上大学的时候都会买医疗保险,所以都会有一张社保卡,等到毕业以后,就想注销学校的社保卡,那么学校的社保卡要注销吗?学校的社保卡怎么注销?下文就来带大家了解一下。学校的社保卡一般不用注销的, ...[详细]
    • 苹果WWDC吐槽大会 iOS 17用了母公司5年前技术

      苹果WWDC吐槽大会 iOS 17用了母公司5年前技术如果你没看WWDC发布会实况,只是准备通过新闻看看苹果发了什么新品的话,那么恭喜你,成功避开了这场持续2个多小时的无聊发布会。一年一度的苹果WWDC开发者大会在今天凌晨正式开幕,不知道大家是熬夜看得直 ...[详细]
    热点阅读