In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-09 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
Today, I would like to share with you the relevant knowledge of what the role of the EventBus framework is, detailed content and clear logic. I believe most people still know too much about this knowledge, so share this article for your reference. I hope you can get something after reading this article.
The role of EventBus
There are various communication scenarios in Android, such as the jump between Activity, the interaction between Activity and Fragment and other components, and the callback callback after a time-consuming operation (such as requesting a network). They often need to hold each other's reference, and the writing of each scenario is also different, resulting in high coupling and inconvenient maintenance.
Take the communication between Activity and Fragment as an example, the official practice is to implement an interface, then hold the reference of the other party, and then forcibly convert it to the interface type, resulting in high coupling.
Take the return of Activity as an example. One side needs to set setResult, while the other needs to do corresponding processing in onActivityResult. If there are multiple return paths, the code will be very bloated. SimpleEventBus (the simplified version of the event bus implemented in this article) is written as follows:
Public class MainActivity extends AppCompatActivity {TextView mTextView; @ Override protected void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); mTextView = findViewById (R.id.tv_demo); mTextView.setText ("MainActivity") MTextView.setOnClickListener (new View.OnClickListener () {@ Override public void onClick (View view) {Intent intent = new Intent (MainActivity.this, SecondActivity.class); startActivity (intent);}}); EventBus.getDefault () .register (this) @ Subscribe (threadMode = ThreadMode.MAIN) public void onReturn (Message message) {mTextView.setText (message.mContent);} @ Override protected void onDestroy () {super.onDestroy (); EventBus.getDefault () .unregister (this);}}
Source Activity:
Public class SecondActivity extends AppCompatActivity {TextView mTextView; @ Override protected void onCreate (@ Nullable Bundle savedInstanceState) {super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); mTextView = findViewById (R.id.tv_demo); mTextView.setText ("SecondActivity, click back") MTextView.setOnClickListener (new View.OnClickListener () {@ Override public void onClick (View view) {Message message = new Message (); message.mContent = "return from SecondActivity"; EventBus.getDefault () .post (message); finish ();}});}}
The effect is as follows:
Image
It seems to be just a different way of writing, but as the scene becomes more complex, EventBus can show better decoupling ability.
Background knowledge
It mainly involves three aspects of knowledge:
Observer mode (or publish-subscribe model)
Android message mechanism
Java concurrent programming
Realization process
The use of EventBus` is divided into three steps: register listening, sending events and canceling listening. This article will also implement these three steps.
Register monitoring
Define an annotation:
@ Retention (RetentionPolicy.RUNTIME) @ Target (ElementType.METHOD) public @ interface Subscribe {ThreadMode threadMode () default ThreadMode.POST;}
Greenrobot/EventBus also supports priority and sticky events, which only supports the most basic capability: thread differentiation, because operations such as updating UI must be placed on the main thread. ThreadMode` is as follows:
Public enum ThreadMode {MAIN, / / main thread POST, / / message sending thread ASYNC / / A new thread sends}
When the object is initialized, register with the register method, which parses all the methods of the registered object and parses the annotated method (that is, the observer). The core code is as follows:
Public class EventBus {... Public void register (Object subscriber) {if (subscriber = = null) {return;} synchronized (this) {subscribe (subscriber);}}. Private void subscribe (Object subscriber) {if (subscriber = = null) {return;} / / TODO ugly indentation Class clazz = subscriber.getClass (); while (clazz! = null & &! isSystemClass (clazz.getName () {final Method [] methods = clazz.getDeclaredMethods () For (Method method: methods) {Subscribe annotation = method.getAnnotation (Subscribe.class); if (annotation! = null) {Class [] paramClassArray = method.getParameterTypes (); if (paramClassArray! = null & & paramClassArray.length = = 1) {Class paramType = convertType (paramClassArray [0]) EventType eventType = new EventType (paramType); SubscriberMethod subscriberMethod = new SubscriberMethod (method, annotation.threadMode (), paramType); realSubscribe (subscriber, subscriberMethod, eventType);} clazz = clazz.getSuperclass ();}}. Private void realSubscribe (Object subscriber, SubscriberMethod method, EventType eventType) {CopyOnWriteArrayList subscriptions = mSubscriptionsByEventtype.get (subscriber); if (subscriptions = = null) {subscriptions = new CopyOnWriteArrayList ();} Subscription subscription = new Subscription (subscriber, method); if (subscriptions.contains (subscription)) {return;} subscriptions.add (subscription); mSubscriptionsByEventtype.put (eventType, subscriptions) }...}
After executing this logic, all the observer methods of the object are stored in a Map, whose Key is EventType, that is, the type of observation event. Value is a list of all methods (that is, observers) that subscribe to this type of event, and each method and object are encapsulated into a Subscription class:
Public class Subscription {public final Reference subscriber; public final SubscriberMethod subscriberMethod; public Subscription (Object subscriber, SubscriberMethod subscriberMethod) {this.subscriber = new WeakReference (subscriber); / / EventBus3 does not use weak references? This.subscriberMethod = subscriberMethod;} @ Override public int hashCode () {return subscriber.hashCode () + subscriberMethod.methodString.hashCode ();} @ Override public boolean equals (Object obj) {if (obj instanceof Subscription) {Subscription other = (Subscription) obj; return subscriber = = other.subscribe & & subscriberMethod.equals (other.subscriberMethod);} else {return false }}}
In this way, it is the core logic of the registered monitoring method.
Message sending
The sending code for the message is simple:
Public class EventBus {... Private EventDispatcher mEventDispatcher = new EventDispatcher (); private ThreadLocal mThreadLocalEvents = new ThreadLocal () {@ Override protected Queue initialValue () {return new ConcurrentLinkedQueue ();};. Public void post (Object message) {if (message = = null) {return;} mThreadLocalEvents.get (). Offer (new EventType (message.getClass ()); mEventDispatcher.dispatchEvents (message);}.}
What is more complicated is that it needs to be published in the corresponding thread according to the thread mode declared by the annotation:
Public class EventBus {... Private class EventDispatcher {private IEventHandler mMainEventHandler = new MainEventHandler (); private IEventHandler mPostEventHandler = new DefaultEventHandler (); private IEventHandler mAsyncEventHandler = new AsyncEventHandler (); void dispatchEvents (Object message) {Queue eventQueue = mThreadLocalEvents.get (); while (eventQueue.size () > 0) {handleEvent (eventQueue.poll (), message) }} private void handleEvent (EventType eventType, Object message) {List subscriptions = mSubscriptionsByEventtype.get (eventType); if (subscriptions = = null) {return;} for (Subscription subscription: subscriptions) {IEventHandler eventHandler = getEventHandler (subscription.subscriberMethod.threadMode); eventHandler.handleEvent (subscription, message) }} private IEventHandler getEventHandler (ThreadMode mode) {if (mode = = ThreadMode.ASYNC) {return mAsyncEventHandler;} if (mode = = ThreadMode.POST) {return mPostEventHandler;} return mMainEventHandler;}} / / end of the class...}
The three threading modes are as follows: DefaultEventHandler (execute the observer release method in the publishing thread):
Public class DefaultEventHandler implements IEventHandler {@ Override public void handleEvent (Subscription subscription, Object message) {if (subscription = = null | | subscription.subscriber.get () = = null) {return;} try {subscription.subscriberMethod.method.invoke (subscription.subscriber.get (), message);} catch (IllegalAccessException | InvocationTargetException e) {e.printStackTrace ();}
MainEventHandler (executed on the main thread):
Public class MainEventHandler implements IEventHandler {private Handler mMainHandler = new Handler (Looper.getMainLooper ()); DefaultEventHandler hanlder = new DefaultEventHandler (); @ Override public void handleEvent (final Subscription subscription, final Object message) {mMainHandler.post (new Runnable () {@ Override public void run () {hanlder.handleEvent (subscription, message);}});}}
AsyncEventHandler (open a new thread for execution):
Public class AsyncEventHandler implements IEventHandler {private DispatcherThread mDispatcherThread; private IEventHandler mEventHandler = new DefaultEventHandler (); public AsyncEventHandler () {mDispatcherThread = new DispatcherThread (AsyncEventHandler.class.getSimpleName ()); mDispatcherThread.start ();} @ Override public void handleEvent (final Subscription subscription, final Object message) {mDispatcherThread.post (new Runnable ()) {@ Override public void run () {mEventHandler.handleEvent (subscription, message) }});} private class DispatcherThread extends HandlerThread {/ / Handle Handler mAsyncHandler; DispatcherThread (String name) {super (name) associated with AsyncExecutor message queue;} public void post (Runnable runnable) {if (mAsyncHandler = = null) {throw new NullPointerException ("mAsyncHandler = = null, please call start () first.") } mAsyncHandler.post (runnable);} @ Override public synchronized void start () {super.start (); mAsyncHandler = new Handler (this.getLooper ());}
This is the code that publishes the message.
Log off snooping
If the last object is destroyed, you have to log off the listener, otherwise it will easily lead to memory leakage. Currently, SimpleEventBus uses WeakReference, which can be automatically recycled through GC, but I don't know why greenrobot/EventBus is not implemented in this way, which needs to be studied. To log out the listener is to traverse the Map. You can remove the subscription of the object:
Public class EventBus {... Public void unregister (Object subscriber) {if (subscriber = = null) {return;} Iterator iterator = mSubscriptionsByEventtype.values (). Iterator (); while (iterator.hasNext ()) {CopyOnWriteArrayList subscriptions = iterator.next (); if (subscriptions! = null) {List foundSubscriptions = new LinkedList () For (Subscription subscription: subscriptions) {Object cacheObject = subscription.subscriber.get (); if (cacheObject = = null | | cacheObject.equals (subscriber)) {foundSubscriptions.add (subscription);}} subscriptions.removeAll (foundSubscriptions) } / / if the number of subscribers for an Event is empty, you need to clear the if from the map (subscriptions = = null | | subscriptions.size () = = 0) {iterator.remove ();}.}
Limitation
Due to the time constraint, we have only studied the core part of EventBus, and there are a few points worthy of in-depth study. I would like to record it here and welcome the passing Daniel to give me some advice.
Performance problem
In practice, annotations and reflections can lead to performance problems, but EventBus3 has basically solved this problem through Subscriber Index, and the implementation is also very interesting. The annotation processor (Annotation Processor) advances the time-consuming logic from the run time to the compile time, and speeds up the run time through the indexes generated at the compile time, which is the origin of the name.
Availability issu
If many subscribers will affect the experience, after all, the original method is peer-to-peer messaging, there will not be this problem, what if some subscribers have not been initialized. Wait. Currently, EventBus3 provides priority and sticky event attributes to further meet development needs. But it remains to be seen whether the problem has been completely solved.
These are all the contents of the article "what is the function of the EventBus Framework?" Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.