1. 程式人生 > >Android廣播機制實現原始碼淺析(一)

Android廣播機制實現原始碼淺析(一)

Android系統的廣播機制應用非常的廣泛,是一種方便快捷的程序間通訊的方式。同時它也有一些很有特殊的使用方式,比如它的兩種註冊方式,三種類型的廣播等,這些充斥在整個系統框架中,那麼為了用好廣播,很有必要對其原始碼進行分析,從而避免一些低階失誤。

本文將對整個廣播機制涉及到的知識做個粗略的講解,為大家自學拋磚引玉。

首先我們從註冊當時入手,牽引出與本機制有關的一些類:Packagemanager
在系統服務啟動時新增PackageManagerService,在這個過程中packagemanager就會對各個app安裝目錄的apk檔案進行掃描解析。
public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
            ……
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
            mSystemInstallObserver = new AppDirObserver(
                mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
            mSystemInstallObserver.startWatching();
            scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
//後續還有其他路徑下的app檔案掃描
……
}
private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
     ……
for (i=0; i<files.length; i  ) {
PackageParser.Package pkg = scanPackageLI(file,
                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);

……
}
}
private PackageParser.Package scanPackageLI(File scanFile,
            int parseFlags, int scanMode, long currentTime, UserHandle user) {
        PackageParser pp = new PackageParser(scanPath);
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setOnlyCoreApps(mOnlyCore);
        final PackageParser.Package pkg = pp.parsePackage(scanFile,
                scanPath, mMetrics, parseFlags);
        …….
        PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
                | SCAN_UPDATE_SIGNATURE, currentTime, user);
        ……
}
public Package parsePackage(File sourceFile, String destCodePath,
            DisplayMetrics metrics, int flags) {

try {
            // XXXX todo: need to figure out correct configuration.
            pkg = parsePackage(res, parser, flags, errorText);
        } catch (Exception e) {
            errorException = e;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
        }
}
private Package parsePackage(
        Resources res, XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {
……
String tagName = parser.getName();
            if (tagName.equals("activity")) {
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
                        hardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.activities.add(a);

            } else if (tagName.equals("receiver")) {
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.receivers.add(a);

            }
            …….
}

private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
            int parseFlags, int scanMode, long currentTime, UserHandle user) {
     ……
mPackages.put(pkg.applicationInfo.packageName, pkg);
……
N = pkg.receivers.size();
            r = null;
            for (i=0; i<N; i  ) {
                PackageParser.Activity a = pkg.receivers.get(i);
                a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        a.info.processName, pkg.applicationInfo.uid);
                mReceivers.addActivity(a, "receiver");
}
}


到這裡可以看到系統是如何解析manifest裡面註冊的廣播接收器的,並且最終將所有的接收器儲存到了
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers = new ActivityIntentResolver();中,請注意這個成員變數,後面會有用的,這裡也暗含了一個靜態的排序,也就是首先掃描目錄的排序,顯示system/app後面是其他的,還有一個就是file.list()對目錄下apk檔案的預設排序。
還有一種動態註冊的方式,平常我們使用registerReceiver其實最終是ContextImpl裡面的registerReceiverInternal方法來實現的,可以看看:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                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 {
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
        } catch (RemoteException e) {
            return null;
        }
}


看到最後其實還是activitymanager在工作,android的設計就是如此,由service管理各個元件省去了很多的工作。
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
       ……
mReceiverResolver.addFilter(bf);
       ……
            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                int N = allSticky.size();
                for (int i=0; i<N; i  ) {
                    Intent intent = (Intent)allSticky.get(i);
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, -1, -1, null, receivers, null, 0, null, null,
                            false, true, true, -1);
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }
……
}


最終將這些動態廣播接收器豆漿儲存到成員變數mReceiverResolver中(既然這裡是一個個add進去的,隱含了一個排序,也就是先註冊的動態廣播在前),並且注意allsticky這裡的判斷,這是sticky廣播處理的邏輯,粘性廣播會停留在service中等待,以便有人註冊這則廣播訊息後能儘快的收到這條廣播。
在註冊完了之後,廣播主要就是傳送和處理了。這個過程都是在activitymanagerservice裡面進行的。可以看看它的broadcastIntentLocked方法,主要的工作都是這裡進行的,尤其注意的是普通廣播與有序廣播,以及廣播的次序等資訊。
private final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle map, String requiredPermission,
            boolean ordered, boolean sticky, int callingPid, int callingUid,
            int userId) {
……
        // Add to the sticky list if requested.
        if (sticky) {
            if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
                    callingPid, callingUid)
                    != PackageManager.PERMISSION_GRANTED) {
                String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
                          callingPid   ", uid="   callingUid
                          " requires "   android.Manifest.permission.BROADCAST_STICKY;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);
            }
            if (requiredPermission != null) {
                Slog.w(TAG, "Can't broadcast sticky intent "   intent
                          " and enforce permission "   requiredPermission);
                return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
            }
            if (intent.getComponent() != null) {
                throw new SecurityException(
                        "Sticky broadcasts can't target a specific component");
            }
            // We use userId directly here, since the "all" target is maintained
            // as a separate set of sticky broadcasts.
            if (userId != UserHandle.USER_ALL) {
                // But first, if this is not a broadcast to all users, then
                // make sure it doesn't conflict with an existing broadcast to
                // all users.
                HashMap<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  ) {
                            if (intent.filterEquals(list.get(i))) {
                                throw new IllegalArgumentException(
                                        "Sticky broadcast "   intent   " for user "
                                          userId   " conflicts with existing global broadcast");
                            }
                        }
                    }
                }
            }
            HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
            if (stickies == null) {
                stickies = new HashMap<String, ArrayList<Intent>>();
                mStickyBroadcasts.put(userId, stickies);
            }
            ArrayList<Intent> list = stickies.get(intent.getAction());
            if (list == null) {
                list = new ArrayList<Intent>();
                stickies.put(intent.getAction(), list);
            }
            int N = list.size();
            int i;
            for (i=0; i<N; i  ) {
                if (intent.filterEquals(list.get(i))) {
                    // This sticky already exists, replace it.
                    list.set(i, new Intent(intent));
                    break;
                }
            }
            if (i >= N) {
                list.add(new Intent(intent));
            }
        }
……
// Figure out who all will receive this broadcast.
//靜態廣播接收器
        List receivers = null;
//動態廣播接收器
        List<BroadcastFilter> registeredReceivers = null;
        // Need to resolve the intent to interested receivers...
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            receivers = collectReceiverComponents(intent, resolvedType, users);
        }
        if (intent.getComponent() == null) {
            registeredReceivers = mReceiverResolver.queryIntent(intent,
                    resolvedType, false, userId);
        }
……
//處理非有序的廣播
        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.
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, requiredPermission,
                    registeredReceivers, resultTo, resultCode, resultData, map,
                    ordered, sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(
                    TAG, "Enqueueing parallel broadcast "   r);
            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
            if (!replaced) {
//只處理了動態註冊的接收器
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }
……
        // Merge into one list.
//後面的邏輯就是,如果前面是非有序廣播,就先處理與之匹配的動態註冊的並置為空,後面的邏輯就只有靜態的接收器並處理
//如果前面是有序廣播則不處理,此後的邏輯中先對靜態和動態的接收器按照priority值進行一個統一的排序最後處理。
        int ir = 0;
        if (receivers != null) {
            ……
            int NT = receivers != null ? receivers.size() : 0;
            int it = 0;
            ResolveInfo curt = null;
            BroadcastFilter curr = null;
            while (it < NT && ir < NR) {
                if (curt == null) {
                    curt = (ResolveInfo)receivers.get(it);
                }
                if (curr == null) {
                    curr = registeredReceivers.get(ir);
                }
//如果動態廣播接收器優先順序高於或者等於靜態廣播接收器,那麼就插到當前位置,靜態廣播後移
//這說明動態的要在靜態的前面
                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();
            }
            receivers.add(registeredReceivers.get(ir));
            ir  ;
        }

        if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
// broadcastQueueForIntent這個方法中對當前是否前臺的廣播接收器佇列進行了判斷,前臺和後臺的接收器處理超時是不一樣的,前臺是10m,後臺是60m。
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, requiredPermission,
                    receivers, resultTo, resultCode, resultData, map, ordered,
                    sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(
                    TAG, "Enqueueing ordered broadcast "   r
                      ": prev had "   queue.mOrderedBroadcasts.size());
            if (DEBUG_BROADCAST) {
                int seq = r.intent.getIntExtra("seq", -1);
                Slog.i(TAG, "Enqueueing broadcast "   r.intent.getAction()   " seq="   seq);
            }
            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
            if (!replaced) {
                queue.enqueueOrderedBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
        }

        return ActivityManager.BROADCAST_SUCCESS;
}


總結一下這裡面含有的排序資訊:分為兩種,有序廣播,首先按照優先順序排列,同優先順序的動態廣播先於靜態廣播,同優先順序的動態廣播中先註冊的先處理,同優先順序的靜態廣播中先掃描的APP廣播先處理。
有序廣播則,無視優先順序,動態廣播先於靜態廣播,動態廣播中先註冊的先處理,靜態廣播中先掃描的APP廣播先處理。

以上是廣播的註冊以及send時候的分發,接下來就是ActivtyManagerService呼叫到使用者程式的broadcastreceiver中處理的過程了,將在後續篇章中進行闡述。
> from eoe.cn