First, there is nothing wrong with the messaging mechanism, but the context in which we use it. Each technique has its advantages and value, but should not be abused, just like inheritance. No technique is one-size-fits-all, except for a few design principles that must be followed.

Before I get into this topic, I’d like to describe a painful experience I had on a project when I was exposed to radio news. As we all know, news information flow products will have multiple channels, each channel is a ListView or RecycleView, a ListView will have a lot of cards, each card corresponds to a news. In order to illustrate the problem, we use the screen shots of typical news products of information flow to show it:

The overall UI layout of the information flow product can be seen intuitively in the figure. The top is channel Tab, and the bottom is column Tab. Click the column Tab, such as the video button, and you will enter the video column, in which there will be many new channels. Each channel will have a lot of cards, and each card will have a delete button on it. Let’s restore a class diagram:


The requirement definition is that when the user clicks the delete button on the card, a user feedback popup box pops up. The previous status quo was to delete the card and pop up a Toast, which automatically disappeared two seconds later. The popbox requirement itself is very simple. However, when I made the popbox, integrated it into the project, and ran it, I found two problems:

  • Interface lag is very severe;
  • Cannot close after clicking options.

However, I have no problem putting it in the Demo, or anywhere else in the project. I did this in the project by calling the doDislike method on a delete message to remove the card directly from the list. So I replaced it with this new user feedback popbox where I called doDislike. When the user clicked on one of the popboxes, I closed the popbox and then performed the doDislike method. It doesn’t feel like there’s anything wrong with the logic, but the fact is the program is stuck badly. So I add Log output to the popbox entry, and oh, my God, 20 popboxes in a second. It turns out that each ListViewController is bound to an event handler that handles events from the card, and this object registers a message, DISLIKE_CLICK_MESSAGE, And this message is also used in the project broadcast message. When the user clicks the Delete button, the card class uses the broadcast message interface to send a DISLIKE_CLICK_MESSAGE message. When the ListViewController event handler receives the message, the doDislike method is executed. The advantage of using messages in this way is that the spatial distance between the card component and the ListView is avoided.

But this brings up a problem. As mentioned earlier, there are many channels in the project. A home page column has more than 20 channels, as well as video columns and other columns. Due to user experience and load performance considerations, caches often need to be prepared in advance, meaning that multiple channel objects have been created in the background. These objects will all receive the DISLIKE_CLICK_MESSAGE, and they will all execute the popbox, so the situation just described, two dozen popboxes at a time, of course, will be unusually slow, and the behavior is not correct. Why is there no problem with the previous deletion? Because when 20 toasts are stacked together, the copy is the same, and they all disappear after two seconds, so it is impossible to attract attention.

At this point, you might say, just query the data in the list, if there is no card data to delete, do not execute the popbox. Yes, this is how I tried to solve the problem. This is just the beginning of the experience. The first thing to do, of course, is to look it up in the list. If you can’t find it, you don’t pop the box. You don’t do anything. Think this is done, the actual measurement found that there will still be two consecutive frame. Continue to check the cause, because the home page and the channel list page are shared a data, that is to say, there are two ListViewController is the same data, so there is a problem, and even without considering the home page data duplication problem, who can guarantee that the server does not send the same card to the two channels? Our client program can not rely on the weather, the total figure out a way to solve the problem. ListViewController has a selected state. A column should have only one channel selected. Can we use this state to determine if the currently selected ListView will respond to a delete message? In addition to the front page news column, there are video columns and other columns, each of which may have a channel in the selected state. To fix this problem completely, we should also introduce an isActive state. When the column is invisible and overwritten by other columns, set isActive to false, and the front-end column isActive to true. Then, when the message is received, check the selected and isActive states. If both are true, the DISLIKE message can be processed.

When I tried to use this scheme to solve the problem, the problem is coming, if it is to design a new system, the state planning from the start, there is no question of nature, but in a very large system of the newly added a state of systemic rules, difficulty cans be imagined, high cost, also it is easy to appear problem, once the state maintenance is bad, There will be an endless process of burying, stepping on and filling pits.

Although it was impressive to imagine, I hit the brakes just in time, gave it a brief try and stopped the scheme.

New approach: Processing of the DISLIKE event does not use the message mechanism. Link card components, cards, listViews, event handlers, and so on in a clear chain. Because these objects are relational chains, there is an explicit lifetime management relationship (although there is garbage collection without considering when to destroy). It is perfectly possible to handle callback events from posterity in the Owner using the Delegate pattern. In our system, there is a UIEventDelegate interface design. The difference with the message mechanism is mainly between ListView and card component DeleteButton. The Delegate callback object must be set layer by layer, and the event will be reported layer by layer. The Owner calls obj.setEventHandler(this) when creating a child object. Any object that generates an event will either digest it or call back to the Owner instead of throwing it away via a message. The only drawback is that passing the Delegate callback interface when constructing an object is a bit cumbersome. But in this way, the transmission of the event is clear, who generated the event, who handles the event, the process is clear, there will be no more chaos, there will be no other irrelevant object to receive the event.

Finally, the problem was solved successfully through this solution. However, it took me two days to try various solutions to solve the problem. The whole process was far more complicated than what I described here. It took three days to implement the Popover requirement, and that was because of a generic Popover component. So, was it worth the two days of Bug solving?

Conclusion: Event transmission should have a clear and clear chain, rather than flying randomly. Whether it can be handled correctly depends on fate.

It is convenient and reasonable to use broadcast messages for things like memory alarms, application peels, and multi-language switches that are very generic and do not have event notifications associated with each business. However, the abuse of message mechanisms, especially broadcast messages, in complex business logic can be disastrous.