A Web Developer Builds a Kivy App

Updated with a comment by a Kivy core dev at the bottom

I'm a web developer and I eat, sleep, and breathe servers and clients. One day I found out that I could run my lovely Python code on Android using Kivy. I decided to write an application to allow an Android device to send and receive binary data on a local network. Something like netcat, but for Android, imaginatively called "AndCat" - currently available from a github repo. I'm eager to share this experience with the Python community, because it turned out much better than I expected.

Kivy, to me, represents the variety present in the Python community. We have everything from web frameworks, to scientific, information-security, and system administration tools. Not many programming language communities can boast such a multidisciplinary streak.

What is Kivy?

Kivy is a "cross-platform Python framework for NUI development". NUI stands for "natural user interface" and it means that interface exposed to the user does not get in the way nor does it require learning. In simpler terms, Kivy is a framework for developing graphical interfaces that work well on the following platforms: Android, iOS, Windows, Linux, and OSX. This means that we, the Python community, on top of creating web applications or machine learning models, can also now create desktop or mobile applications.

Where do I start?

I have zero knowledge about developing anything for mobile platforms. I decided to get Roberto Ulloa's book "Kivy - Interactive Applications and Games in Python" to not only understand the Kivy framework, but to get a glimpse of how to work on a wholly alien platform.

Roberto Ulloa does a great job because the book consists of 3-4 projects to get your hands dirty and actually build something. It is through these examples that Roberto teaches us on how to think about graphical applications - how to reason about placing graphical objects in certain places, how to move between these "places", how to tie events, which happen to these objects, with running "back-end" code, and much more.

Every developer needs help, especially when starting out. The Kivy documentation is detailed and well structured; it includes many examples and tackles related topics such as building your application for Android or iOS or integrating Twisted into it. Despite that it's not overwhelming and you get the answer you're looking for quickly. But what if the docs can't answer your question?

The Kivy community is active and produces a lot of reading material. There are many blogs (like this one, or this one) that deal with more arcane situations that come up and then there's the mailing list that gathers those really unique problems - the kind of problems that maybe five people ever bump into and there are only two other developers out there who know the answer. I can't judge the IRC channel because I haven't used it; however, the static sources always recommend it. This may also mean that I haven't found a problem that I couldn't solve by myself or with the help of the documentation and mailing list. It may also mean that my project was too simple ;).

The Experience

As I mentioned before, I decided to make an Android version of netcat. I found that there are no simple tools to transfer larger files to and from an Android device. There are SSH applications, which offer SCP support, as well as FTP servers, but all of those solutions felt overly complicated. I also wanted to be able to easily transfer files from my Linux computer and is there any easier way to do that than netcat?

AndCat is a natural extension of the knowledge presented in the book I read. I am reusing the ideas and guidelines that I picked up from the practice projects. I also decided to cut the scope as much as possible and focus on functionality. Because of this I am using all-default styling and I am assembling the interface manually instead of relying on an industrial-strength tool like Kivy Designer.

The biggest difference between developing for the web and developing for mobile has been the new runtime environment. Browsers are finicky beasts full of surprises and every developer learns how to deal with them. We, as web developers, learn how to solve problems that appear between the user and the browser, inside of the browser, and between the browser and the server.

Mobile is completely different. There is no browser - just a very general interface that gives you a number of inputs: touch screen, camera, sensors, etc.; and a few outputs: screen, speaker, vibrator. Web developers can only dream of some of these. This feels complicated at first, but compared to web development, there is no middleman in the form a browser. That's one less moving part to keep in the back of your mind.

Kivy does a whole lot of heavy lifting for you. I've chosen to extend Kivy's rich collection of building blocks as a way of working with it. These blocks include everything from layout grids to buttons to file selectors. They are plugged into the application event loop so getting an element to respond to an event is as simple as defining a properly named function on one of these elements:

class CustomTextInput(TextInput): def on_focus(self, instance, value): # define what happens when a 'focus' event is fired.

Kivy comes with its own "Kv" language; it is similar to YAML and it provides a way to declare how an element should look and behave. It also enables the developer to nest elements - this means it's possible to use Kv to declare a whole layout with buttons, images, and whatnot! I used Kv to describe how the file-receiving-screen will look like for the user: I define that the screen will contain a grid layout and that this layout will contain elements such as "AndCatLogo", "AndCatIPLabel", "AndCatPortInput", and a few others. Each of these elements has some additional attributes such as its size or, in the case of the Label, its text. Check it out:

#:kivy 1.9.0 <RecvFile@GridLayout>: cols: 1 row: 8 AndCatLogo: AndCatIPLabel: text: 'Current IP: {0}'.format(root.parent.manager.own_ip) text_size: self.width, None size_hint: 1, 0.1 AndCatPortInput: id: _recv_port multiline: False text: 'Port (value between 1024 and 65535)' size_hint: 1, 0.1 # snip

Kivy is just plain Python. Thanks to this, the learning curve is very shallow and it's possible to build a decent application in a matter of hours - it's all normal Python objects, loops, functions - nothing exotic. This has the added benefit that if you can't find something in the documentation, you can rummage through the Kivy source code, which is pythonic and easy to understand.

One issue that I'm on the fence about is testing. I'm used to building up a suite of tests, but I have no clue on how to test my Kivy-based application. I have not found a satisfying way to isolate the business logic I want to test from the workings of the framework. Furthermore, I can't imagine how I can test the GUI itself. I'm using Twisted to handle the networking, which is easy to test with its own integrated testing framework; however, due to the way that the Twisted-based portions of code integrate with the Kivy-based portions, I have not figured out how to properly test any of them. I've found Kivy documentation on this topic, but due to my lack of any graphics knowledge, I haven't been able to make use of it. Maybe a more gentle introduction would help newbies? Or did I miss something?

One final gotcha is the long start up time. The first time around I launch AndCat on a 1st gen Moto X, that start up takes about 30 seconds. Subsequent start ups only take 5-8 seconds. I've read that this may be related to running a lot of code in the initialization file (aka "main.py") and it is certainly related to firing up the Python interpreter on the Android device itself. I can't find the source now, but I also read that this is being worked on and will change in the near future.

In summary:

Kivy gives developers the ability to create great applications.

Picking up Kivy is simple thanks to its pythonic nature.

There's a lot learning and support materials thanks to the active community.

Some minor gotchas that are smoothed over with more experience.

I'm definitely putting Kivy in my toolbox and I'm looking forward to using it on some other projects after I release AndCat in the next few days.

Inclement commented on my reddit thread with some very valuable info about testing and start up speed, which I really appreciate:

Nice app, and I'm glad you had a good experience with Kivy.

One issue that I'm on the fence about is testing. I'm used to building up a suite of tests, but I have no clue on how to test my Kivy-based application.

There's been some discussion about this (we'd love to have a good solution for it, including so that Kivy can have more extensive gui tests of its own), but I'm not sure if any conclusion was reached beyond the current relatively basic ones. If you're interested, it might be worth querying on the mailing list or irc, in case someone does have something good working.

One final gotcha is the long start up time. The first time around I launch AndCat on a 1st gen Moto X, that start up takes about 30 seconds. Subsequent start ups only take 5-8 seconds.

I've never properly benchmarked this, but I suspect the long times here depend strongly on slow internal memory, particularly the 25s for extraction. As long as your app isn't unnecessarily big (which it shouldn't be), there probably isn't much we can do here.

For the 5-8s startup on later times ( included in the 30s the first time), this is where pre-gui-start python code can make a difference, but these timings are comparable to or maybe a little worse than my nexus 4, which was just slow like that. If the cause is the same then you probably can't improve it much by modifying your app code, but it's likely to be significantly better on newer devices (on the lg g4, startup is only 1-2s for me).

One thing you can try is to build your app with SDL2 instead of pygame as the backend. On the nexus 4, I observed a noticeable 30-40% improvement in startup time after the first run (about 3s down from 5s for a trivial app), but on newer devices the difference doesn't seem significant, possibly because it's so much faster anyway. I don't know what's really the bottleneck, but it's possible it could help you too. Using SDL2 is only possible with the new python-for-android toolchain, and doesn't yet have a buildozer backend, though one of the other developers has been working on one.

This work is licensed under a Creative Commons Attribution 4.0 International License.