Android广播管理三--广播发送(sendBroadcast)流程分析

2023-05-16

        前面我们分析了Android应用程序注册广播接收器的过程,接下来它还要等待ActivityManagerService将广播分发过来。ActivityManagerService是如何得到广播并把它分发出去的呢?

         广播的发送者将广播发送到ActivityManagerService,ActivityManagerService接收到这个广播以后,就会在自己的注册中心查看有哪些广播接收器订阅了该广播,然后把这个广播逐一发送到这些广播接收器中,但是ActivityManagerService并不等待广播接收器处理这些广播就返回了,因此,广播的发送和处理是异步的。概括来说,广播的发送路径就是从发送者到ActivityManagerService,再从ActivityManagerService到接收者,这中间的两个过程都是通过Binder进程间通信机制来完成的

发送广播

ContextWrapper.sendBroadcast

        这个函数定义在frameworks/base/core/java/android/content/ContextWrapper.java文件中:

public class ContextWrapper extends Context {  
    Context mBase;  
  
    ......  
  
    @Override  
    public void sendBroadcast(Intent intent) {  
        mBase.sendBroadcast(intent);  
    }  
  
    ......  
  
} 
         我们上文介绍了ContextWrapper类是Context类的封装类,ContextImpl是Context类的实现类。这里的成员变量mBase是一个ContextImpl实例,这里只简单地调用ContextImpl.sendBroadcast进一行操作。

ContextImpl.sendBroadcast

         这个函数定义在frameworks/base/core/java/android/app/ContextImpl.java文件中:

public void sendBroadcast(Intent intent) {
    ............
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        //StrictMode下,对一些Action需要进行检查
        intent.prepareToLeaveProcess(this);

        //调用AMS中的接口
        ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    } catch(RemoteException e) {
        ..............
    }
}
        这里的resolvedType表示这个Intent的MIME类型,如果没有设置这个Intent的MIME类型,resolvedType为null。接下来就调用ActivityManagerService的远程接口ActivityManagerProxy把这个广播发送给ActivityManagerService了。

ActivityManagerProxy.broadcastIntent

        这个函数定义在frameworks/base/core/java/android/app/ActivityManagerNative.java文件中:

    class ActivityManagerProxy implements IActivityManager  
    {  
        ......  
      
        public int broadcastIntent(IApplicationThread caller,  
            Intent intent, String resolvedType,  IIntentReceiver resultTo,  
            int resultCode, String resultData, Bundle map,  
            String requiredPermission, boolean serialized,  
            boolean sticky) throws RemoteException  
        {  
            Parcel data = Parcel.obtain();  
            Parcel reply = Parcel.obtain();  
            data.writeInterfaceToken(IActivityManager.descriptor);  
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);  
            intent.writeToParcel(data, 0);  
            data.writeString(resolvedType);  
            ......
            mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);  
            reply.readException();  
            int res = reply.readInt();  
            reply.recycle();  
            data.recycle();  
            return res;  
        }  
      
        ......  
      
    }  
        这里主要是把要传递的参数封装好,然后通过Binder驱动程序进入到ActivityManagerService的broadcastIntent()方法中。

ActivityManagerService.broadcastIntent

         这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

    public final class ActivityManagerService extends ActivityManagerNative  
            implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
        ......  
      
        public final int broadcastIntent(IApplicationThread caller,  
                Intent intent, String resolvedType, IIntentReceiver resultTo,  
                int resultCode, String resultData, Bundle map,  
                String requiredPermission, boolean serialized, boolean sticky) {  
            synchronized(this) {//verifyBroadcastLocked方法检查Broadcast中intent携带的信息是否有问题(不能携带文件描述符,避免安全隐患)
                intent = verifyBroadcastLocked(intent);//同时检查intent的flag
      
                final ProcessRecord callerApp = getRecordForAppLocked(caller);//获取调用方(发送广播)的ProcessRecord
                final int callingPid = Binder.getCallingPid();  
                final int callingUid = Binder.getCallingUid();  
                final long origId = Binder.clearCallingIdentity();  
                int res = broadcastIntentLocked(callerApp,  
                    callerApp != null ? callerApp.info.packageName : null,  
                    intent, resolvedType, resultTo,  
                    resultCode, resultData, map, requiredPermission, serialized,  
                    sticky, callingPid, callingUid);  
                Binder.restoreCallingIdentity(origId);  
                return res;  
            }  
        }  
      
        ......  
    }  
        这里注意几个参数:

        serialized:表示当前广播是否是order广播,true代表order广播(有序广播);

        sticky:表示当前广播是否是sticky广播,true代表sticky广播(粘性广播);

        broadcastIntent中对Broadcast对应的Intent进行一些检查后,调用broadcastIntentLocked进行实际的处理。

ActivityManagerService.broadcastIntentLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中,代码量较大,分段分析。

Part I

final int broadcastIntentLocked(.....) {
    intent = new Intent(intent);

    // By default broadcasts do not go to stopped apps.
    // Android系统对所有app的运行状态进行了跟踪
    // 当应用第一次安装但未使用过,或从程序管理器被强行关闭后,将处于停止状态

    // 在发送广播时,不管是什么广播类型,系统默认增加了FLAG_EXCLUDE_STOPPED_PACKAGES的flag
    // 使得对于所有BroadcastReceiver而言,如果其所在进程的处于停止状态,该BroadcastReceiver将无法接收到广播
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

    //大量条件检查,例如:
    //有些Action只能由系统来发送;
    //有些Action需要发送方申明了对应权限
    ...................

    //某些Action,例如Package增加或移除、时区或时间改变、清除DNS、代理变化等,需要AMS来处理
    //AMS需要根据Action,进行对应的操作
    ..................
}

1、排除Stopped状态的应用

        // By default broadcasts do not go to stopped apps.
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
        所有的广播Intent在这里都会默认加上这个标记,表示 所有的广播都不会发送到Stopped状态的应用,应该在发送的时候会检查应用当前的状态。

2、系统升级广播

        // If we have not finished booting, don't allow this to launch new processes.
        if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        }
         Intent.FLAG_RECEIVER_BOOT_UPGRADE是系统升级的flag,允许在系统启动前发送。只有注册了的接收者被调用,所有的BroadcastReceiver组件不会被加载。

3、处理受保护广播

        // Verify that protected broadcasts are only being sent by system code,受保护的广播只能有系统发送
        // and that system code is only sending protected broadcasts.
        final String action = intent.getAction();
        final boolean isProtectedBroadcast;
        try {
            isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
        } catch (RemoteException e) {
            Slog.w(TAG, "Remote exception", e);
            return ActivityManager.BROADCAST_SUCCESS;
        }
        受保护广播是通过查询PMS得到的,如果是受保护广播,抛出异常后直接返回ActivityManager.BROADCAST_SUCCESS,不会进行下面的动作。

4、判断发送者是否是系统进程

        final boolean isCallerSystem;
        switch (UserHandle.getAppId(callingUid)) {//如果是root、system、phone、bluetooth、nfc等的UID,则广播不受限制
            case Process.ROOT_UID:
            case Process.SYSTEM_UID:
            case Process.PHONE_UID:
            case Process.BLUETOOTH_UID:
            case Process.NFC_UID:
                isCallerSystem = true;
                break;
            default:
                isCallerSystem = (callerApp != null) && callerApp.persistent;
                break;
        }
        isCallerSystem表示系统级用户发送的广播,这部分广播不受限制。

5、处理特定系统广播

        if (action != null) {
            switch (action) {
                case Intent.ACTION_UID_REMOVED:
                case Intent.ACTION_PACKAGE_REMOVED:
                case Intent.ACTION_PACKAGE_CHANGED:
                case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
                case Intent.ACTION_PACKAGES_SUSPENDED:
                case Intent.ACTION_PACKAGES_UNSUSPENDED:
                    // Handle special intents: if this broadcast is from the package
                    // manager about a package being removed, we need to remove all of
                    // its activities from the history stack.
        对于一些来自PMS的包状态的变化,AMS需要及时的处理相关的Activity,这里是因为AMS兼顾了所有的4大组件,当包的状态发生变化,AMS作为总管需要第一时间内处理完总管要做的事情,然后将对应的广播再转发给对应的应用。

        发送广播的第一阶段主要工作有:

        (1).根据广播对应的Intent中的信息,判断发送方是否有发送该广播的权限;

        (2).检查发送的广播是否是一些特殊的系统广播,特别是从PackageManagerService中发出的有个安装的应用移除的广播,如果检测到,需要将这些包中的Activity从AMS的Activity栈中移除。

Part II

..............
// Add to the sticky list if requested.
// 处理粘性广播相关的内容
if (sticky) {
    //检查是否有发送权限,在AndroidManifest.xml中必须声明android.permission.BROADCAST_STICKY权限
    if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
            callingPid, callingUid)
            != PackageManager.PERMISSION_GRANTED) {
        ..................//抛出SecurityException异常
    }

    //粘性广播不能指定接收权限,即发送时不能有requiredPermission权限信息
    if (requiredPermissions != null && requiredPermissions.length > 0) {
        ..............
        return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
    }

    if (intent.getComponent() != null) {
        //发送的intent里面不能指定接收者,即粘性广播不能指定接收方
        ............
    }

    // We use userId directly here, since the "all" target is maintained
    // as a separate set of sticky broadcasts.
    //当粘性广播是针对特定userId时,判断该粘性广播与系统保存的是否冲突
    if (userId != UserHandle.USER_ALL) {

        //取出发送给所有user的粘性广播,关于mStickyBroadcasts的介绍可参考上一篇博客
        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                UserHandle.USER_ALL);
        if (stickies != null) {
            ArrayList<Intent> list = stickies.get(intent.getAction());
            if (list != null) {
                int N = list.size();
                int i;
                for (i=0; i<N; i++) {
                    //发送给特定user的粘性广播,与发送给所有user的粘性广播,action一致时,发生冲突
                    if (intent.filterEquals(list.get(i))) {
                        //抛出异常
                        .............
                    }
                }
            }
        }

        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
        if (stickies == null) {
            stickies = new ArrayMap<>();
            //按userId保存粘性广播
            //即一个user,可能有多个粘性广播
            mStickyBroadcasts.put(userId, stickies);
        }

        //按照Action保存粘性广播
        //即一个Action,可能对应中多个广播
        ArrayList<Intent> list = stickies.get(intent.getAction());
        if (list == null) {
            //list为null时,直接加入
            list = new ArrayList<>();
            stickies.put(intent.getAction(), list);
        }
        final int stickiesCount = list.size();
        int i;
        for (i = 0; i < stickiesCount; i++) {
            //新的粘性广播与之前的重复,则保留新的
            //即当发送多个相同的粘性广播时,系统仅会保留最新的
            if (intent.filterEquals(list.get(i))) {
                // This sticky already exists, replace it.
                list.set(i, new Intent(intent));
                break;
            }
        }
        if (i >= stickiesCount) {
            //不重复时,直接加入
            list.add(new Intent(intent));
        }
    }
}
...............

        broadcastIntentLocked方法第二部分主要是处理粘性广播的,判断发送粘性广播的条件是否满足,然后就粘性广播保存起来,将其保存到AMS的mStickyBroadcasts变量里面,上一篇博客中分析了mStickyBroadcasts是以用户id作为key保存的,首先取出当前用户的所有sticky广播,然后根据当前广播的action保存到action对应的List里面即可。里面还有一个细节,就是如果当前intent和list中的某个intent用filterEquals()比较相等,就直接替换掉以前的,否则直接添加到list末尾。

Part III

.............
// Figure out who all will receive this broadcast.弄清楚谁会接收这个广播
//receivers主要用于保存匹配当前广播的静态注册的BroadcastReceiver
//若当前广播是有序广播时,还会插入动态注册的BroadcastReceiver
List receivers = null;

//registeredReceivers用于保存匹配当前广播的动态注册的BroadcastReceiver 
//BroadcastFilter中有对应的BroadcastReceiver的引用
List<BroadcastFilter> registeredReceivers = null;

// Need to resolve the intent to interested receivers...
// 若设置了FLAG_RECEIVER_REGISTERED_ONLY,那么只有此时完成了注册的BroadcastReceiver才会收到信息
// 简单讲就是,有FLAG_RECEIVER_REGISTERED_ONLY时,不通知静态注册的BroadcastReceiver

// 此处处理未设置该标记的场景
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
    //利用PKMS的queryIntentReceivers接口,查询满足条件的静态BroadcastReceiver
    receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}

//广播没有指定特定接收者时
if (intent.getComponent() == null) {
    //这里的要求比较特殊,针对所有user,且从shell发送的广播
    //即处理调试用的广播
    if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
        // Query one target user at a time, excluding shell-restricted users
        for (int i = 0; i < users.length; i++) {
            //user不允许调试时,跳过
            if (mUserController.hasUserRestriction(
                    UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                continue;
            }

            //得到当前user对应的满足Intent要求的BroadcastReceiver
            //mReceiverResolver中保存的都是动态注册的BroadcastReceiver对应的BroadcastFilter
            List<BroadcastFilter> registeredReceiversForUser =
                    mReceiverResolver.queryIntent(intent,
                            resolvedType, false, users[i]);

            if (registeredReceivers == null) {
                registeredReceivers = registeredReceiversForUser;
            } else if (registeredReceiversForUser != null) {
                registeredReceivers.addAll(registeredReceiversForUser);
            }
        }
    } else {
        //一般没有指定接收者的广播,都会走到这里,这是通常的处理流程
        registeredReceivers = mReceiverResolver.queryIntent(intent,
                resolvedType, false, userId);
    }
}

//检查广播中是否有REPLACE_PENDING标签
//如果设置了这个标签,那么新的广播可以替换掉AMS广播队列中,与之匹配的且还未被处理的旧有广播
//这么做的目的是:尽可能的减少重复广播的发送
final boolean replacePending =
        (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
.....................

//先处理动态注册的BroadcastReceiver对应的广播
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.
    //根据Intent的Flag决定BroadcastQueue,获取前台或后台的广播队列
    final BroadcastQueue queue = broadcastQueueForIntent(intent);

    //构造广播对应的BroadcastRecord
    //BroadcastRecord中包含了该广播的所有接收者
    BroadcastRecord r = new BroadcastRecord(........);
    ................
    //设置了REPLACE_PENDING标签,同时与旧有广播匹配时,才会进行替换
    //若能够替换,replaceParallelBroadcastLocked方法中就会将新的广播替换到队列中
    final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);

    if (!replaced) {
        //没有替换时,才需要将新的广播加入到BroadcastQueue.mParallelBroadcasts队列中
        queue.enqueueParallelBroadcastLocked(r);//这里非常重要,也就是说动态广播都放在了BroadcastQueue.mParallelBroadcasts中

        //触发广播发送流程
        queue.scheduleBroadcastsLocked();
    }
    registeredReceivers = null;
    NR = 0;
}
.....................
        broadcastIntentLocke第三部分的工作主要包括:

        1、查询与当前广播匹配的静态和动态BroadcastReceiver;

        2、若当前待发送的广播是无序的,那么为动态注册的BroadcastReceiver,构造该广播对应的BroadcastRecord加入到发送队列中,并触发广播发送流程;最后将动态广播接收器置为null。

        从这部分代码可以看出,对于无序广播而言,动态注册的BroadcastReceiver接收广播的优先级,高于静态注册的BroadcastReceiver。

Part IV

.................
// Merge into one list.将receivers和registerReceiver合并到一个列表(receivers)中。
int ir = 0;
if (receivers != null) {//静态BroadcastReceiver
    // A special case for PACKAGE_ADDED: do not allow the package
    // being added to see this broadcast.  This prevents them from
    // using this as a back door to get run as soon as they are
    // installed.  Maybe in the future we want to have a special install
    // broadcast or such for apps, but we'd like to deliberately make
    // this decision.
    //处理特殊的Action,例如PACKAGE_ADDED,系统不希望有些应用一安装就能启动
    //APP安装后,PKMS将发送PACKAGE_ADDED广播
    //若没有这个限制,在刚安装的APP内部静态注册监听该消息的BroadcastReceiver,新安装的APP就能直接启动
    String skipPackages[] = null;
    if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
            || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
            || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
        Uri data = intent.getData();
        if (data != null) {
            String pkgName = data.getSchemeSpecificPart();
            if (pkgName != null) {
                skipPackages = new String[] { pkgName };//将广播对应的包名添加到skipPackages中
            }
        }
    } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {//移动app之后发出的广播(APP2SD)
        skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
    }

    if (skipPackages != null && (skipPackages.length > 0)) {
        for (String skipPackage : skipPackages) {
            if (skipPackage != null) {
                int NT = receivers.size();
                for (int it=0; it<NT; it++) {
                    ResolveInfo curt = (ResolveInfo)receivers.get(it);
                    if (curt.activityInfo.packageName.equals(skipPackage)) {
                        //将skipPackages对应的BroadcastReceiver移出receivers;这就是上面说的限制,因此,应用不能接收到自己的PACKAGE_ADDED广播
                        receivers.remove(it);
                        it--;
                        NT--;
                     }
                }
            }
        }
    }

    int NT = receivers != null ? receivers.size() : 0;
    int it = 0;
    ResolveInfo curt = null;
    BroadcastFilter curr = null;
    //NT对应的是静态BroadcastReceiver的数量
    //NR对应的是动态BroadcastReceiver的数量
    //若发送的是无序广播,此时NR为0
    //若是有序广播,才会进入下面两个while循环

    //下面两个while循环就是将静态注册的BroadcastReceiver和动态注册的BroadcastReceiver
    //按照优先级合并到一起(有序广播才会合并)
    while (it < NT && ir < NR) {
        if (curt == null) {
            curt = (ResolveInfo)receivers.get(it);
        }
        if (curr == null) {
            curr = registeredReceivers.get(ir);
        }

        //动态优先级大于静态时,将动态插入到receivers中
        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();
    }
    //插入剩下的动态BroadcastReceiver
    receivers.add(registeredReceivers.get(ir));
    ir++;
}

if ((receivers != null && receivers.size() > 0)
        || resultTo != null) {
    BroadcastQueue queue = broadcastQueueForIntent(intent);//根据intent的FLAG获取前台或后台广播队列
    //构造对应的BroadcastRecord
    BroadcastRecord r = new BroadcastRecord(........);
    ............
    //同样判断是否需要替换,这里是Ordered Queue
    boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
    if (!replaced) {
        //没替换时,就加入Ordered Queue
        queue.enqueueOrderedBroadcastLocked(r);

        //触发发送流程
        queue.scheduleBroadcastsLocked();
    }
} else {
    // There was nobody interested in the broadcast, but we still want to record
    // that it happened.
    if (intent.getComponent() == null && intent.getPackage() == null
            && (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
        // This was an implicit broadcast... let's record it for posterity.
        //没有处理的静态或有序广播,保存起来
        //感觉保存起来也没什么用啊
        addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
    }
}
...............

        走到这里的话,registerReceivers不为null说明当前发送的是一个order广播。如果是order广播,动态BroadcastReceiver和静态BroadcastReceiver合并到一个队列中进行处理,也就是说order广播下,所有的BroadcastReceiver(静态和动态)处理方式都是一样的(串行化处理)。另外,对于静态BroadcastReceiver而言,始终是和order广播的处理方式是一样的,也就是说静态的BroadcastReceiver只有order模式。

        简单来说,broadcastIntentLocked()的第四部分工作就是有序广播,把静态BroadcastReceiver和动态BroadcastReceiver按照优先级合并到receivers中,构造对于的BroadcastRecord,然后将BroadcastRecord加入到Ordered Queue中,并触发广播发送流程。

        至此,整个broadcastIntentLocked函数分析完毕,除去一些条件判断的细节外,整个工作流程如下图所示:


        1、处理粘性广播。

        由于粘性广播的特性(BroadcastReceiver注册即可接收),系统必须首先保存粘性广播。

        2、处理普通动态广播。

        普通广播是并发的,系统优先为动态注册的BroadcastReceiver发送广播。动态广播对应的BroadcastRecord加入到Parallel Queue中。

        3、处理静态广播和有序广播。

        这一步主要是为静态注册BroadcastReceiver发送广播,对应的BroadcastRecord加入到Ordered Queue中。

        此外,需要注意的是:如果广播是有序的,那么第2步不会为动态注册的BroadcastReceiver发送广播,而是在第三步统一发送。发送有序广播时,AMS将按照BroadcastReceiver的优先级,依次构造BroadcastRecord加入到Ordered Queue中。

BroadcastQueue.scheduleBroadcastsLocked

        从上面的代码,我们知道广播发送方调用sendBroadcast后,AMS会构造对应的BroadcastRecord加入到BroadcastQueue中,返回调用BroadcastQueue的scheduleBroadcastsLocked()方法。现在我们看下这个方法。

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

        if (mBroadcastsScheduled) {//避免短时间内重复发送BROADCAST_INTENT_MSG
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }
    /**
     * Set when we current have a BROADCAST_INTENT_MSG in flight.
     */
    boolean mBroadcastsScheduled = false;
        上面的代码将发送BROADCAST_INTENT_MSG,触发BroadcastQueue调用processNextBroadcast()方法进行处理。

BroadcastQueue.processNextBroadcast

        processNextBroadcast方法较长,分段进行分析。

Part I

final void processNextBroadcast(boolean fromMsg) {
    synchronized(mService) {
        BroadcastRecord r;
        ..............
        //更新CPU的使用情况
        //处理静态广播时,可能需要拉起对应进程,因此在这里先记录一下CPU情况
        mService.updateCpuStats();

        if (fromMsg) {
            //处理BROADCAST_INTENT_MSG后,将mBroadcastsScheduled置为false
            //scheduleBroadcastsLocked就可以再次被调用了
            mBroadcastsScheduled = false;
        }

        // First, deliver any non-serialized broadcasts right away.
        //先处理“并发”发送的普通广播(无序广播),mParallelBroadcasts中保存动态注册的广播接收器
        while (mParallelBroadcasts.size() > 0) {
            //依次取出BroadcastRecord
            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++) {
                //mParallelBroadcasts中的每个成员均为BroadcastFilter类型
                Object target = r.receivers.get(i);
                ............
                //为该BroadcastRecord对应的每个Receiver发送广播 
                deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
            }

            //将这里处理过的信息加入到历史记录中
            addBroadcastToHistoryLocked(r);
        }
        ........................
    }
}
        从上面的代码可以看出, processNextBroadcast方法第一部分主要是为动态注册的BroadcastReceiver发送普通广播。发送普通广播的方法为 deliverToRegisteredReceiverLocked方法。
1.deliverToRegisteredReceiverLocked

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
        BroadcastFilter filter, boolean ordered, int index) {
    boolean skip = false;
    //检查广播发送方是否有BroadcastReceiver指定的权限
    if (filter.requiredPermission != null) {
        int perm = mService.checkComponentPermission(filter.requiredPermission,
                r.callingPid, r.callingUid, -1, true);
        if (perm != PackageManager.PERMISSION_GRANTED) {
            ............
            skip = true;
        } else {
            //进一步检查权限的合理性
            final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
            if (opCode != AppOpsManager.OP_NONE
                    && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
                            r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
                ............
                skip = true;
            }
        }
    }

    //检查BroadcastReceiver是否有Broadcast要求的权限
    if (!skip && r.requiredPermissions != null && r.requiredPermissions.length > 0) {
        for (int i = 0; i < r.requiredPermissions.length; i++) {
            String requiredPermission = r.requiredPermissions[i];
            int perm = mService.checkComponentPermission(requiredPermission,
                    filter.receiverList.pid, filter.receiverList.uid, -1, true);
            if (perm != PackageManager.PERMISSION_GRANTED) {
                .................
                //只要一条权限不满足,就结束
                skip = true;
                break;
            }

            //进一步检查权限的合理性
            int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
            if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                    && mService.mAppOpsService.noteOperation(appOp,
                            filter.receiverList.uid, filter.packageName)
                    != AppOpsManager.MODE_ALLOWED) {
                ...........
                skip = true;
                break;
            }
        }
    }

    //这段代码我看的有些懵逼,发送方没要求权限,还检查啥?
    if (!skip && (r.requiredPermissions == null || r.requiredPermissions.length == 0)) {
        int perm = mService.checkComponentPermission(null,
                filter.receiverList.pid, filter.receiverList.uid, -1, true);
        if (perm != PackageManager.PERMISSION_GRANTED) {
            ............
            skip = true;
        }
    }

    //还有一些其它的检查,不再分析代码了。。。。。
    //包括是否允许以background的方式发送、IntentFirewall是否允许广播中的Intent发送
    ...............................

    if (skip) {
        //不满足发送条件的话,标记一下,结束发送
        r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
        return;
    }

    // If permissions need a review before any of the app components can run, we drop
    // the broadcast and if the calling app is in the foreground and the broadcast is
    // explicit we launch the review UI passing it a pending intent to send the skipped
    // broadcast.
    //特殊情况,还需要再次检查权限,中断广播发送
    //再次满足发送条件后,会重新进入到后续的发送流程
    if (Build.PERMISSIONS_REVIEW_REQUIRED) {
        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
                filter.owningUserId)) {
            r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
            return;
        }
    }

    //可以发送了,标记一下
    r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;

    // If this is not being sent as an ordered broadcast, then we
    // don't want to touch the fields that keep track of the current
    // state of ordered broadcasts.
    if (ordered) {
        //如果发送的是有序广播,则记录一些状态信息等,不涉及广播发送的过程
        .............
    }

    try {
        ..............
        //若BroadcastReceiver对应的进程处于fullBackup状态(备份和恢复),则不发送广播
        if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
            if (ordered) {
                //有序广播必须处理完一个,才能处理下一个,因此这里主动触发一下
                skipReceiverLocked(r);
            }
        } else {
            //执行发送工作
            performReceiveLocked(.............);
        }
        if (ordered) {
            r.state = BroadcastRecord.CALL_DONE_RECEIVE;
        }
    } catch (RemoteException e) {
        .............
    }
}
        d eliverToRegisteredReceiverLocked方法看起来很长,但大部分内容都是进行权限检查等操作,实际的发送工作交给了performReceiveLocked方法。广播是一种可以携带数据的跨进程、跨APP通信机制,为了避免安全泄露的问题,Android才在此处使用了大量的篇幅进行权限检查的工作。
2.BroadcastQueue.performReceiveLocked

void performReceiveLocked(.........) {
    // Send the intent to the receiver asynchronously using one-way binder calls.
    if (app != null) {
        if (app.thread != null) {
            // If we have an app thread, do the call through that so it is
            // correctly ordered with other one-way calls.
            try {
                //通过Binder通信,将广播信息发往BroadcastReceiver处在的进程
                app.thread.scheduleRegisteredReceiver(.......);
            } catch (RemoteException ex) {
                // Failed to call into the process. It's either dying or wedged. Kill it gently.
                .......
                app.scheduleCrash("can't deliver broadcast");
            }
            throw ex;
        } else {
            // Application has died. Receiver doesn't exist.
            throw new RemoteException("app.thread must not be null");
        }
    } else {
        //直接通过Binder通信,将消息发往BroadcastReceiver的IIntentReceiver接口
        receiver.performReceive(intent, resultCode, data, extras, ordered,
                sticky, sendingUser);
    }
}
        对于动态注册的BroadcastReceiver而言,发送广播的流程应该进入到上述代码的If分支。 进程的ApplicationThread调用scheduleRegisteredReceiver函数的流程,我们放在后面单独分析。这一部分整体的流程如下图所示:

Part II

        至此,processNextBroadcast函数已经发送完所有的动态普通广播,我们看看该函数后续的流程:

..........
// Now take care of the next serialized one...

// If we are waiting for a process to come up to handle the next
// broadcast, then do nothing at this point.  Just in case, we
// check that the process we're waiting for still exists.
// 对于有序或静态广播而言,需要依次向每个BroadcastReceiver发送广播,前一个处理完毕后才能发送下一个广播
// 如果BroadcastReceiver对应的进程还未启动,则需要等待
// mPendingBroadcast就是用于保存,因为对应进程还未启动,而处于等待状态的BroadcastRecord
if (mPendingBroadcast != null) {
    ................
    boolean isDead;
    synchronized (mService.mPidsSelfLocked) {
        //通过AMS得到对应进程的信息
        //BroadRecord对应多个BroadcastReceiver,即对应多个进程
        //此处curApp保存当前正在等待的进程
        ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
        isDead = proc == null || proc.crashing;
    }
    if (!isDead) {
        // It's still alive, so keep waiting
        // 注意到有序和静态广播必须依次处理
        // 因此,若前一个BroadcastRecord对应的某个进程启动较慢,不仅会影响该BroadcastRecord中后续进程接收广播
        // 还会影响到后续所有BroadcastRecord对应进程接收广播
        return;
    } else {
        ............
        mPendingBroadcast.state = BroadcastRecord.IDLE;
        mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
        mPendingBroadcast = null;
    }
}

boolean looped = false;

//处理之前发送BroadcastRecord的状态
do {
    if (mOrderedBroadcasts.size() == 0) {
        // No more broadcasts pending, so all done!
        mService.scheduleAppGcsLocked();

        //当至少一个BroadcastRecord被处理完毕后,looped才会被置变为true
        if (looped) {
            // If we had finished the last ordered broadcast, then
            // make sure all processes have correct oom and sched
            // adjustments.
            // 因为静态广播和有序广播,可能拉起进程
            // 因此这些广播处理完毕后,AMS需要释放掉一些不需要的进程
            mService.updateOomAdjLocked();
        }
        return;
    }

    //每次均处理当前的第一个
    r = mOrderedBroadcasts.get(0);
    boolean forceReceive = false;

    // 这里用于判断此广播是否处理超时
    // 仅在系统启动完毕后,才进行该判断,因为PRE_BOOT_COMPLETED广播可能由于系统升级需要等待较长时间
    int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
    if (mService.mProcessesReady && r.dispatchTime > 0) {
        long now = SystemClock.uptimeMillis();
        if ((numReceivers > 0) &&
                //单个广播处理的超时时间,定义为2 * 每个接收者处理的最长时间(10s)* 接收者的数量
                (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
            ...............
            //如果超时,则强制结束这条广播的处理
            broadcastTimeoutLocked(false); // forcibly finish this broadcast
            forceReceive = true;
            r.state = BroadcastRecord.IDLE;
        }
    }

    if (r.state != BroadcastRecord.IDLE) {
        //BroadcastRecord还未处理或处理完毕后,状态为IDLE态
        ............
        return;
    }

    //如果广播处理完毕,或中途被取消
    if (r.receivers == null || r.nextReceiver >= numReceivers
            || r.resultAbort || forceReceive) {
        // No more receivers for this broadcast!  Send the final
        // result if requested...
        if (r.resultTo != null) {
            try {
                ..........
                //广播处理完毕,将该广播的处理结果发送给resultTo对应BroadcastReceiver
                performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
            } catch (RemoteException e) {
                .............
            }
        }
        .................
        cancelBroadcastTimeoutLocked();
        addBroadcastToHistoryLocked(r);

        //作一下记录
        if (r.intent.getComponent() == null && r.intent.getPackage() == null
                 && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
            // This was an implicit broadcast... let's record it for posterity.
            mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
                    r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
        }
        mOrderedBroadcasts.remove(0);
        //一个BroadcastRecord处理完毕后,将r置为null
        r = null;

        //如果一个广播处理完毕,说明可能拉起过进程,于是looped置为true
        looped = true;
        continue;
    }
//r == null时,可以处理下一个BroadcastRecord
//r != null, 继续处理当前BroadcastRecord的下一个BroadReceiver
} while (r == null);
....................
        这部分代码看起来有点乱,其实主要做了两个工作:

        1、判断是否有PendingBroadcast。

        当存在PendingBroadcast,且当前正在等待启动的进程并没有死亡,那么不能处理下一个BroadcastRecord,必须等待PendingBroadcast处理完毕。

         2、处理mOrderedBroadcasts中的BroadcastRecord。

        由于有序广播和静态广播,必须一个接一个的处理。因此每发送完一个广播后,均会重新调用processNextBroadcast方法。

        在发送新的BroadcastRecord时,需要先处理旧有BroadcastRecord的状态。于是,这段代码后续部分,主要进行了以下操作:

        若所有BroadcastRecord均处理完毕,利用AMS释放掉无用进程;

        更新超时BroadcastRecord的状态,同时越过此BroadcastRecord;

        当一个BroadcastRecord处理完毕后,将结果发送给指定BroadcastReceiver(指定了接收者,才进行此操作),同时将该BroadcastRecord从mOrderedBroadcasts中移除。

        这一系列动作的最终目的,就是选出下一个处理的BroadcastRecord,然后就可以开始向该BroadcastRecord中下一个BroadcastReceiver发送广播了。

        这一部分整体的判断逻辑大致上如下图所示:


Part III

..............
// Get the next receiver...
// 开始处理当前BroadcastRecord的下一个BroadcastReceiver
int recIdx = r.nextReceiver++;

// Keep track of when this receiver started, and make sure there
// is a timeout message pending to kill it if need be.
// 记录单个广播的起始时间
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {
    //记录整个BroadcastRecord的起始时间
    r.dispatchTime = r.receiverTime;
    r.dispatchClockTime = System.currentTimeMillis();
    ................
}

if (! mPendingBroadcastTimeoutMessage) {
    long timeoutTime = r.receiverTime + mTimeoutPeriod;
    ....................
    //设置广播处理的超时时间为10s
    setBroadcastTimeoutLocked(timeoutTime);
}

final BroadcastOptions brOptions = r.options;
//取出下一个广播接收者
final Object nextReceiver = r.receivers.get(recIdx);

if (nextReceiver instanceof BroadcastFilter) {
    // Simple case: this is a registered receiver who gets
    // a direct call.
    //动态注册的BroadcastReceiver,即处理的是有序广播
    BroadcastFilter filter = (BroadcastFilter)nextReceiver;
    ............
    //与处理普通广播一样,调用deliverToRegisteredReceiverLocked
    //将广播发送给BroadcastReceiver对应进程的ApplicationThread
    deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);

    //从代码流程来看,deliverToRegisteredReceiverLocked发送广播出现问题时,r.receiver才可能是null
    //代码运行到这个位置,加入到Ordered Queue的Broadcast一定是order的
    if (r.receiver == null || !r.ordered) {
        // The receiver has already finished, so schedule to
        // process the next one.
        ...............
        //因此这里应该是有序广播发送错误,因此重新发送BROADCAST_INTENT_MSG,触发下一次发送广播的流程
        r.state = BroadcastRecord.IDLE;
        scheduleBroadcastsLocked();
    } else {
        //处理特殊的选项
        //向DeviceIdleController发送消息,赋予白名单内的应用,
        //在Doze模式的激活窗口中,额外的可以访问网络和持锁的时间
        if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
            scheduleTempWhitelistLocked(filter.owningUid,
                    brOptions.getTemporaryAppWhitelistDuration(), r);
        }
    }

    //对于有序广播而言,已经通知了一个BroadcastReceiver,需要等待处理结果,因此返回
    return;
}
.......................
        processNextBroadcast的第3部分,主要是处理有序广播发往动态BroadcastReceiver的场景。

        从代码可以看出,有序广播具体的方法流程与普通广播一致,均是调用deliverToRegisteredReceiverLocked函数。唯一不同的是,有序广播发往一个BroadcastReceiver后,必须等待处理结果,才能进行下一次发送过程。

Part IV

...................
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
// 开始处理静态广播

ResolveInfo info = (ResolveInfo)nextReceiver;

//得到静态广播对应的组件名
ComponentName component = new ComponentName(
        info.activityInfo.applicationInfo.packageName,
        info.activityInfo.name);

boolean skip = false;

//以下与deliverToRegisteredReceiverLocked中类似,进行发送广播前的检查工作

//判断发送方和接收方要求的权限,是否互相满足
//判断Intent是否满足AMS的IntentFirewall要求
...................

boolean isSingleton = false;
try {
    //判断BroadcastReceiver是否是单例的
    isSingleton = mService.isSingleton(info.activityInfo.processName,
            info.activityInfo.applicationInfo,
            info.activityInfo.name, info.activityInfo.flags);
} catch (SecurityException e) {
    .................
}

//BroadcastReceiver要求SINGLE_USER
//那么必须申明INTERACT_ACROSS_USERS权限
if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
    if (ActivityManager.checkUidPermission(
            android.Manifest.permission.INTERACT_ACROSS_USERS,
            info.activityInfo.applicationInfo.uid)
                    != PackageManager.PERMISSION_GRANTED) {
        ..............
        skip = true;
    }
}

if (!skip) {
    r.manifestCount++;
} else {
    r.manifestSkipCount++;
}

if (r.curApp != null && r.curApp.crashing) {
    .........
    skip = true;
}

if (!skip) {
    boolean isAvailable = false;
    try {
        isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
                info.activityInfo.packageName,
                UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
    } catch (Exception e) {
        ..........
    }
    if (!isAvailable) {
        .........
        skip = true;
    }
}

//判断BroadcastReceiver对应进程是否允许后台启动
//不允许也会skip
..............

if (skip) {
    ..................
    r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
    r.receiver = null;
    r.curFilter = null;
    r.state = BroadcastRecord.IDLE;
    //跳过该广播,发送下一个广播
    scheduleBroadcastsLocked();
    return;
}

r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
................
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
    //与处理有序普通广播一样,在此处理特殊的选项
    scheduleTempWhitelistLocked(receiverUid,
            brOptions.getTemporaryAppWhitelistDuration(), r);
}

// Broadcast is being executed, its package can't be stopped.
try {
    //发送静态广播前,修改BroadcastReceiver对应的Package状态
    AppGlobals.getPackageManager().setPackageStoppedState(
            r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
    ...............
}

// Is this receiver's application already running?
if (app != null && app.thread != null) {
    try {
        app.addPackage(info.activityInfo.packageName,
                info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
        //BroadcastReceiver对应进程启动时,调用ApplicationThread的scheduleReceiver
        processCurBroadcastLocked(r, app);

        //等待结果,故return
        return;
    } catch (RemoteException e) {
        //这可能是对应进程死亡,可以重新拉起进程发送
        ..........
    } catch (RuntimeException e) {
        .........
        //发送失败,结束本次发送
        finishReceiverLocked(r, r.resultCode, r.resultData,
        r.resultExtras, r.resultAbort, false);

        //继续发送后续的广播
        scheduleBroadcastsLocked();
        // We need to reset the state if we failed to start the receiver.
        r.state = BroadcastRecord.IDLE;
        return;
    }
}
.............
//启动进程处理广播
if ((r.curApp=mService.startProcessLocked(targetProcess,
        info.activityInfo.applicationInfo, true,
        r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
        "broadcast", r.curComponent,
        (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                == null) {
    //创建进程失败
    ...............
    //发送失败,结束本次发送
    finishReceiverLocked(r, r.resultCode, r.resultData,
    r.resultExtras, r.resultAbort, false);

    //继续发送后续的广播
    scheduleBroadcastsLocked();
    r.state = BroadcastRecord.IDLE;
    return;
}

//进程启动成功时,mPendingBroadcast保存当前的BroadcastRecord,及待发送广播的下标
//当进程启动后,将根据这两个值处理广播
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
.............
        processNextBroadcast第四部分主要是处理静态广播,除了检查是否满足发送条件外,主要进行了以下工作:

        1、若BroadcastReceiver对应的进程已经启动,那么将直接调用进程对应ApplicationThread的scheduleReceiver发送广播;

        2、若BroadcastReceiver对应的进程没有启动,那么AMS将启动对应的进程。

        当对应的进程启动,同时完成Android环境的创建后,AMS在attachApplicationLocked函数中重新处理等待发送的广播,对应代码如下:

...............
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
    try {
        //发送因目标进程还未启动,而处于等待状态的广播
        //sendPendingBroadcastsLocked将调用BroadcastQueue中的sendPendingBroadcastsLocked函数
        //sendPendingBroadcastsLocked最后仍会调用进程对应ApplicationThread的scheduleReceiver函数
        didSomething |= sendPendingBroadcastsLocked(app);
    } catch (Exception e) {
        // If the app died trying to launch the receiver we declare it 'bad'
        Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
        badApp = true;
    }
}
.............

         这一部分整体的流程大致如下图所示:


应用进程处理广播

        最后,我们看看应用进程收到广播处理请求后的流程。

1.动态广播的处理流程

         在上面的performReceiveLocked方法中,调用了app.thread.scheduleRegisteredReceiver方法,最后通过Binder通信调用的是进程对应ApplicationThread的scheduleRegisteredReceiver接口。

ActivityThread.ApplicationThread.scheduleRegisteredReceiver

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
        int resultCode, String dataStr, Bundle extras, boolean ordered,
        boolean sticky, int sendingUser, int processState) throws RemoteException {
    ..............
    //调用的是LoadedApk中ReceiverDispatcher的内部类InnerReceiver的接口
    receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
            sticky, sendingUser);
}

       这里的receiver的具体类型是LoadedApk.ReceiverDispatcher.InnerReceiver,即定义在LoadedApk类的内部类ReceiverDispatcher里面的一个内部类InnerReceiver,这里调用它的performReceive方法。

InnerReceiver.performReceive()

    static final class ReceiverDispatcher {

        final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;

            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }

            @Override
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                final LoadedApk.ReceiverDispatcher rd;
                if (intent == null) {
                    ........
                    rd = null;
                } else {
                    rd = mDispatcher.get();
                }
                .......
                if (rd != null) {
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);//调用ReceiverDispatcher的performReceive方法
                } else {
                    // The activity manager dispatched a broadcast to a registered
                    // receiver in this process, but before it could be delivered the
                    // receiver was unregistered.  Acknowledge the broadcast on its
                    // behalf so that the system's broadcast sequence can continue.
                    //处理特殊情况,在收到广播前,BroadcastReceiver已经unregister了。
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    try {
                        if (extras != null) {
                            extras.setAllowFds(false);
                        }
                        mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());//通知AMS结束
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
        }
    这里只是简单地调用ReceiverDispatcher的performReceive方法来进一步处理,这里的ReceiverDispatcher是LoadedApk类里面的一个内部类。

ReceiverDispatcher.performReceive()

        public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);//创建Args对象
            ..........
            //mActivityThread是一个Handler对象,若创建BroadcastReceiver时没有指定Handler对象,则默认时ActivityThread主线程对应的Handler
            if (intent == null || !mActivityThread.post(args)) {
                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    ...........
                    args.sendFinished(mgr);
                }
            }
        }
        这里mActivityThread成员变量的类型是Handler,它是Activity在注册广播接收器时,从ActivityThread取得的。这里ReceiverDispatcher借助这个Handler,把这个广播以消息的形式放到Activity所在的ActivityThread的消息队列中去,因此,ReceiverDispatcher不等这个广播被Activity处理就返回了,这里也体现了 广播的发送和处理是异步进行的

        注意这里处理消息的方式是通过Handler.post方法进行的,post方法的参数是Runnable类型的,这个消息最终会调用这个参数的run方法来处理。这里的Args类是LoadedApk类的内部类ReceiverDispatcher的一个内部类,Args类实现了Runnable接口,因此,可以作为mActivityThread.post方法的参数,代表这个广播的intent也保存在这个Args实例中。

Args.run()

        final class Args extends BroadcastReceiver.PendingResult implements Runnable {
            private Intent mCurIntent;
            private final boolean mOrdered;
            private boolean mDispatched;

            public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                    boolean ordered, boolean sticky, int sendingUser) {
                super(resultCode, resultData, resultExtras,
                        mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
                        sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
                mCurIntent = intent;
                mOrdered = ordered;
            }
            
            public void run() {
                final BroadcastReceiver receiver = mReceiver;
                final boolean ordered = mOrdered;
                
                ...........
                
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                final Intent intent = mCurIntent;
                ..........

                mCurIntent = null;
                mDispatched = true;
                if (receiver == null || intent == null || mForgotten) {
                    if (mRegistered && ordered) {
                        ...........
                        sendFinished(mgr);
                    }
                    return;
                }

                ..........
                try {//这里处理的是动态广播的注册,默认认为BroadcastReceiver已经存在
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    intent.prepareToEnterProcess();
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);//设置BroadcastReceiver的PendingResult
                    receiver.onReceive(mContext, intent);//调用BroadcastReceiver的onReceive方法
                } catch (Exception e) {
                    ......
                }
                //当调用BroadcastReceiver的goAsync方法时,会把mPendingResult设为null;于是不会结束发送流程,直到调用PendingResult的finish方法
                if (receiver.getPendingResult() != null) {
                    finish();//Args继承PendingResult,此处也是调用PendingResult的finish方法。
                }
                ..........
            }
        }

    这里的mReceiver是ReceiverDispatcher类的成员变量,类型是BroadcastReceiver,就是Activity注册广播接收器时创建的BroadcastReceiver实例了,有了这个ReceiverDispatcher实例之后,就可以调用它的onReceive方法把这个广播发给它处理了。我们写的广播接收器继承了BroadcastReceiver,并且重写onReceive方法,因此这里就调用到我们自己写的onReceive方法中了。之后调用了BroadcastReceiver.PendingResult.finish()方法,来结束处理流程。

BroadcastReceiver.PendingResult.finish()

        /**
         * Finish the broadcast.  The current result will be sent and the
         * next broadcast will proceed.发送当前结果,下一个广播将会被处理。
         */
        public final void finish() {
            if (mType == TYPE_COMPONENT) {//对于动态广播而言,mType为TYPE_REGISTERED或TYPE_UNREGISTERED
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                if (QueuedWork.hasPendingWork()) {
                    QueuedWork.singleThreadExecutor().execute( new Runnable() {
                        @Override public void run() {
                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                    "Finishing broadcast after work to component " + mToken);
                            sendFinished(mgr);
                        }
                    });
                } else {
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing broadcast to component " + mToken);
                    sendFinished(mgr);
                }
            } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {//有序广播才返回结果
                ...........
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                sendFinished(mgr);//Binder通信,调用AMS的finishReceiver方法。
            }
        }
        这里的mType是在创建Args对象的时候传进来的,在Args的构造函数中,对于动态注册的广播接收器,参数mRegistered为true,mType就是TYPE_REGISTERED,对于非order广播而言,ordered变量是false,这样BroadcastReceiver.PendingResult中的mOrderedHint为false,mType是TYPE_REGISTERED;根据finish方法,if和else if条件都不成立,因此对于非order的动态广播而言,finish方法什么都没做。

         最后,我们总结一下这个Android应用程序发送广播的过程:

         1.通过sendBroadcast方法把一个广播通过Binder进程间通信机制发送给ActivityManagerService,AMS根据这个广播的Action类型找到相应的广播接收器,然后把这个广播放进自己的消息队列中去,就完成第一阶段对这个广播的异步分发了;

        2.AMS在消息循环中处理这个广播,并通过Binder进程间通信机制把这个广播分发给注册的广播接收分发器ReceiverDispatcher,ReceiverDispatcher把这个广播放进Activity所在的线程的消息队列中去,就完成第二个阶段对这个广播的异步分发了;

        3.ReceiverDispatcher的内部类Args在Activity所在的线程消息循环中处理这个广播,最终是将这个广播分发给所注册的BroadcastReceiver实例的onReceive分发进行处理。

2.串行化广播处理(scheduleRegisteredReceiver)

    前面分析到,对于接收者进程而言,order和非order广播都调用scheduleRegisterReceiver方法处理,只是上面处理的时候BroadcastReceiver.PendingResult中的mOrderedHint为true,mType为TYPE_REGISTERED,在finish()方法里面会调用sendFinished()方法:

BroadcastReceiver.PendingResult.sendFinished

        /** @hide */
        public void sendFinished(IActivityManager am) {
            synchronized (this) {
                if (mFinished) {
                    throw new IllegalStateException("Broadcast already finished");
                }
                mFinished = true;
            
                try {
                    .........
                    if (mOrderedHint) {
                        am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                                mAbortBroadcast, mFlags);//调用AMS的finishReceiver方法
                    } else {
                        // This broadcast was sent to a component; it is not ordered,
                        // but we still need to tell the activity manager we are done.
                        am.finishReceiver(mToken, 0, null, null, false, mFlags);
                    }
                } catch (RemoteException ex) {
                }
            }
        }
    对于order广播而言,if(mOrderedHint)语句成立,其中参数mAbortBroadcast代表当前高优先级的广播接收器是否需要disable掉低优先级的广播接收器接收当前的广播。我们接着看下finishReceiver方法。

ActivityManagerService.finishReceiver

    public void finishReceiver(IBinder who, int resultCode, String resultData,
            Bundle resultExtras, boolean resultAbort, int flags) {
        ..........

        final long origId = Binder.clearCallingIdentity();
        try {
            boolean doNext = false;
            BroadcastRecord r;

            synchronized(this) {
                BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
                        ? mFgBroadcastQueue : mBgBroadcastQueue;//根据flags标记,获取前台或后台广播队列
                r = queue.getMatchingOrderedReceiver(who);
                if (r != null) {//结束当前正在发送的广播,判断是否需要发送后续广播
                    doNext = r.queue.finishReceiverLocked(r, resultCode,
                        resultData, resultExtras, resultAbort, true);
                }
            }

            if (doNext) {//立马调度一次广播发送,发送下一个广播,processNextBroadcast是一个同步函数,一次只能处理一个请求
                r.queue.processNextBroadcast(false);
            }
            trimApplications();//释放不必要的进程
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
        首先辨别出当前的receiver所在的BroadcastRecord属于前台广播还是后台广播,然后在对应的BroadcastQueue中找出对应的BroadcastRecord,里面的finishReceiverLocked方法主要是重新设置BroadcastRecord的一些状态变量,以便于BroadcastRecord将广播发送给下一个接收者。这里尤其是,如果签名的mAbortBroadcast设置为true,那么BroadcastRecord的成员resultAbort会设置为true。

    接下来调用BroadcastQueue.processNextBroadcast方法继续驱动里面的mParallelBroadcasts和mOrderedBroadcasts中的BroadcastRecord处理下一个接收者,里面有这样一个判断:


    如果r.resultAbort为true,会停止处理当前正在发送的BroadcastRecord,这样优先级低的接收者也就收不到这个广播了。

3.静态广播的处理流程(scheduleReceiver)

ActivityThread.scheduleReceiver

    ActivityThread.scheduleReceiver方法处理应用进程中接收到的静态广播消息,实际处理该广播的是ActivityThread.handleReceiver方法,我们看下代码调用过程:

        public final void scheduleReceiver(Intent intent, ActivityInfo info,
                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                boolean sync, int sendingUser, int processState) {
            .........
            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                    sync, false, mAppThread.asBinder(), sendingUser);
            r.info = info;
            r.compatInfo = compatInfo;
            sendMessage(H.RECEIVER, r);
        }
    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }
    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }
    final H mH = new H();
    private class H extends Handler {
......
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
......
                case RECEIVER:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
                    handleReceiver((ReceiverData)msg.obj);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
......
    使用handler发送消息机制,调用到ActivityThread.handleReceiver方法处理。

ActivityThread.handleReceive

    handleReceive方法处理中,主要包含三大步骤:

创建BroadcastReceiver对象

    private void handleReceiver(ReceiverData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        String component = data.intent.getComponent().getClassName();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);

        IActivityManager mgr = ActivityManagerNative.getDefault();

        BroadcastReceiver receiver;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
        } catch (Exception e) {
            if (DEBUG_BROADCAST) Slog.i(TAG,
                    "Finishing failed broadcast to " + data.intent.getComponent());
            data.sendFinished(mgr);
            throw new RuntimeException(
                "Unable to instantiate receiver " + component
                + ": " + e.toString(), e);
        }
......
    }
    首先从AMS传递的intent中获取当前处理该广播的组件名称,然后通过反射创建一个BroadcastReceiver对象。从这里可以看出,静态广播处理的时候,每次都会创建一个新的BroadcastReceiver对象。
执行onReceive方法

......
        try {
            Application app = packageInfo.makeApplication(false, mInstrumentation);

            .........

            ContextImpl context = (ContextImpl)app.getBaseContext();
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);//调用BroadcastReceiver的onReceive方法
        } catch (Exception e) {
            .......
            data.sendFinished(mgr);//静态广播出问题,需要通知AMS
            .........
        } finally {
            sCurrentBroadcastIntent.set(null);//处理完毕,将进程的静态变量置为null
        }
......
        这里的receiver就是上面第一步创建的BroadcastReceiver对象,这里首先尝试创建一个Application对象,但是如果进程已经启动,Application对象已经建立,那么直接返回。下面调用BroadcastReceiver.onReceive方法,就是调用到我们自己写的onReceive方法。
向AMS发送处理结束消息

        if (receiver.getPendingResult() != null) {
            data.finish();//再次调用PendingResult的finish方法,此时type为TYPE_COMPONENT
        }

        静态广播是一种有序广播,处理结束后,需要通知AMS继续处理下一个广播接收者或者下一条BroadcastRecord。看看这次PendingResult的finish函数流程:

public final void finish() {
    if (mType == TYPE_COMPONENT) {
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        //静态广播一定是由主线程处理事件
        //若主线程的QueuedWork中有事情还未处理完,则必须让事情做完后,才通知结果
        //保证AMS不会将进程kill掉
        if (QueuedWork.hasPendingWork()) {

            //主线的QueuedWork是单线程依次处理任务的
            QueuedWork.singleThreadExecutor().execute( new Runnable() {
                @Override public void run() {
                    ............
                    //通知AMS
                    sendFinished(mgr);
                }
            });
        } else {
            ............
            //无等待处理的事件,直接通知AMS处理结果
            sendFinished(mgr);
        }
    } else (mOrderedHint && mType != TYPE_UNREGISTERED){
        .................
    }
        进程处理静态广播时,主要流程与处理动态广播时一致。主要的差别就是:进程需要反射创建出BroadcastReceiver,同时广播处理完毕后,一定要向AMS返回结果。

总结

        至此,Android中广播相关的流程分析完毕。

        从实现原理看上,Android中的广播机制使用了观察者模式,基于消息的发布/订阅模型。广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。

        整体来看,流程可粗略概括如下:

        1.广播接收者BroadcastReceiver,通过Binder通信向AMS进行注册;

        2.广播发送者通过Binder通信向AMS发送广播;

        3.AMS收到广播后,查找与之匹配的BroadcastReceiver,然后将广播发送到BroadcastReceiver对应进程的消息队列中;

        4.BroadcastReceiver对应进程的处理该消息时,将回调BroadcastReceiver中的onReceive()方法;

        5.广播处理完毕后,BroadcastReceiver对应进程按需将执行结果通知给AMS,以便触发下一次广播发送。

        对于不同的广播类型,以及不同的BroadcastReceiver注册方式,具体实现上会有不同,但整体的通信流程大致相似。

        我们通过下面的流程图再来熟悉一下广播发送流程,如下:


避免广播ANR

        上面熟悉了广播的发送流程,其中涉及到了广播ANR的问题,这里我们顺便了解下。

        首先明确AMS只会对order模式的广播设置超时时间,而order模式有两种情形:

        (1)、发送者以sendOrderedBroadcast/sendOrderedBroadcastAsUser方法发送的广播,这时候无论是动态注册还是静态注册,AMS都会以order模式处理所有的接收者,也就是所有的接收者以同步的方式处理。这种方式一般比较少。

        (2)、静态注册的广播接收器。

        以上两种情况时可能出现广播ANR的情形。

        根据上面ANR超时的机制来看,只要保证order模式下BroadcastReceiver的处理能够尽快的返回即可。结合广播的超时机制,可以有一下两种避免超时的思路:

        (1)、BroadcastReceiver.onReceive方法尽快返回

         如果需要完成一项比较耗时的工作,可以通过发送Intent给Service,由Service来完成;如果用子线程的方式来处理,BroadcastReceiver的生命周期很短(在onReceive方法执行后BroadcastReceiver的实例就会被销毁),子线程可能还没有结束BroadcastReceiver就先结束了。如果BroadcastReceiver结束了,它的宿主进程还在运行,那么子线程还会继续执行,但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程),进程的优先级很低。

        (2)、采用没有超时时间限制的非order模式的动态注册广播。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android广播管理三--广播发送(sendBroadcast)流程分析 的相关文章

  • linux中如何查看进程启动时间,持续时长

    root 64 gasdb2 Transsoft ps eo pid lstart etime grep 5359 5359 Tue Sep 16 14 47 22 2014 00 43 其中5359为进程号 xff0c 方便做过滤用的 T
  • 错误: 程序包org.apache.log4j不存在问题处理

    将maven中依赖修改如下 xff1a lt dependency gt lt groupId gt log4j lt groupId gt lt artifactId gt log4j lt artifactId gt lt versio
  • 构建maven项目失败解决办法

    通过eclipse的maven插件新建maven项目时 xff0c 出现如下错误 Unable to create project from archetype org apache maven archetypes maven arche
  • 关于java中的非静态代码块和静态代码块

    非静态代码块如 xff1a System out println 34 2 34 以上代码块就是非静态的代码块 xff0c 请注意这里的方法名 作用域 返回值 参数一概没有 xff0c 非静态代码块会在每次类被调用或者被实例化时就会被执行
  • jsoninclude.include.non_null 不起作用问题

    在开发过程中 xff0c 遇到在属性上加jsoninclude include non null注解 xff0c 有些属性起了作用 xff0c 有些却没有起作用 xff0c 下面使用代码简要说明一下 xff1a 64 Data public
  • 再次认识java的序列化

    首先是我们为什么要序列化 xff1f 我们可以来想想游戏的场景 xff0c 一个游戏有很多关卡 xff0c 并不是一次性能够打完的 如果我们打完一关 xff0c 这时候需要退出游戏休息了 当我们再次进入游戏之后发现这个游戏竟然需要重新打起
  • 控制actionbar中的向上键

    android中会给配置了 android parentActivityName 的activity默认加入一个向上返回键 xff0c 如下 xff1a 当点击向上返回后 xff0c 默认的行为是finish自身 xff0c startac
  • Android Studio快捷键

    Alt 43 回车 导入包 自动修正 Ctrl 43 N 查找类 Ctrl 43 Shift 43 N 查找文件 Ctrl 43 Alt 43 L 格式化代码 Ctrl 43 Alt 43 O 优化导入的类和包 Alt 43 Insert
  • Ubuntu 16.04 出现E: Problem executing scripts E: Sub-process returned an error code处理办法

    E Problem executing scripts APT Update Post Invoke Success 39 if usr bin test w var cache app info a e usr bin appstream
  • android studio “leaked window“ 错误

    昨天玩我做的app 发现app运行没问题 xff0c 但log里面出现下面的错误 xff1a Activity com example thirdversionclock MainActivity welcome has leaked wi
  • Archlinux 安装桌面环境 dwm + polybar

    Archlinux 安装桌面环境 dwm 43 polybar 关于git一 安装一些必要软件1 壁纸 状态栏等软件2 安装dwm和st 三 安装Polybar四 Fish Shell五 登录管理器六 一些个人使用的工具 2023 1 31
  • Arch Linux 安装和配置(陆续更新)

    Arch Linux 安装和配置 陆续更新 南国一年一度的回南天到来 xff0c 周末百无聊赖 xff0c 研究一下Arch Linux xff0c 整理此文 xff0c 其他使用笔记陆续补充 一 在Vmware中安装Arch Linux
  • ASP.NET Core MemoryCache 缓存

    ASP NET Core 中的缓存内存 xff08 MemoryCache xff09 ASP NET Core 中的缓存内存 ASP NET Core 中的分布式缓存 xff08 SQL Server 和 Redis 分布式缓存 xff0
  • FFmpeg In Android - 多媒体文件解封装/解码/保存Yuv

    FFMPEG视音频编解码零基础学习方法 100行代码实现最简单的基于FFMPEG 43 SDL的视频播放器 本文例子的源码 demuxing decoding cpp 修改自源码自带的例子ffmpeg源码 doc example demux
  • 【Java篇】多线程详解

    Java 多线程基础详解 文章目录 Java 多线程基础详解线程的相关概念一 创建线程的几种方式1 继承Thread类2 实现Runnable接口3 实现Callable接口4 推荐使用Runnable接口 二 线程安全1 线程安全问题引入
  • NotePad++ XMLTools 插件离线安装

    在使用NotePad 43 43 时 xff0c 在某些情形下 xff0c 需要格式化Xml格式内容 xff0c 可以使用Xml Tools插件 xff0c 注意下载安装包时 xff0c 需下载与NotePad 43 43 像匹配版本的插件
  • 【Windows逆向】【Qt】日志信息打印

    目录 x1f6eb 导读需求开发环境 1 示例程序Demo2 编写功能 xff08 QtCreator版本 xff09 3 编写功能 xff08 VS版本 xff09 x1f6ec 文章小结 x1f4d6 参考资料 x1f6eb 导读 需求
  • Ubuntu 18.04 安装ROS melodic文件错误问题broken packages

    反复多次尝试安装ros melodic xff0c 一直报错 xff0c 有文件损坏或者安装依赖问题 直接进入安装阶段 xff0c 前面的请看其他详细帖子 sudo apt span class token operator span ge
  • 在虚拟机安装Archlinux

    最近花了挺长一段时间练习在虚拟机安装archlinux的 xff0c 在这里跟大家分享一下经验 xff0c 如有错误 xff0c 欢迎大家指出 xff0c 谢谢大家 准备工作 archlinux镜像 43 vmware workstatio
  • Linux系统启动流程及系统裁剪

    一 内核管理简要理论 1 内核的功能 xff08 1 xff09 进程管理 xff08 2 xff09 内存管理 xff08 内核管理代码中代码量最大的部分 xff09 xff08 3 xff09 I O管理 xff1a 中断及中断处理 x

随机推荐

  • UNIX环境高级编程习题——第三章

    第三章习题 3 1 当读 写磁盘文件时 xff0c 本章中描述的函数确实是不带缓冲机制的吗 xff1f 请说明原因 xff1a span class hljs number 1 span 本章中描述的read和write函数都是系统调用 x
  • Ubuntu 16.04 安装Vmware Workstation12

    1 安装Vmware Workstation12 1 从官网上获取http www vmware com products workstation workstation evaluation html 2 如果觉得上面的方法下载得比较慢
  • Idea2017查看Class字节码文件

    Idea查看字节码文件的原理 1 javap命令的使用 在jdk工具包的bin目录下 xff0c 有一个java可执行文件javap xff0c 该工具可以查看java编译后的class文件 使用命令如下命令进行查看 javap span
  • Idea配置Web项目路径以及使用非默认Tomcat启动

    1 Web项目发布路径配置 1 首先点击Run gt Edit Configurations 2 点击左上角绿色的加号 xff0c 选择Tomcat gt Local 3 点击Deployment 4 点击绿色的小铅笔 5 在此处设置Out
  • emacs下org-mode导出pdf时pdflatex无法找到的问题解决方案

    配置环境 Deepin15 6 Linux emacs25 2 发现的问题 系统没有找到pdflatex命令 xff0c org mode无法导出latex的pdf 解决步骤 安装texlive2018 因为pdflatex是texlive
  • 通过Flask框架封装Tushare获取的日线股票数据

    概要介绍 概要介绍 xff08 TuShare id 282782 xff09 当我们需要进行量化交易分析 xff0c 或者通过代码进行股票的数据计算 xff0c 研究金融时 xff0c 我们需要获取最基本的股票价格 xff0c 开盘价收盘
  • IBM Was 打补丁记录

    0 拷贝解压ifph52925升级包 通过FTP工具 xff0c 把压缩包传到服务器 xff0c unzip d test01 9 0 0 0 ws was ifph52925 zip 1 停掉was 服务 ps ef grep was k
  • CoreText --- 段落样子CTParagraphStyle

    在前面一篇文章中 xff0c 介绍了属性文字的基本使用 xff0c 本章节主要针对文字的段落样式展开演示说明 先定义一段演示文字 xff08 文字中有中 xff0c 英文 xff09 cpp view plain copy NSString
  • 将自己的域名解析跳转到博客主页(GitHub中的gitpage跳转)

    最近突然迷上了博客 xff0c 突然又突发奇想 xff0c 将自己几个月前买的现在限制的域名拿来跳转到自己的csdn博客 经过一番研究 xff0c 总结 把自己的购买的域名 比如我买的circleyuan top 跳转到CSDN博客 只需要
  • Python3.4简单爬虫实现之抓取糗事百科段子

    网上的python教程大都是2 X版本的 xff0c python2 X和python3 X相比较改动比较大 xff0c 好多库的用法不太一样 xff0c 我安装的是3 4 1 xff0c 就用3 4 1实现一下网页内容抓取 首先是库 xf
  • 【C++】类和对象的关系

    概念 xff1a 对象 xff1a 将数据和对数据的操作方法放在一起 xff0c 形成一个相对独立的整体 属性和操作是对象的两大要素 类 xff1a 某一类对象所共有的 本质的属性和类行为 类和对象的关系 类是抽象的 xff0c 对象是具体
  • 美团笔试题_20220409

    前言 笔试一共五道编程题 xff08 四 43 一 xff09 xff0c 一为专项编程题 xff0c 估计不同岗位有题目不一样 xff0c 使用的是赛码网 xff0c 允许跳出界面使用自己的IDE 在此感谢筱羊冰冰提供的部分题目及题解 题
  • Android应用管理一 -- APK包的安装、卸载和优化(PackageManagerService)

    Android的应用管理主要是通过PackageManagerService来完成的 PackageManagerService服务负责各种APK包的安装 卸载 优化和查询 PackageManagerService在启动时会扫描所有的AP
  • Android5.1--多用户模式

    Android中的多用户与Windows的多用户类似 xff0c 可以支持多个用户使用系统 通常 xff0c 第一个在系统中注册的用户将默认成为系统管理员 不同用户的设置各不相同 xff0c 并且不同用户安装的应用及应用数据也不相同 但是系
  • Android--应用安装卸载失败问题分析

    1 应用安装失败 xff1a does not include classes dex 08 04 17 15 23 118W dalvikvm 5253 DexOptZ zip archive 39 data app cloneit 1
  • Android5.1--电源管理之省电模式分析

    一 如何开启5 0省电模式 打开安卓5 0设备的设置选项 xff0c 然后前往电池菜单 xff0c 点击菜单键 xff0c 弹出并选择 节电助手 xff0c 随后点击顶部的开关便可 此外 xff0c 要是用户运行的是原生版安卓 xff0c
  • 如何判断一个未安装的APK是Launcher应用

    网上找的大都是已安装应用的判断方法 xff0c 那么如何判断未安装的apk是否是桌面应用呢 xff1f 直接上代码 xff1a private boolean isLauncherApp String dexPath boolean isH
  • oracle 备份表

    一 PL SQL备份 xff08 1 xff09 打开PL SQL xff08 2 xff09 在Tools下选择Export Tables xff08 3 xff09 在列表中找到想要备份的表 xff0c 右键选择Export Data
  • Android 预置应用

    预置应用主要有4种情况 xff1a 1 如何将带源码的应用预置进系统 xff1f 2 如何将无源码的应用预置 xff08 APK xff09 进系统 xff1f 3 如何预置应用使得用户可以卸载 xff0c 恢复出厂设置时不能恢复 xff1
  • Android广播管理三--广播发送(sendBroadcast)流程分析

    前面我们分析了Android应用程序注册广播接收器的过程 xff0c 接下来它还要等待ActivityManagerService将广播分发过来 ActivityManagerService是如何得到广播并把它分发出去的呢 xff1f 广播