Last active
August 29, 2015 14:14
-
-
Save lidemin/7206e8e0440ff551438a to your computer and use it in GitHub Desktop.
LocalBroadcastManager enhancement
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
abstract public class BaseFragment extends Fragment { | |
/** Constant String representing class name for logs.*/ | |
protected String _tag = ((Object) this).getClass().getSimpleName(); | |
//broadcast manager | |
protected DLBroadcastManager broadcastManager; | |
protected BroadcastReceiver broadcastReceiver; | |
private IntentFilter intentFilter; | |
private HashMap<String, DLBroadcastHandler> messageHandlers; | |
/** Empty constructor for using this fragment in xml.*/ | |
public BaseFragment() { | |
} | |
@Override | |
public void onAttach(Activity activity) { | |
super.onAttach(activity); | |
broadcastManager = DLBroadcastManager.getInstance(activity); | |
} | |
@Override | |
public void onResume() { | |
super.onResume(); | |
setUpMessageHandler(); | |
if (broadcastReceiver != null && intentFilter != null) { | |
broadcastManager.registerReceiver(broadcastReceiver, intentFilter); | |
} | |
} | |
@Override | |
public void onPause() { | |
super.onPause(); | |
if (broadcastReceiver != null) { | |
broadcastManager.unregisterReceiver(broadcastReceiver); | |
} | |
} | |
private void setUpMessageHandler() { | |
if (messageHandlers != null) { | |
intentFilter = new IntentFilter(); | |
for (String action : messageHandlers.keySet()) { | |
intentFilter.addAction(action); | |
} | |
broadcastReceiver = new BroadcastReceiver() { | |
@Override | |
public void onReceive(Context context, Intent intent) { | |
if (intent != null && intent.getAction() != null) { | |
String action = intent.getAction(); | |
for (Map.Entry<String, DLBroadcastHandler> entry : messageHandlers.entrySet()) { | |
if (entry.getKey().equalsIgnoreCase(action)) { | |
entry.getValue().process(intent); | |
return; | |
} | |
} | |
} | |
} | |
}; | |
} | |
} | |
/** | |
* this method is used for set up message handlers and it is supposed to be called before | |
* onResume() in lifecycle. | |
* | |
* @param action | |
* @param handler | |
*/ | |
protected BaseFragment addMessageHandler(String action, DLBroadcastHandler handler) { | |
if (messageHandlers == null) { | |
messageHandlers = new HashMap<String, DLBroadcastHandler>(); | |
} | |
messageHandlers.put(action, handler); | |
return this; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface DLBroadcastHandler { | |
/** | |
* process handling the intent | |
* @param intent is the intent received. | |
*/ | |
public void process(Intent intent); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Created by damon on 19/12/14. | |
*/ | |
public class DLBroadcastManager { | |
private static class ReceiverRecord { | |
final IntentFilter filter; | |
final BroadcastReceiver receiver; | |
boolean broadcasting; | |
ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) { | |
filter = _filter; | |
receiver = _receiver; | |
} | |
@Override | |
public String toString() { | |
StringBuilder builder = new StringBuilder(128); | |
builder.append("Receiver{"); | |
builder.append(receiver); | |
builder.append(" filter="); | |
builder.append(filter); | |
builder.append("}"); | |
return builder.toString(); | |
} | |
} | |
private static class BroadcastRecord { | |
final Intent intent; | |
final ArrayList<ReceiverRecord> receivers; | |
BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) { | |
intent = _intent; | |
receivers = _receivers; | |
} | |
} | |
private static final String TAG = "LocalBroadcastManager"; | |
private static final boolean DEBUG = false; | |
private final Context mAppContext; | |
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers | |
= new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>(); | |
private final HashMap<String, ArrayList<ReceiverRecord>> mActions | |
= new HashMap<String, ArrayList<ReceiverRecord>>(); | |
private final ArrayList<BroadcastRecord> mPendingBroadcasts | |
= new ArrayList<BroadcastRecord>(); | |
private final ArrayList<Intent> mStickyBroadcasts | |
= new ArrayList<Intent>(); | |
static final int MSG_EXEC_PENDING_BROADCASDL = 1; | |
private final Handler mHandler; | |
private static final Object mLock = new Object(); | |
private static DLBroadcastManager mInstance; | |
public static DLBroadcastManager getInstance(Context context) { | |
synchronized (mLock) { | |
if (mInstance == null) { | |
mInstance = new DLBroadcastManager(context.getApplicationContext()); | |
} | |
return mInstance; | |
} | |
} | |
private DLBroadcastManager(Context context) { | |
mAppContext = context; | |
mHandler = new Handler(context.getMainLooper()) { | |
@Override | |
public void handleMessage(Message msg) { | |
switch (msg.what) { | |
case MSG_EXEC_PENDING_BROADCASDL: | |
executePendingBroadcasts(); | |
break; | |
default: | |
super.handleMessage(msg); | |
} | |
} | |
}; | |
} | |
/** | |
* Register a receive for any local broadcasts that match the given IntentFilter. | |
* | |
* @param receiver The BroadcastReceiver to handle the broadcast. | |
* @param filter Selects the Intent broadcasts to be received. | |
* @see #unregisterReceiver | |
*/ | |
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { | |
synchronized (mReceivers) { | |
ReceiverRecord entry = new ReceiverRecord(filter, receiver); | |
ArrayList<IntentFilter> filters = mReceivers.get(receiver); | |
if (filters == null) { | |
filters = new ArrayList<IntentFilter>(1); | |
mReceivers.put(receiver, filters); | |
} | |
filters.add(filter); | |
for (int i = 0; i < filter.countActions(); i++) { | |
String action = filter.getAction(i); | |
ArrayList<ReceiverRecord> entries = mActions.get(action); | |
if (entries == null) { | |
entries = new ArrayList<ReceiverRecord>(1); | |
mActions.put(action, entries); | |
} | |
entries.add(entry); | |
} | |
//check sticky broadcasts | |
for (Intent intent : mStickyBroadcasts) { | |
if (mActions.containsKey(intent.getAction())) { | |
sendBroadcast(intent, false); | |
} | |
} | |
} | |
} | |
/** | |
* Unregister a previously registered BroadcastReceiver. <em>All</em> | |
* filters that have been registered for this BroadcastReceiver will be | |
* removed. | |
* | |
* @param receiver The BroadcastReceiver to unregister. | |
* @see #registerReceiver | |
*/ | |
public void unregisterReceiver(BroadcastReceiver receiver) { | |
synchronized (mReceivers) { | |
ArrayList<IntentFilter> filters = mReceivers.remove(receiver); | |
if (filters == null) { | |
return; | |
} | |
for (int i = 0; i < filters.size(); i++) { | |
IntentFilter filter = filters.get(i); | |
for (int j = 0; j < filter.countActions(); j++) { | |
String action = filter.getAction(j); | |
ArrayList<ReceiverRecord> receivers = mActions.get(action); | |
if (receivers != null) { | |
for (int k = 0; k < receivers.size(); k++) { | |
if (receivers.get(k).receiver == receiver) { | |
receivers.remove(k); | |
k--; | |
} | |
} | |
if (receivers.size() <= 0) { | |
mActions.remove(action); | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* remove the sticky broadcasts . | |
* | |
* @param action is the action need to be removed. | |
*/ | |
public void removeStickyBroadcast(String action) { | |
if (action == null || action.length() <= 0) { | |
return; | |
} | |
synchronized (mStickyBroadcasts) { | |
Iterator<Intent> intentIterator = mStickyBroadcasts.iterator(); | |
while (intentIterator.hasNext()) { | |
Intent intent = intentIterator.next(); | |
if (action.equalsIgnoreCase(intent.getAction())) { | |
intentIterator.remove(); | |
} | |
} | |
} | |
} | |
/** | |
* remove the sticky broadcasts . | |
* | |
* @param action is the action need to be removed. | |
*/ | |
public void removeStickyBroadcast(String action, Bundle bundle) { | |
if (action == null || action.length() <= 0) { | |
return; | |
} | |
if (bundle == null) { | |
return; | |
} | |
synchronized (mStickyBroadcasts) { | |
Iterator<Intent> intentIterator = mStickyBroadcasts.iterator(); | |
while (intentIterator.hasNext()) { | |
Intent intent = intentIterator.next(); | |
if (action.equalsIgnoreCase(intent.getAction()) | |
&& equalBundles(bundle, intent.getExtras())) { | |
intentIterator.remove(); | |
} | |
} | |
} | |
} | |
/** | |
* remove all the sticky broadcasts. | |
*/ | |
public void removeAllStickyBroadcasts() { | |
synchronized (mStickyBroadcasts) { | |
mStickyBroadcasts.clear(); | |
} | |
} | |
/** | |
* send broadcast with specified action. | |
* | |
* @param action is the action . | |
* @return | |
*/ | |
public boolean sendBroadcast(String action) { | |
Intent intent = new Intent(action); | |
return sendBroadcast(intent); | |
} | |
/** | |
* send broadcast with specified action. | |
* | |
* @param action is the action . | |
* @return | |
*/ | |
public boolean sendBroadcast(String action, boolean isSticky) { | |
Intent intent = new Intent(action); | |
return sendBroadcast(intent, isSticky); | |
} | |
/** | |
* send broadcast with specified action. | |
* | |
* @param action is the action . | |
* @return | |
*/ | |
public boolean sendBroadcast(String action, Bundle bundle, boolean isSticky) { | |
Intent intent = new Intent(action); | |
if (bundle != null) { | |
intent.putExtras(bundle); | |
} | |
return sendBroadcast(intent, isSticky); | |
} | |
/** | |
* Broadcast the given intent to all interested BroadcastReceivers. This | |
* call is asynchronous; it returns immediately, and you will continue | |
* executing while the receivers are run. | |
* | |
* @param intent The Intent to broadcast; all receivers matching this | |
* Intent will receive the broadcast. | |
* @see #registerReceiver | |
*/ | |
public boolean sendBroadcast(Intent intent) { | |
return sendBroadcast(intent, false); | |
} | |
/** | |
* Broadcast the given intent to all interested BroadcastReceivers. This | |
* call is asynchronous; it returns immediately, and you will continue | |
* executing while the receivers are run. | |
* | |
* @param intent The Intent to broadcast; all receivers matching this | |
* Intent will receive the broadcast. | |
* @param isSticky The flag of this intent is need to be sticky or not. | |
* @see #registerReceiver | |
*/ | |
public boolean sendBroadcast(Intent intent, boolean isSticky) { | |
if (isSticky) { | |
mStickyBroadcasts.add(intent); | |
} | |
synchronized (mReceivers) { | |
final String action = intent.getAction(); | |
final String type = intent.resolveTypeIfNeeded( | |
mAppContext.getContentResolver()); | |
final Uri data = intent.getData(); | |
final String scheme = intent.getScheme(); | |
final Set<String> categories = intent.getCategories(); | |
final boolean debug = DEBUG || | |
((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); | |
if (debug) Log.v( | |
TAG, "Resolving type " + type + " scheme " + scheme | |
+ " of intent " + intent); | |
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction()); | |
if (entries != null) { | |
if (debug) Log.v(TAG, "Action list: " + entries); | |
ArrayList<ReceiverRecord> receivers = null; | |
for (int i = 0; i < entries.size(); i++) { | |
ReceiverRecord receiver = entries.get(i); | |
if (debug) Log.v(TAG, "Matching against filter " + receiver.filter); | |
if (receiver.broadcasting) { | |
if (debug) { | |
Log.v(TAG, " Filter's target already added"); | |
} | |
continue; | |
} | |
int match = receiver.filter.match(action, type, scheme, data, | |
categories, "LocalBroadcastManager"); | |
if (match >= 0) { | |
if (debug) Log.v(TAG, " Filter matched! match=0x" + | |
Integer.toHexString(match)); | |
if (receivers == null) { | |
receivers = new ArrayList<ReceiverRecord>(); | |
} | |
receivers.add(receiver); | |
receiver.broadcasting = true; | |
} else { | |
if (debug) { | |
String reason; | |
switch (match) { | |
case IntentFilter.NO_MATCH_ACTION: | |
reason = "action"; | |
break; | |
case IntentFilter.NO_MATCH_CATEGORY: | |
reason = "category"; | |
break; | |
case IntentFilter.NO_MATCH_DATA: | |
reason = "data"; | |
break; | |
case IntentFilter.NO_MATCH_TYPE: | |
reason = "type"; | |
break; | |
default: | |
reason = "unknown reason"; | |
break; | |
} | |
Log.v(TAG, " Filter did not match: " + reason); | |
} | |
} | |
} | |
if (receivers != null) { | |
for (int i = 0; i < receivers.size(); i++) { | |
receivers.get(i).broadcasting = false; | |
} | |
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers)); | |
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASDL)) { | |
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASDL); | |
} | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Like {@link #sendBroadcast(Intent, boolean)}, but if there are any receivers for | |
* the Intent this function will block and immediately dispatch them before | |
* returning. | |
*/ | |
public void sendBroadcastSync(Intent intent) { | |
if (sendBroadcast(intent, false)) { | |
executePendingBroadcasts(); | |
} | |
} | |
private void executePendingBroadcasts() { | |
while (true) { | |
BroadcastRecord[] brs = null; | |
synchronized (mReceivers) { | |
final int N = mPendingBroadcasts.size(); | |
if (N <= 0) { | |
return; | |
} | |
brs = new BroadcastRecord[N]; | |
mPendingBroadcasts.toArray(brs); | |
mPendingBroadcasts.clear(); | |
} | |
for (int i = 0; i < brs.length; i++) { | |
BroadcastRecord br = brs[i]; | |
for (int j = 0; j < br.receivers.size(); j++) { | |
br.receivers.get(j).receiver.onReceive(mAppContext, br.intent); | |
} | |
} | |
} | |
} | |
public boolean equalBundles(Bundle one, Bundle two) { | |
if (one.size() != two.size()) | |
return false; | |
Set<String> setOne = one.keySet(); | |
Object valueOne; | |
Object valueTwo; | |
for (String key : setOne) { | |
valueOne = one.get(key); | |
valueTwo = two.get(key); | |
if (valueOne instanceof Bundle && valueTwo instanceof Bundle && | |
!equalBundles((Bundle) valueOne, (Bundle) valueTwo)) { | |
return false; | |
} else if (valueOne == null) { | |
if (valueTwo != null || !two.containsKey(key)) | |
return false; | |
} else if (!valueOne.equals(valueTwo)) | |
return false; | |
} | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment