Android组件之BroadcastReceiver详解

一、Broadcast的种类

  • 普通广播
    sendBroadcast()这个方法的广播按照BroadcastReceiver注册的先后顺序发送给所有的BroadcastReceiver。如果BroadcastReceiver设置了优先级并且优先级与注册顺序相同,则不会出任何问题,但是如果优先级与注册顺序不同,则会抛出异常
  • 有序广播
    sendOrderedBroadcas方法用来发送有序广播,优先级高的BroadcastReceiver先收到,并且先收到的BroadcastReceiver可以拦截广播
  • 粘性广播

二、BroadcastReceiver的注册方式

  • 静态注册:在AndroidManifest.xml中注册,常驻系统,不受组件生命周期影响,即便应用退出,广播还是可以被接收,这种BroadcastReceiver耗电、占内存
  • 动态注册:代码中注册,非常驻系统,收到组件生命周期影响,组件结束,便不接收广播。当组件结束时我们应该手动注销前面动态注册的BroadcastReceiver,否则会出现内存泄漏

三、BroadcastReceiver的注册源码分析

我们在Activity或者Service中注册BroadcastReceiver其实最终都是调用到ContextWrapper.registerReceiver这个方法里

public class ContextWrapper extends Context {  
    Context mBase;  
    ......  

    @Override  
    public Intent registerReceiver(  
        BroadcastReceiver receiver, IntentFilter filter) {  
        return mBase.registerReceiver(receiver, filter);  
    }  

    ......  

} 

这里面的mBase是ContextImpl实例,所以最终会调用到ContextImpl.registerReceiver

class ContextImpl extends Context {  
    ......  

    @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());  
    }  

    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();  
                }  
                rd = mPackageInfo.getReceiverDispatcher(  
                    receiver, context, scheduler,  
                    mMainThread.getInstrumentation(), true);  
            } else {  
                ......  
            }  
        }  
        try {  
            return ActivityManagerNative.getDefault().registerReceiver(  
                    mMainThread.getApplicationThread(),  
                    rd, filter, broadcastPermission);  
        } catch (RemoteException e) {  
                return null;  
        }  
    }  

    ......  

}  

这里的mPackageInfo是LoadApk实例,getOuterContext()得到的就是我们注册这个BroadcastReceiver的context了,可能是Activity,也可能是Service,都是继承自Context。scheduler = mMainThread.getHandler();返回的是ActivityThread的mH成员变量,这个一个Handler,用来分发后面ActivityManagerService发送过来的广播。

rd = mPackageInfo.getReceiverDispatcher(  
                    receiver, context, scheduler,  
                    mMainThread.getInstrumentation(), true);  

这一句代码返回的是一个IIntentReceiver接口对象,这是一个Binder对象,接下来会将它传递给ActivityManagerService,当ActivityManagerService收到相应的广播的时候就是通过它来通知Activity或Service的。我们来看一下这句代码的具体实现

final class LoadedApk {  
    ......  

    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,  
            Context context, Handler handler,  
            Instrumentation instrumentation, boolean registered) {  
        synchronized (mReceivers) {  
            LoadedApk.ReceiverDispatcher rd = null;  
            HashMap 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 HashMap();  
                        mReceivers.put(context, map);  
                    }  
                    map.put(r, rd);  
                }  
            } else {  
                rd.validate(context, handler);  
            }  
            return rd.getIIntentReceiver();  
        }  
    }  

    ......  

    static final class ReceiverDispatcher {  

        final static class InnerReceiver extends IIntentReceiver.Stub {  
            final WeakReference mDispatcher;  
            ......  

            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {  
                mDispatcher = new WeakReference(rd);  
                ......  
            }  

            ......  
        }  

        ......  

        final IIntentReceiver.Stub mIIntentReceiver;  
        final Handler mActivityThread;  

        ......  

        ReceiverDispatcher(BroadcastReceiver receiver, Context context,  
                Handler activityThread, Instrumentation instrumentation,  
                boolean registered) {  
            ......  

            mIIntentReceiver = new InnerReceiver(this, !registered);  
            mActivityThread = activityThread;  

            ......  
        }  

        ......  

        IIntentReceiver getIIntentReceiver() {  
            return mIIntentReceiver;  
        }  

    }  

    ......  

}  

代码很简单,其实就是先判断LoadedApk中是否存在BroadcastReceiver对应的ReceiverDispather,如果不存在就创建,然后保存。这是Android里普遍用到的思想。

我们再回到ContextImpl.registerReceiverInternal里面,利用Binder通信最终会调用到ActivityManagerService.registerReceiver中

public final class ActivityManagerService extends ActivityManagerNative  
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
    ......  

    public Intent registerReceiver(IApplicationThread caller,  
            IIntentReceiver receiver, IntentFilter filter, String permission) {  
        synchronized(this) {  
            ProcessRecord callerApp = null;  
            if (caller != null) {  
                callerApp = getRecordForAppLocked(caller);  
                if (callerApp == null) {  
                    ......  
                }  
            }  

            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);  
                }  
            } else {  
                ......  
            }  

            // The first sticky in the list is returned directly back to  
            // the client.  
            Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;  

            ......  

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

            ReceiverList rl  
                = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());  
            if (rl == null) {  
                rl = new ReceiverList(this, callerApp,  
                    Binder.getCallingPid(),  
                    Binder.getCallingUid(), receiver);  

                if (rl.app != null) {  
                    rl.app.receivers.add(rl);  
                } else {  
                    ......  
                }  
                mRegisteredReceivers.put(receiver.asBinder(), rl);  
            }  

            BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);  
            rl.add(bf);  
            ......  
            mReceiverResolver.addFilter(bf);  

            // Enqueue broadcasts for all existing stickies that match  
            // this filter.  
            if (allSticky != null) {  
                ......  
            }  

            return sticky;  
        }  
    }  

    ......  

}  

代码很长,但是做的事很简单,就是讲BroadcastReceiver和其对应的filter保存在ActivityManagerService中,以便以后收到相应的广播时进行处理。

四、发送广播的源码分析

同样,发送广播还是会调用到ContextImpl.sendBroadcast方法,最终通过Binder机制调用到ActivityManagerService.broadcastIntent方法

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) {  
            intent = verifyBroadcastLocked(intent);  

            final ProcessRecord callerApp = getRecordForAppLocked(caller);  
            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;  
        }  
    }  

    ······
    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) {  
        intent = new Intent(intent);  

        ......  

        // Figure out who all will receive this broadcast.  
        List receivers = null;  
        List registeredReceivers = null;  
        try {  
            if (intent.getComponent() != null) {  
                ......  
            } else {  
                ......  
                registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);  
            }  
        } catch (RemoteException ex) {  
            ......  
        }  

        final boolean replacePending =  
            (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;  

        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.  
            BroadcastRecord r = new BroadcastRecord(intent, callerApp,  
                callerPackage, callingPid, callingUid, requiredPermission,  
                registeredReceivers, resultTo, resultCode, resultData, map,  
                ordered, sticky, false);  
            ......  
            boolean replaced = false;  
            if (replacePending) {  
                for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {  
                    if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) {  
                        ......  
                        mParallelBroadcasts.set(i, r);  
                        replaced = true;  
                        break;  
                    }  
                }  
            }  

            if (!replaced) {  
                mParallelBroadcasts.add(r);  

                scheduleBroadcastsLocked();  
            }  

            registeredReceivers = null;  
            NR = 0;  
        }  

        ......  

    }  
    ......  
}  

从其中的代码

registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);  

可以看到,broadcastIntentLocked方法中首先根据发送广播传递的intent来查询与之对应的注册过的BroadcastReceiver,之后调用scheduleBroadcastsLocked(); 进一步执行

public final class ActivityManagerService extends ActivityManagerNative  
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
    ......  

    private final void scheduleBroadcastsLocked() {  
        ......  

        if (mBroadcastsScheduled) {  
            return;  
        }  

        mHandler.sendEmptyMessage(BROADCAST_INTENT_MSG);  
        mBroadcastsScheduled = true;  
    }  

    ......  
} 

当ActivityManagerService收到广播时直接把广播放进消息队列中去了,根本不管广播后续是如何处理的,从这里可以看出广播的发送和处理都是异步的。

final Handler mHandler = new Handler() {  
    public void handleMessage(Message msg) {  
        switch (msg.what) {  
        ......  
        case BROADCAST_INTENT_MSG: {  
            ......  
            processNextBroadcast(true);  
        } break;  
        ......  
        }  
    }  
} 

······

private final void processNextBroadcast(boolean fromMsg) {  
    synchronized(this) {  
        BroadcastRecord r;  

        ......  

        if (fromMsg) {  
            mBroadcastsScheduled = false;  
        }  

        // First, deliver any non-serialized broadcasts right away.  
        while (mParallelBroadcasts.size() > 0) {  
            r = mParallelBroadcasts.remove(0);  
            ......  
            final int N = r.receivers.size();  
            ......  
            for (int i=0; i

processNextBroadcast方法中,r.receivers对应的是这个广播所有对应的BroadcastReceiver,因为有可能会针对一个广播注册多个BroadcastReceiver。通过循环中的deliverToRegisteredReceiverLocked方法分别给每个BroadcastReceiver来发送广播

private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,  
        BroadcastFilter filter, boolean ordered) {  
    boolean skip = false;  
    if (filter.requiredPermission != null) {  
        ......  
    }  
    if (r.requiredPermission != null) {  
        ......  
    }  

    if (!skip) {  
        // 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 {  
            ......  
            performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,  
                new Intent(r.intent), r.resultCode,  
                r.resultData, r.resultExtras, r.ordered, r.initialSticky);  
            ......  
        } catch (RemoteException e) {  
            ......  
        }  
    }  

}  

······

static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,  
        Intent intent, int resultCode, String data, Bundle extras,  
        boolean ordered, boolean sticky) throws RemoteException {  
    // Send the intent to the receiver asynchronously using one-way binder calls.  
    if (app != null && app.thread != null) {  
        // If we have an app thread, do the call through that so it is  
        // correctly ordered with other one-way calls.  
        app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,  
                data, extras, ordered, sticky);  
    } else {  
        ......  
    }  
} 

performReceiveLocked方法中,app.thread对应的是注册BroadcastReceiver时传递进来的ApplicationThreadProxy远程Binder对象,用于通知注册者接收广播。通过Binder机制,最终会调用到ApplicaitonThread.scheduleRegisteredReceiver方法

public final class ActivityThread {  
    ......  

    private final class ApplicationThread extends ApplicationThreadNative {  
        ......  

        // This function exists to make sure all receiver dispatching is  
        // correctly ordered, since these are one-way calls and the binder driver  
        // applies transaction ordering per object for such calls.  
        public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,  
                int resultCode, String dataStr, Bundle extras, boolean ordered,  
                boolean sticky) throws RemoteException {  
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);  
        }  

        ......  
    }  

    ......  

} 

这里的receiver是之前注册BroadcastReceiver时创建的,具体类型是LoadedApk.ReceiverDispatcher.InnerReceiver

final class LoadedApk {    
    ......   

    static final class ReceiverDispatcher {    

        final static class InnerReceiver extends IIntentReceiver.Stub {   
            ......  

            public void performReceive(Intent intent, int resultCode,  
                    String data, Bundle extras, boolean ordered, boolean sticky) {  

                LoadedApk.ReceiverDispatcher rd = mDispatcher.get();  
                ......  
                if (rd != null) {  
                    rd.performReceive(intent, resultCode, data, extras,  
                            ordered, sticky);  
                } else {  
                    ......  
                }  
            }  
        }  

        ......  
    }  

    ......  
}  

这里只是简单的调用了ReceiverDispatcher.performReceive方法

public void performReceive(Intent intent, int resultCode,  
            String data, Bundle extras, boolean ordered, boolean sticky) {  
        ......  

        Args args = new Args();  
        args.mCurIntent = intent;  
        args.mCurCode = resultCode;  
        args.mCurData = data;  
        args.mCurMap = extras;  
        args.mCurOrdered = ordered;  
        args.mCurSticky = sticky;  
        if (!mActivityThread.post(args)) {  
            ......  
        }   
    }  

    ......  
}  

这里的mActivityThread是一个Handler对象,是我们注册BroadcastReceiver时传递进来的那个scheduler = mMainThread.getHandler(); 对象,而args是一个Runnable,最终会调用到Args.run方法中。从这里也可以看出这是一个异步的过程。

final class LoadedApk {    
    ......   

    static final class ReceiverDispatcher {  
        ......  

        final class Args implements Runnable {  
            ......  

            public void run() {  
                BroadcastReceiver receiver = mReceiver;  

                ......  

                Intent intent = mCurIntent;  

                ......  

                try {  
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();  
                    intent.setExtrasClassLoader(cl);  
                    if (mCurMap != null) {  
                        mCurMap.setClassLoader(cl);  
                    }  
                    receiver.setOrderedHint(true);  
                    receiver.setResult(mCurCode, mCurData, mCurMap);  
                    receiver.clearAbortBroadcast();  
                    receiver.setOrderedHint(mCurOrdered);  
                    receiver.setInitialStickyHint(mCurSticky);  
                    receiver.onReceive(mContext, intent);  
                } catch (Exception e) {  
                    ......  
                }  

                ......  
            }  

            ......  
        }  

        ......  
    }  

    ......  
}

这里的BroadcastReceiver receiver = mReceiver; 其实就是我们注册BroadcastReceiver时创建的实例了,最终就会调用到BroadcastReceiver.onReceive方法中。

到这里我们的广播发送的过程就结束了,我们来总结一下这个过程:

  • ContextImpl.sendBroadcast通过Binder机制将广播发送到ActivityManagerService,ActivityManagerService根据传递过来的Intent的Action找到与之对应的BroadcastReceiver,然后将这个广播放到自己的消息队列中去,完成了广播的第一步异步分发
  • ActivityManagerService在消息循环中处理广播,通过Binder机制将这个广播分发给注册BroadcastReceiver时创建的ReceiverDispatcher,然后ReceiverDispatcher将广播放进注册BroadcastReceiver的Activity或Service所在的线程的消息队列中去,这就完成了广播的第二步异步分发
  • ReceiverDispatcher的内部类Args在注册BroadcastReceiver所在的线程消息队列中处理广播,最终分发给BroadcastReceiver,执行其onReceive方法

总结

以上就是广播的种类、注册方式、注册过程、发送过程的分析,水平有限,有错之处请理解。

本文参考老罗的
Android应用程序注册广播接收器(registerReceiver)的过程分析
Android应用程序发送广播(sendBroadcast)的过程分析

你可能感兴趣的:(Android开发)