Android广播机制剖析【android广播系列一】

广播是android四大组件之一,所以广播的重要性也是显而易见的,最近需要做个功能,需要对广播的机制深入了解,所以我就整理下思路。平时我们写代码的时候,广播只是用来通知机制的,不是用来通信机制,通信机制还是要用binder机制来实现。但是现在市场上的第三方app大部分都是滥用广播,用广播来监听一些事件来实现自启动或者后台启动。这就误解了谷歌的意思。本来很好的组件,被大家滥用,导致用户老是感觉手机的程序控制不住,老是后台偷偷启动。小白用户甚至更不理解了。好了现在言归正传。

android广播按发送方式分为三种:

(1)无序广播

(2)有序广播 :OrderedBroadcast

(3)粘连广播 :StickyBroadcast

广播按照注册方式分为两种:

(1)静态广播

(2)动态广播

摘录老罗文章“广播机制是一种基于消息发布和订阅的事件驱动模型,即广播发送者负责发布消息,而广播接收者需要先订阅消息,然后才能接收到消息。在广播机制中,广播发送者事先不需要知道广播接收者的存在的,这样就能大大降低广播发送者和广播接收者之间的耦合度,进而提高系统的可扩展性和可维护性。”

广播的正真机制就两种情况,注册广播的机制,和接收广播的机制,我们先来谈谈注册广播的流程。

注册广播的流程:分为动态注册和静态注册两种;

(1)先来说说动态注册广播

说明: ReceiverDispatcher是frameworks/base/core/java/android/app/LoadedApk.java的内部类,这个类是保存广播接收者的组件(Activity或者Service),负责将这个被注册的广播接收者和注册它的组件关联在一起。

static final class ReceiverDispatcher 
{
    final static class InnerReceiver extends IIntentReceiver.Stub {
        . . . . . .
        . . . . . .
    }
    final IIntentReceiver.Stub mIIntentReceiver;   // 请注意这个域!它就是传到外面的rd。
    final BroadcastReceiver mReceiver;
    final Context mContext;
    final Handler mActivityThread;
    final Instrumentation mInstrumentation;
    final boolean mRegistered;
    final IntentReceiverLeaked mLocation;
    RuntimeException mUnregisterLocation;
    boolean mForgotten;
    . . . . . .
好了先说说需要了解的知识。registerReceiver(),是通过context来注册的,调用过程最后调用到ContextImpl这个对象中,

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)  {    
    return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {   
    return registerReceiverInternal(receiver, filter, broadcastPermission,  scheduler, getOuterContext());
}
我们接着来看看registerReceiverInternal()这个方法:

private Intent registerReceiverInternal(BroadcastReceiver receiver,
                                        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();
            }            
            // 查找和context对应的“子哈希表”里的ReceiverDispatcher,如果找不到,就重新new一个
            rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,
                                                    mMainThread.getInstrumentation(), true);
        } else {
            if (scheduler == null)  {
                scheduler = mMainThread.getHandler();
            }
            rd = new LoadedApk.ReceiverDispatcher(..., ...);
        }
        . . . . . .
    }    
    try  {        
        return ActivityManagerNative.getDefault().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName,
                rd, filter, broadcastPermission);
    }  catch (RemoteException e)  {        
        return null;
    }
}
大家注意那个rd对象(IIntentReceiver rd),首先调用成员变量mPackageInfo的成员函数(是LoadedApk的一个对象)getReceiverDispatcher()将广播接收者receiver封装成一个实现了IIntentReceiver接口的Binder的本地对象rd,最后将这个对象发送给AcitivityManagerService。

接着来看看getReceiverDispatcher()这个方法:

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
            Context context, Handler handler,
            Instrumentation instrumentation, boolean registered) {
        synchronized (mReceivers) {
            LoadedApk.ReceiverDispatcher rd = null;
			//注册过广播接收者与注册它的组件关联在一起
            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
			//已经注册过了
            if (registered) {
                map = mReceivers.get(context);
                if (map != null) {
                    rd = map.get(r);
                }
            }
			//没有将接收者和组件关联在一起,即没有注册过
            if (rd == null) {
                rd = new ReceiverDispatcher(r, context, handler,
                        instrumentation, registered);
                if (registered) {
                    if (map == null) {
                        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                        mReceivers.put(context, map);
                    }
                    map.put(r, rd);
                }
            } else {
                rd.validate(context, handler);
            }
            rd.mForgotten = false;
			//来获得一个实现了IIntentReceiver接口的Binder的本地对象
            return rd.getIIntentReceiver();
        }
    }

IIntentReceiver getIIntentReceiver() {
            return mIIntentReceiver;
        }
接着来看看给mIIntentReceiver赋值的地方,

static final class ReceiverDispatcher {

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

 。。。 。。。
}

ReceiverDispatcher(BroadcastReceiver receiver, Context context,
                Handler activityThread, Instrumentation instrumentation,
                boolean registered) {
            if (activityThread == null) {
                throw new NullPointerException("Handler must not be null");
            }
	    //指向了一个实现IIntentReceiver接口的Binder的本地对象
            mIIntentReceiver = new InnerReceiver(this, !registered);
            mReceiver = receiver;
            mContext = context;
            mActivityThread = activityThread;
            mInstrumentation = instrumentation;
            mRegistered = registered;
            mLocation = new IntentReceiverLeaked(null);
            mLocation.fillInStackTrace();
        }
}
注意:这个InnerReceiver对象内部有一个类型为ReceiverDispatcher的弱引用mDispatcher,它指向一个外部的ReceiverDispatcher对象。

接着就注册到AMS中去了。代码如下:

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        enforceNotIsolatedCaller("registerReceiver");
        int callingUid;
        int callingPid;
        synchronized(this) {
            ProcessRecord callerApp = null;
            if (caller != null) {//得到调用者的ProcessRecord
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when registering receiver " + receiver);
                }
                if (callerApp.info.uid != Process.SYSTEM_UID &&
                        !callerApp.pkgList.containsKey(callerPackage) &&
                        !"android".equals(callerPackage)) {
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                }
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            userId = this.handleIncomingUser(callingPid, callingUid, userId,
                    true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

            List allSticky = null;

            // Look for any matching sticky broadcasts...
            Iterator actions = filter.actionsIterator();
            if (actions != null) {
                while (actions.hasNext()) {
                    String action = (String)actions.next();
                    allSticky = getStickiesLocked(action, filter, allSticky,
                            UserHandle.USER_ALL);
                    allSticky = getStickiesLocked(action, filter, allSticky,
                            UserHandle.getUserId(callingUid));
                }
            } else {
                allSticky = getStickiesLocked(null, filter, allSticky,
                        UserHandle.USER_ALL);
                allSticky = getStickiesLocked(null, filter, allSticky,
                        UserHandle.getUserId(callingUid));
            }

            // The first sticky in the list is returned directly back to
            // the client.//判断黏性广播是否为null
            Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;

            if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter
                    + ": " + sticky);

            if (receiver == null) {
                return sticky;
            }

            ReceiverList rl   //这段代码注意看,很有研究价值。
                = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } else if (rl.uid != callingUid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for uid " + callingUid
                        + " was previously registered for uid " + rl.uid);
            } else if (rl.pid != callingPid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for pid " + callingPid
                        + " was previously registered for pid " + rl.pid);
            } else if (rl.userId != userId) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for user " + userId
                        + " was previously registered for user " + rl.userId);
            }
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadast");
            }
            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, null, AppOpsManager.OP_NONE, receivers, null, 0,
                            null, null, false, true, true, -1);
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }

            return sticky;
        }
    }

好了今天就到这里吧,后续再整理。。。










郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。