Foreword
Recently I have ported a rather large project from Qt (C ++) to Android (Java), in the process of work I often had to apply dynamic linking of objects. The trouble was that, in contrast to the usual signals and slots, Qt in Java is implemented through listeners, and how much I did not try to convince myself that this method is equivalent and also takes place to be the same convenience as when using signals and slots could not be reached.
For example, we need to associate a slider (QSlider in Qt or SeekBar in Android) with some action, at least to bind another slider that will obediently move after the first one. In Qt, a similar operation looks like this:
Example 1
As a result, we get the connection of the
valueChanged () signal of the
primary slider with the
setValue () slot of the
secondary slider . The same on Android:
')
Example 2
And as a result we get the same thing, that is, we associate the displacement of
firstBar and
secondBar .
Let's see what's going on here. Somewhere in the depths of
firstBar there is a variable of the
OnSeekBarChangeListener type which, when a move
occurs, is checked for
null and if it turns out to be non-zero, and so it happens, its
onProgressChanged () method will be called with the appropriate parameters which in turn will call
secondBar.setProgress (progress) and set the value of the second slider.
Everything is very clear and understandable, although somewhat cumbersome. Qt is more concise in this case, although to implement dynamic binding it goes beyond C ++ to generate code in the project building process using
MOC (Meta Object Compiler) . You have to pay for conciseness, and it becomes obvious when in the process of debugging you get into the generation code. But fortunately, if you follow the simplest rules, this is extremely rare.
But back to Android. All Android API classes have a sufficient set of liseners to provide convenience of their use, but what to do if there is a large array of code operating with signals and slots? Googling, I found several implementations of signals and slots in Java, the most worthy of which, not surprisingly, in the
Qt Jambi library, an unfairly forgotten implementation of Qt in Java. An excellent implementation, however, did not suit me for several reasons, the most weighty of which, the inconsistency of the syntax with the original, it is strange that the same technology as part of the libraries for C ++ and Java is implemented so differently.
As a result, the idea to implement the signals and slots for Android on Java independently emerged.
Task
Implement a Java signal and slot engine as close as possible to the Qt C ++ syntax using the Android API.
Implementation
What happened
After several attempts, the Java
Connector class was written on less than 600 lines with several static methods and a static map (Map) of signals and slots, which is essentially a singleton. All functionality is enclosed in four static methods:
- boolean connect (Object sender, String signal, Object receiver, String slot, ConnectionType type)
- void disconnect (Object sender, String signal, Object receiver, String slot)
- void emit (Object sender, String signalName, Object ... params)
- Object sender ()
- connect () allows you to connect a signal and a slot, a type in this context is an enumeration instance that can take the values of DirectConnection and QueuedConnection by analogy with Qt. DirectConnection , the default, also works as the lisener in Example 1. QueuedConnection uses the Handler from the Android API to implement an asynchronous slot call. Other types of connections remained unrealized, since The listed 2 covered 100% of cases occurring in a ported project.
- disconnect () operation is reverse to connect. Breaks the signal connection with the slot. It has options with 4, 3, 2, and 1m parameter. Which respectively break the signal connection with a specific slot, the signal connection with all receiver slots (receiver), the signal connection with all slots, and the sender connection (sender) with all slots.
- emit () allows you to send a signal from an arbitrary location in the program. The first parameter contains a link to the object (sender) that this signal sends, usually this . Declaring a signal as a method or variable, unlike other implementations, is not required in this case; the signal is simply an arbitrary string that must necessarily coincide with the string passed in the connect () method. Then a comma can be followed by an arbitrary number of parameters of arbitrary types, all of which, or at least the first of them, must coincide with the slot parameters.
- sender () is a very useful method, also an analog from Qt, called from the body of the slot and returns a pointer (in Java, an Object type reference) to the object that sent the signal.
In the runner example, the connection would look like this:
Example 3
At first glance it may seem that the implementation with the
Connector is more cumbersome than with the help of the logger, but do not forget that the
SeekBar class is sharpened for the use of the logger, like all other standard Android API classes, because you have to use wrappers. Much more benefits can be obtained by using a connector when developing your classes, or by porting Qt projects on Android:
- no need to create an interface for licenser
- no need to create a variable for it
- no setter method needed for licer
For more complex examples, details about the implementation details, see the next article.
(To be continued)