2 * Copyright (C) 2011 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android
.support
.v4
.content
;
19 import java
.util
.ArrayList
;
20 import java
.util
.HashMap
;
23 import android
.content
.BroadcastReceiver
;
24 import android
.content
.Context
;
25 import android
.content
.Intent
;
26 import android
.content
.IntentFilter
;
27 import android
.net
.Uri
;
28 import android
.os
.Handler
;
29 import android
.os
.Message
;
30 import android
.util
.Log
;
33 * Helper to register for and send broadcasts of Intents to local objects
34 * within your process. This is has a number of advantages over sending
35 * global broadcasts with {@link android.content.Context#sendBroadcast}:
37 * <li> You know that the data you are broadcasting won't leave your app, so
38 * don't need to worry about leaking private data.
39 * <li> It is not possible for other applications to send these broadcasts to
40 * your app, so you don't need to worry about having security holes they can
42 * <li> It is more efficient than sending a global broadcast through the
46 public class LocalBroadcastManager
{
47 private static class ReceiverRecord
{
48 final IntentFilter filter
;
49 final BroadcastReceiver receiver
;
52 ReceiverRecord(IntentFilter _filter
, BroadcastReceiver _receiver
) {
58 public String
toString() {
59 StringBuilder builder
= new StringBuilder(128);
60 builder
.append("Receiver{");
61 builder
.append(receiver
);
62 builder
.append(" filter=");
63 builder
.append(filter
);
65 return builder
.toString();
69 private static class BroadcastRecord
{
71 final ArrayList
<ReceiverRecord
> receivers
;
73 BroadcastRecord(Intent _intent
, ArrayList
<ReceiverRecord
> _receivers
) {
75 receivers
= _receivers
;
79 private static final String TAG
= "LocalBroadcastManager";
80 private static final boolean DEBUG
= false;
82 private final Context mAppContext
;
84 private final HashMap
<BroadcastReceiver
, ArrayList
<IntentFilter
>> mReceivers
85 = new HashMap
<BroadcastReceiver
, ArrayList
<IntentFilter
>>();
86 private final HashMap
<String
, ArrayList
<ReceiverRecord
>> mActions
87 = new HashMap
<String
, ArrayList
<ReceiverRecord
>>();
89 private final ArrayList
<BroadcastRecord
> mPendingBroadcasts
90 = new ArrayList
<BroadcastRecord
>();
92 static final int MSG_EXEC_PENDING_BROADCASTS
= 1;
94 private final Handler mHandler
;
96 private static final Object mLock
= new Object();
97 private static LocalBroadcastManager mInstance
;
99 public static LocalBroadcastManager
getInstance(Context context
) {
100 synchronized (mLock
) {
101 if (mInstance
== null) {
102 mInstance
= new LocalBroadcastManager(context
.getApplicationContext());
108 private LocalBroadcastManager(Context context
) {
109 mAppContext
= context
;
110 mHandler
= new Handler(context
.getMainLooper()) {
113 public void handleMessage(Message msg
) {
115 case MSG_EXEC_PENDING_BROADCASTS
:
116 executePendingBroadcasts();
119 super.handleMessage(msg
);
126 * Register a receive for any local broadcasts that match the given IntentFilter.
128 * @param receiver The BroadcastReceiver to handle the broadcast.
129 * @param filter Selects the Intent broadcasts to be received.
131 * @see #unregisterReceiver
133 public void registerReceiver(BroadcastReceiver receiver
, IntentFilter filter
) {
134 synchronized (mReceivers
) {
135 ReceiverRecord entry
= new ReceiverRecord(filter
, receiver
);
136 ArrayList
<IntentFilter
> filters
= mReceivers
.get(receiver
);
137 if (filters
== null) {
138 filters
= new ArrayList
<IntentFilter
>(1);
139 mReceivers
.put(receiver
, filters
);
142 for (int i
=0; i
<filter
.countActions(); i
++) {
143 String action
= filter
.getAction(i
);
144 ArrayList
<ReceiverRecord
> entries
= mActions
.get(action
);
145 if (entries
== null) {
146 entries
= new ArrayList
<ReceiverRecord
>(1);
147 mActions
.put(action
, entries
);
155 * Unregister a previously registered BroadcastReceiver. <em>All</em>
156 * filters that have been registered for this BroadcastReceiver will be
159 * @param receiver The BroadcastReceiver to unregister.
161 * @see #registerReceiver
163 public void unregisterReceiver(BroadcastReceiver receiver
) {
164 synchronized (mReceivers
) {
165 ArrayList
<IntentFilter
> filters
= mReceivers
.remove(receiver
);
166 if (filters
== null) {
169 for (int i
=0; i
<filters
.size(); i
++) {
170 IntentFilter filter
= filters
.get(i
);
171 for (int j
=0; j
<filter
.countActions(); j
++) {
172 String action
= filter
.getAction(j
);
173 ArrayList
<ReceiverRecord
> receivers
= mActions
.get(action
);
174 if (receivers
!= null) {
175 for (int k
=0; k
<receivers
.size(); k
++) {
176 if (receivers
.get(k
).receiver
== receiver
) {
181 if (receivers
.size() <= 0) {
182 mActions
.remove(action
);
191 * Broadcast the given intent to all interested BroadcastReceivers. This
192 * call is asynchronous; it returns immediately, and you will continue
193 * executing while the receivers are run.
195 * @param intent The Intent to broadcast; all receivers matching this
196 * Intent will receive the broadcast.
198 * @see #registerReceiver
200 public boolean sendBroadcast(Intent intent
) {
201 synchronized (mReceivers
) {
202 final String action
= intent
.getAction();
203 final String type
= intent
.resolveTypeIfNeeded(
204 mAppContext
.getContentResolver());
205 final Uri data
= intent
.getData();
206 final String scheme
= intent
.getScheme();
207 final Set
<String
> categories
= intent
.getCategories();
209 final boolean debug
= DEBUG
||
210 ((intent
.getFlags() & Intent
.FLAG_DEBUG_LOG_RESOLUTION
) != 0);
212 TAG
, "Resolving type " + type
+ " scheme " + scheme
213 + " of intent " + intent
);
215 ArrayList
<ReceiverRecord
> entries
= mActions
.get(intent
.getAction());
216 if (entries
!= null) {
217 if (debug
) Log
.v(TAG
, "Action list: " + entries
);
219 ArrayList
<ReceiverRecord
> receivers
= null;
220 for (int i
=0; i
<entries
.size(); i
++) {
221 ReceiverRecord receiver
= entries
.get(i
);
222 if (debug
) Log
.v(TAG
, "Matching against filter " + receiver
.filter
);
224 if (receiver
.broadcasting
) {
226 Log
.v(TAG
, " Filter's target already added");
231 int match
= receiver
.filter
.match(action
, type
, scheme
, data
,
232 categories
, "LocalBroadcastManager");
234 if (debug
) Log
.v(TAG
, " Filter matched! match=0x" +
235 Integer
.toHexString(match
));
236 if (receivers
== null) {
237 receivers
= new ArrayList
<ReceiverRecord
>();
239 receivers
.add(receiver
);
240 receiver
.broadcasting
= true;
245 case IntentFilter
.NO_MATCH_ACTION
: reason
= "action"; break;
246 case IntentFilter
.NO_MATCH_CATEGORY
: reason
= "category"; break;
247 case IntentFilter
.NO_MATCH_DATA
: reason
= "data"; break;
248 case IntentFilter
.NO_MATCH_TYPE
: reason
= "type"; break;
249 default: reason
= "unknown reason"; break;
251 Log
.v(TAG
, " Filter did not match: " + reason
);
256 if (receivers
!= null) {
257 for (int i
=0; i
<receivers
.size(); i
++) {
258 receivers
.get(i
).broadcasting
= false;
260 mPendingBroadcasts
.add(new BroadcastRecord(intent
, receivers
));
261 if (!mHandler
.hasMessages(MSG_EXEC_PENDING_BROADCASTS
)) {
262 mHandler
.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS
);
272 * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for
273 * the Intent this function will block and immediately dispatch them before
276 public void sendBroadcastSync(Intent intent
) {
277 if (sendBroadcast(intent
)) {
278 executePendingBroadcasts();
282 private void executePendingBroadcasts() {
284 BroadcastRecord
[] brs
= null;
285 synchronized (mReceivers
) {
286 final int N
= mPendingBroadcasts
.size();
290 brs
= new BroadcastRecord
[N
];
291 mPendingBroadcasts
.toArray(brs
);
292 mPendingBroadcasts
.clear();
294 for (int i
=0; i
<brs
.length
; i
++) {
295 BroadcastRecord br
= brs
[i
];
296 for (int j
=0; j
<br
.receivers
.size(); j
++) {
297 br
.receivers
.get(j
).receiver
.onReceive(mAppContext
, br
.intent
);