BroadcastReceiver

   BroadcastReceiver,似乎很简单!很多书籍上都有描述,从书中例子来看百多行代码就搞定!但是真的如此吗?

       假如有个需求,要求大概如下:当蓝牙开始搜索设备时,点亮LED;搜索停止时,关掉LED。这个需求开始检讨时,认为新规一个BroadcastReceiver子类,收到蓝牙开始搜索/停止的广播通知时,点亮/关闭LED即可。可是经过一番调查后,木子发现:BroadcastReceiver没那么简单。让我们一起来看看吧!

第一,    sendBroadcast sendOrderedBroadcast / sendStickyBroadcast是异步函数,它们发出广播通知后即返回,不会等到所有receiver收到通知后才继续往下走,所以控制LED的receiver动作就不会和真正的蓝牙搜索同步,甚至可能搜索完了时,LED才开始点灯在;

涉及的代码比较多,来张时序图吧!

 

 

第二,      为什么对sendOrderedBroadcast发出的broadcast,receiver在处理时能重新赋值(通过setResultCode / setResultData / setResultExtras),但是对sendBroadcast发出的broadcast就不行;

看看源代码吧!打开BroadReceiv.java文件,可发现如下代码:

    public final void setResultExtras(Bundle extras) {

        checkSynchronousHint();

        mResultExtras = extras;

}

    void checkSynchronousHint() {

        // Note that we don't assert when receiving the initial sticky value,

        // since that may have come from an ordered broadcast.  We'll catch

        // them later when the real broadcast happens again.

        if (mOrderedHint || mInitialStickyHint) {

            return;

        }

        RuntimeException e = new RuntimeException(

                "BroadcastReceiver trying to return result during a non-ordered broadcast");

        e.fillInStackTrace();

        Log.e("BroadcastReceiver", e.getMessage(), e);

    }

在这里,我们发现可否调用setResultExtras之类的函数取决于mOrderedHint || mInitialStickyHint的值,那么它们是何时赋值的呢?

继续看代码,打开ActivityThread.java,发现以下代码:

private final void handleReceiver(ReceiverData data) {

……

            ContextImpl context = (ContextImpl)app.getBaseContext();

            receiver.setOrderedHint(true);

            receiver.setResult(data.resultCode, data.resultData,

                data.resultExtras);

            receiver.setOrderedHint(data.sync);

            receiver.onReceive(context.getReceiverRestrictedContext(),

                    data.intent);

……

}

还有,ReceiverDispatcher. InnerReceiver. Args. Run() {

……

                        receiver.setOrderedHint(true);

                        receiver.setResult(mCurCode, mCurData, mCurMap);

                        receiver.clearAbortBroadcast();

                        receiver.setOrderedHint(mCurOrdered);

                        receiver.setInitialStickyHint(mCurSticky);

                        receiver.onReceive(mContext, intent);

……

}

现在是不是很清楚原因了?

 

第三,      什么是sendStickyBroadcast?SDK上说的简单了点,至少木子没看懂!

 

打开ActivityManagerService.java文件,找到如下代码:

    public Intent registerReceiver(IApplicationThread caller,

            IIntentReceiver receiver, IntentFilter filter, String permission) {

……

            // 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 {

                allSticky = getStickiesLocked(null, filter, allSticky);

            }

……

}

    private final List getStickiesLocked(String action, IntentFilter filter,

            List cur) {

        final ContentResolver resolver = mContext.getContentResolver();

        final ArrayList<Intent> list = mStickyBroadcasts.get(action);

        if (list == null) {

            return cur;

        }

        int N = list.size();

        for (int i=0; i<N; i++) {

            Intent intent = list.get(i);

            if (filter.match(resolver, intent, true, TAG) >= 0) {

                if (cur == null) {

                    cur = new ArrayList<Intent>();

                }

                cur.add(intent);

            }

        }

        return cur;

    }

从这里可以看出调用 Intent android.content.ContextWrapper.registerReceiver(BroadcastReceiver receiver, IntentFilter filter) 返回的intent是从mStickyBroadcasts里得到的。那么mStickyBroadcasts里的内容从何而来?
接着看:
 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) {

……

        // 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 BROADCAST_STICKY_CANT_HAVE_PERMISSION;

            }

            if (intent.getComponent() != null) {

                throw new SecurityException(

                        "Sticky broadcasts can't target a specific component");

            }

            ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());

            if (list == null) {

                list = new ArrayList<Intent>();

                mStickyBroadcasts.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));

            }

        }

……

}

从这里,我们可以知道mStickyBroadcasts的内容是系统处理sendStickyBroadcast时添加的,现在很清楚了吧!所以如果你调用Intent android.content.ContextWrapper.registerReceiver的时机点在调用sendStickyBroadcast后,你就可以直接得到该intent,从而可以快速的得到intent的数据,继续你的处理。

注意事项,调用sendStickyBroadcast,需要在app的AndroidManifest.xml里添加

<uses-permission android:name="android.permission.BROADCAST_STICKY" />

 

第四,    接收sendOrderedBroadcast发出的broadcast的receivers是如何排序的?

首先,在app安装时,PackageManager会根据intentFilter给activity、service和receiver建立列表,具体代码位于intentResolver.java文件,调用顺序如下:

addFilter  》register_mime_types

其次,broadcast发送过程中,有以下代码:

    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) {

……

        // Figure out who all will receive this broadcast.

        List receivers = null;

        List<BroadcastFilter> registeredReceivers = null;

        try {

            if (intent.getComponent() != null) {

                // Broadcast is going to one specific receiver class...

                ActivityInfo ai = ActivityThread.getPackageManager().

                    getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS);

                if (ai != null) {

                    receivers = new ArrayList();

                    ResolveInfo ri = new ResolveInfo();

                    ri.activityInfo = ai;

                    receivers.add(ri);

                }

            } else {

                // Need to resolve the intent to interested receivers...

                if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)

                         == 0) {

                    receivers =

                        ActivityThread.getPackageManager().queryIntentReceivers(

                                intent, resolvedType, STOCK_PM_FLAGS);

                }

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

            }

        } catch (RemoteException ex) {

……

}

让我们看一下函数queryIntentReceivers,其核心处理在intentResolver.java文件

    public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {

……

        sortResults(finalList);

……

}

    protected void sortResults(List<R> results) {

        Collections.sort(results, mResolvePrioritySorter);

    }

 

    // Sorts a List of IntentFilter objects into descending priority order.

    private static final Comparator mResolvePrioritySorter = new Comparator() {

        public int compare(Object o1, Object o2) {

            float q1 = ((IntentFilter)o1).getPriority();

            float q2 = ((IntentFilter)o2).getPriority();

            return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);

        }

    };

从这里receiver的优先级取决于intentFilter的属性Priority,它可以在AndroidManifest.xml里指明,例如,

            <receiver android:name=".MyReceiver">

                  <intent-filter android:priority="100">

                          <action      android:name="com.david.broadcast001.MY_ACTION" />

                  </intent-filter>

           </receiver>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值