post03_kivy_and_android.skr 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. (post
  2. :title "State of data-binding (MVVM) in Kivy and Android"
  3. :date (make-date* 2016 9 23)
  4. :tags '("data" "data-binding" "kivy" "python" "android" "sdk" "mvvm")
  5. (h4 [TLDR: Data-binding support is an essential component in the MVVM
  6. framework. While android has recently adopted data-binding, Kivy has always long had excellent data-binding support])
  7. (h3 [MVVM and data-binding])
  8. (p [(Skip if familiar) Application architectures like MVVM
  9. (Model, View, View-Model) exist to aid in separating view logic from
  10. business logic. What makes
  11. MVVM different, is its focus on binding data between the view and the
  12. view-model. What this means, is that data in the view and in the
  13. view-model are tightly coupled, in that any registerable event in the
  14. view, is immediately recognized by the view-model. Additionally, the
  15. view-model has hooks into the model, so it can format and send any
  16. changed model data directly to the view.])
  17. (h3 [Android and MVVM])
  18. (p [Android was designed, unlike iOS, to be architecture independent.
  19. For many small applications, this is preferable, since setting up
  20. MVVM requires many more files and added complexity.
  21. However, as an application continues
  22. to grow, it's easy for Android code can get messy. In particular,
  23. Activities, which already deal with life-cycles, and broadcast
  24. listeners, also has to manage view events from the view.
  25. Interacting with the view in activities
  26. makes for ugly code too: after typecasting each widget we want to
  27. interact with
  28. after finding it by id, even simple things like setting text or
  29. responding to a click
  30. event are awfully verbose.])
  31. (code-block-scheme
  32. (car
  33. [public class ExampleActivity extends Activity {
  34. ...
  35. private EditText messageField;
  36. private Button submitBtn;
  37. protected void onCreate(Bundle icicle) {
  38. super.onCreate(icicle);
  39. setContentView(R.layout.content_layout_id);
  40. messageField = (EditText) findViewById(R.id.button_id);
  41. messageField.setText("Hi there!");
  42. submitBtn = (Button) findViewById(R.id.button_id);
  43. submitBtn.setOnClickListener(new View.OnClickListener() {
  44. public void onClick(View v) {
  45. // TODO: send message to server
  46. }
  47. });
  48. }
  49. ...
  50. }]))
  51. (p [Worse yet, since we are working directly with activities, nearly every
  52. test requires the full SDK,
  53. which implies we are stick with slow
  54. ,(anchor
  55. [emulation testing]
  56. "http://www.vogella.com/tutorials/AndroidTesting/article.html").])
  57. (p [Clearly there was room for improvement, and a number of packages were since
  58. developed to address these issues. DI frameworks like
  59. ,(anchor [ButterKnife ]
  60. "https://github.com/JakeWharton/butterknife")
  61. handle automatic view binding])
  62. (code-block-scheme
  63. (car
  64. [public class ExampleActivity extends Activity {
  65. ...
  66. @BindView(R.id.edit_id) EditText messageField;
  67. @OnClick(R.id.button_id) void sendMessage() {
  68. // TODO: send message to server
  69. }
  70. protected void onCreate(Bundle icicle) {
  71. super.onCreate(icicle);
  72. setContentView(R.layout.content_layout_id);
  73. ButterKnife.bind(this);
  74. // fields can now be used...
  75. messageField.setText("Hi there!");
  76. }
  77. ...
  78. }]))
  79. (p [and
  80. ,(anchor [RoboBindings]
  81. "https://github.com/RoboBinding/RoboBinding")
  82. allow view logic to be handled in a separate ViewModel class.])
  83. (p [Since DI is separate software concept, let's instead focus on RoboBindings.
  84. RoboBindings is a great library because
  85. it allows for strong separation of concerns.
  86. Now, all the view logic originally in the Activity is implemented in a
  87. separate ViewModel class, freeing the Activity to focus on its main
  88. goals: controlling lifecycles, and listening for app/system-wide broadcasts.
  89. And unlike before, the viewmodel and view directly share the same data,
  90. and agree on an API to handle events like button clicks. Overall, this makes
  91. for much cleaner view logic code. ])
  92. (code-block-xml
  93. (car
  94. [Our ExampleView XML:
  95. <LinearLayout
  96. xmlns:android="http://schemas.android.com/apk/res/android"
  97. xmlns:bind="http://robobinding.org/android">
  98. <EditText
  99. android:id="@+id/editText"
  100. android:hint="write a message..."
  101. bind:text="${inputMessage}"/>
  102. <Button
  103. android:text="send"
  104. bind:onClick="sendMessage"/>
  105. </LinearLayout>]))
  106. (code-block-scheme
  107. (car [Our ExampleViewModel Class:
  108. @PresentationModel
  109. public class ExampleVM {
  110. private String inputMessage;
  111. public void setInputMessage(String text) {
  112. this.inputMessage = text;
  113. }
  114. public String getInputMessage() {
  115. return this.inputMessage;
  116. }
  117. public void sendMessage() {
  118. // TODO: Send message to server
  119. }
  120. }]))
  121. (p [Now, event listeners are gone, since the
  122. view chooses how to delegate events to the view-model, and in the view-model
  123. we don't have to manually update any view widget since any data change
  124. in the view-model automatically updates the view!])
  125. (p [Yeah, data-binding is pretty damn genius. Just one last point: you may have
  126. noticed that view-models can be represented as standard
  127. ,(anchor [POJOs]
  128. "https://en.wikipedia.org/wiki/Plain_Old_Java_Object").
  129. This makes fast unit testing trivial!])
  130. (p [Robobinding is of course a 3rd party library, which is somewhat risky for a
  131. large app developer that needs reliability. Fortunately, Android
  132. has, since Marshmallow, provided its own
  133. ,(anchor [data-binding library]
  134. "https://developer.android.com/topic/libraries/data-binding/index.html")
  135. While I still personally use robobinding, Android's library looks solid,
  136. and follows a similar approach.])
  137. (h3 [Okay so what is/why Kivy?])
  138. (p [I have a feeling y'all are less likely to have heard of
  139. ,(anchor [Kivy]
  140. "https://kivy.org"). Essentially
  141. it's a well-established cross platform application framework written
  142. in Python. It was originally designed to run on large multi-touch
  143. screens, but has since been ported to run on Android and iOS. One of
  144. it's coolest features is
  145. ,(anchor [kv-lang]
  146. "https://kivy.org/docs/guide/lang.html"),
  147. an alternative to XML seen in Android,
  148. that makes it really easy to build a widget tree, with access to
  149. the full power of Python. In addition it has amazing data-binding
  150. support.])
  151. (p [Since we are familiar with what data-binding looks like in Android, let's
  152. get right to some code.])
  153. (ul
  154. (li [kivy implementation of above:
  155. ,(code-block-scheme
  156. (car [KV code:
  157. <ExampleWidget>:
  158. <TextInput>:
  159. text: root.input_message
  160. <Button>:
  161. on_click: root.sendMessage()]))
  162. ,(code-block-scheme
  163. (car
  164. [Python code:
  165. class ExampleWidget(BoxLayout):
  166. input_message = StringProperty()
  167. def send_message(self, btn_instance):
  168. send_msg_to_server(input_message)]))])
  169. (li [How to use python directly in kv:
  170. ,(code-block-scheme
  171. (car
  172. [#:import string
  173. #:import random
  174. <GenRandStringButton>:
  175. on_click: root.input_message =
  176. ''.join(random.SystemRandom()
  177. .choice(string.ascii_uppercase + string.digits) for _ in range(N))]))]))
  178. (p [While the above code does not implement MVVM directly (databinding
  179. is managed by a root widget, not a separate view-model class)
  180. it is clear how expressive and simple databinding under the kivy
  181. framework can be! I recommend checking out some of the kivy tutorials
  182. online for more interesting ways to use kv-lang!
  183. Shameless plug, but you can also check out
  184. ,(anchor [goblinoid]
  185. "https://notabug.org/sapientech/goblinoid")
  186. for more examples.]))