123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- (post
- :title "State of data-binding (MVVM) in Kivy and Android"
- :date (make-date* 2016 9 23)
- :tags '("data" "data-binding" "kivy" "python" "android" "sdk" "mvvm")
- (h4 [TLDR: Data-binding support is an essential component in the MVVM
- framework. While android has recently adopted data-binding, Kivy has always long had excellent data-binding support])
- (h3 [MVVM and data-binding])
- (p [(Skip if familiar) Application architectures like MVVM
- (Model, View, View-Model) exist to aid in separating view logic from
- business logic. What makes
- MVVM different, is its focus on binding data between the view and the
- view-model. What this means, is that data in the view and in the
- view-model are tightly coupled, in that any registerable event in the
- view, is immediately recognized by the view-model. Additionally, the
- view-model has hooks into the model, so it can format and send any
- changed model data directly to the view.])
- (h3 [Android and MVVM])
- (p [Android was designed, unlike iOS, to be architecture independent.
- For many small applications, this is preferable, since setting up
- MVVM requires many more files and added complexity.
- However, as an application continues
- to grow, it's easy for Android code can get messy. In particular,
- Activities, which already deal with life-cycles, and broadcast
- listeners, also has to manage view events from the view.
- Interacting with the view in activities
- makes for ugly code too: after typecasting each widget we want to
- interact with
- after finding it by id, even simple things like setting text or
- responding to a click
- event are awfully verbose.])
- (code-block-scheme
- (car
- [public class ExampleActivity extends Activity {
- ...
- private EditText messageField;
- private Button submitBtn;
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.content_layout_id);
- messageField = (EditText) findViewById(R.id.button_id);
- messageField.setText("Hi there!");
- submitBtn = (Button) findViewById(R.id.button_id);
- submitBtn.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- // TODO: send message to server
- }
- });
- }
- ...
- }]))
- (p [Worse yet, since we are working directly with activities, nearly every
- test requires the full SDK,
- which implies we are stick with slow
- ,(anchor
- [emulation testing]
- "http://www.vogella.com/tutorials/AndroidTesting/article.html").])
- (p [Clearly there was room for improvement, and a number of packages were since
- developed to address these issues. DI frameworks like
- ,(anchor [ButterKnife ]
- "https://github.com/JakeWharton/butterknife")
- handle automatic view binding])
- (code-block-scheme
- (car
- [public class ExampleActivity extends Activity {
- ...
- @BindView(R.id.edit_id) EditText messageField;
- @OnClick(R.id.button_id) void sendMessage() {
- // TODO: send message to server
- }
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.content_layout_id);
- ButterKnife.bind(this);
- // fields can now be used...
- messageField.setText("Hi there!");
- }
- ...
- }]))
- (p [and
- ,(anchor [RoboBindings]
- "https://github.com/RoboBinding/RoboBinding")
- allow view logic to be handled in a separate ViewModel class.])
- (p [Since DI is separate software concept, let's instead focus on RoboBindings.
- RoboBindings is a great library because
- it allows for strong separation of concerns.
- Now, all the view logic originally in the Activity is implemented in a
- separate ViewModel class, freeing the Activity to focus on its main
- goals: controlling lifecycles, and listening for app/system-wide broadcasts.
- And unlike before, the viewmodel and view directly share the same data,
- and agree on an API to handle events like button clicks. Overall, this makes
- for much cleaner view logic code. ])
- (code-block-xml
- (car
- [Our ExampleView XML:
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:bind="http://robobinding.org/android">
- <EditText
- android:id="@+id/editText"
- android:hint="write a message..."
- bind:text="${inputMessage}"/>
- <Button
- android:text="send"
- bind:onClick="sendMessage"/>
- </LinearLayout>]))
- (code-block-scheme
- (car [Our ExampleViewModel Class:
- @PresentationModel
- public class ExampleVM {
- private String inputMessage;
-
- public void setInputMessage(String text) {
- this.inputMessage = text;
- }
-
- public String getInputMessage() {
- return this.inputMessage;
- }
- public void sendMessage() {
- // TODO: Send message to server
- }
- }]))
- (p [Now, event listeners are gone, since the
- view chooses how to delegate events to the view-model, and in the view-model
- we don't have to manually update any view widget since any data change
- in the view-model automatically updates the view!])
- (p [Yeah, data-binding is pretty damn genius. Just one last point: you may have
- noticed that view-models can be represented as standard
- ,(anchor [POJOs]
- "https://en.wikipedia.org/wiki/Plain_Old_Java_Object").
- This makes fast unit testing trivial!])
- (p [Robobinding is of course a 3rd party library, which is somewhat risky for a
- large app developer that needs reliability. Fortunately, Android
- has, since Marshmallow, provided its own
- ,(anchor [data-binding library]
- "https://developer.android.com/topic/libraries/data-binding/index.html")
- While I still personally use robobinding, Android's library looks solid,
- and follows a similar approach.])
-
- (h3 [Okay so what is/why Kivy?])
- (p [I have a feeling y'all are less likely to have heard of
- ,(anchor [Kivy]
- "https://kivy.org"). Essentially
- it's a well-established cross platform application framework written
- in Python. It was originally designed to run on large multi-touch
- screens, but has since been ported to run on Android and iOS. One of
- it's coolest features is
- ,(anchor [kv-lang]
- "https://kivy.org/docs/guide/lang.html"),
- an alternative to XML seen in Android,
- that makes it really easy to build a widget tree, with access to
- the full power of Python. In addition it has amazing data-binding
- support.])
- (p [Since we are familiar with what data-binding looks like in Android, let's
- get right to some code.])
- (ul
- (li [kivy implementation of above:
- ,(code-block-scheme
- (car [KV code:
- <ExampleWidget>:
- <TextInput>:
- text: root.input_message
- <Button>:
- on_click: root.sendMessage()]))
- ,(code-block-scheme
- (car
- [Python code:
- class ExampleWidget(BoxLayout):
- input_message = StringProperty()
- def send_message(self, btn_instance):
- send_msg_to_server(input_message)]))])
- (li [How to use python directly in kv:
- ,(code-block-scheme
- (car
- [#:import string
- #:import random
- <GenRandStringButton>:
- on_click: root.input_message =
- ''.join(random.SystemRandom()
- .choice(string.ascii_uppercase + string.digits) for _ in range(N))]))]))
- (p [While the above code does not implement MVVM directly (databinding
- is managed by a root widget, not a separate view-model class)
- it is clear how expressive and simple databinding under the kivy
- framework can be! I recommend checking out some of the kivy tutorials
- online for more interesting ways to use kv-lang!
- Shameless plug, but you can also check out
- ,(anchor [goblinoid]
- "https://notabug.org/sapientech/goblinoid")
- for more examples.]))
|