It has been three years since I started developing Android apps on my spare time.
My history as a developer dates back to when I was a little kid and started playing with MS-DOS and x86 assembly. Like everybody I always had to adapt to new technologies, learn new APIs, new tools and new programming languages.
Some transitions can be easier than others but in the end I have always been a desktop developer.
When approaching android programming I found myself a little lost at the beginning and after a while I was... even more lost!
The problem is mobile development works in a totally different way. That's why you can find the same questions asked a million times on different forums.
In this post I would like to give some hints about android programming for people coming from desktop programming. Code is not what matters here, it is the programming model.
1 There is no executable
In a desktop environment you usually end up with an installer package that unpacks and installs your application into a folder. Then you have one or more executables which are somehow linked to the OS UI (i.e. the start menu entry or a desktop link on Windows).
Android is a different beast. You have to provide an APK file which is more or less a zip file containing your application code+data. Inside this APK file there is one file called the manifest. The manifest declares what's inside, which permissions are required, which hardware features are requested by your app, etc. Obviously this is also used to check device compatibility with that specific APK. Every APK has to be signed (even the debug one that uses a debug key inside your android SDK folder) and contains a version number. When updating an APK the system checks the version number and verifies the new APK has been signed with the same key as the old one, for obvious security reasons.
2 An android application... is not an application
This is confusing to many people: what is usually referred to as an application is an activity.
In android an application class is a lower level class that can be overridden in order to perform specific tasks, but at least at the beginning you probably won't need it. An activity, on the other hand, is exactly what a desktop application is. It starts, it loads its contents, it can handle user input, etc.
3 One apk, one application, many activities
Yes, you can have many activities inside a single APK. A game activity could call another activity in its APK that takes care of the player settings. In order to keep your code modular and reusable you might want to have multiple activities.
4 One Activity, many Fragments
As android OS evolved, it was soon clear that the many activities model was a good idea. At the same time, for trivial tasks it was maybe a little too much to create a different activity. So starting with honeycomb (android 3.0) the fragments were introduced. Fragments are views (with code) inside an activity. You can easily create multiple fragments, no need to declare them in the manifest.
5 Intents, the "main activity" and a very good idea
In order to start an activity, you need to use a class called Intent. You inform the system of your intention to start an activity. You can pass parameters and the activity can be started. Even more: an activity can declare in the manifest which kind of intents it supports.
This leads us to two considerations.
The first is the main activity is the one that supports the intent linked to the "launcher". That means the intent the system generates when you tap on an app icon. That is your main activity.
The second consideration is you can actually call external activities if you use an intent which is supported. So if you are using a phone number intent
the phone will ask you which app should be opened (as long as you don't select a default app for that kind of intent).
This is actually a very good idea because it makes possible to use the same system (or custom) activity from many other different activities.
Also, every activity which is started from another activity is added to an activity stack. Pressing the back button or closing an activity in another way brings you back to the previous activity in the stack. Automatically.
6 Lifecycle and configuration changes
So now you have an idea of how things work in android. One APK with a manifest, one application class, one or more activities, at least one of those handling the launcher intent and others handling no intent or specific intents. Probably some activities having multiple fragments/views.
The most important thing to understand is the activity lifecycle. Here it is:
These are all callbacks to your activity class.
Inside the oncreate you usually load the UI.
When a configuration change happens, like switching from portrait to landscape, your activity is destroyed and recreated(!!!!).
You can save data into a class called the bundle which is passed to the oncreate in this case.
The principle behind this choice is you might want to load different layouts for portrait and landscape modes.
Is it annoying? YES.
When another event occurs (a phone call for example) the application is paused. It will be resumed after.
Well.. kinda of.
When an application is paused and the system claims more memory, an app can be destroyed.
Is it annoying? Again... YES. Expecially if that means you lose your OpenGL context and have to reload all textures, meshes, etc.
Also note that in android clicking the back button until you close the app means to pause and destroy the app, while clicking the home button to come back to the main screen means to pause the app.
The idea is an app that resides in memory and is paused will be back in foreground very fast.
7 Services, alarm manager, wake locks and broadcast receivers
In a desktop environment you think about a service as something that runs in background or something that provides a set of functions you can call.
In android a service is a class that does.... almost nothing. If you need a thread inside a service you have to create it by yourself.
Everybody asks about reading positions or contacting a server once every while. Their idea is they create a service, run the service and then everything will work, even when the phone is in sleep mode.
Sorry, it doesn't work like this. If you could do something like that you would kill the battery in no time.
The solution is to schedule an alarm via the alarmmanager so that an event is generated once every X seconds (better minutes), then intercept the event, acquire a wake lock, perform the task and release the wake lock.
When you don't touch your phone, it soon turns the screen off. After a while it goes into sleep mode to save battery. It wakes up when an event occurs (like the alarm of the alarmmanager). At that point in order to prevent the phone to go back to sleep you acquire a wake lock, keep the phone "alive" and then inform it can go back to sleep.
When an alarm is fired, it broadcasts an intent you specify.
You can define a broadcastreceiver class that handles that intent. In general this will start a service that will start a thread that will perform the operations and then will stop.
It is complicated. It makes sense but the first time this can be confusing.
8 Passing data between activities
You can set extra parameters to the intent when calling an activity. The problem is it works only if this data is trivial.
This is one of the most asked questions: which is the correct way to pass a lot of data from an activity to another?
Well, there is no clear answer. As explained before an activity should be self-contained and reusable. On one hand it should not rely upon complex data to be passed. On the other hand if you have a specific activity for your specific application you might want to pass complex data without worrying about allowing other apps to use your activity.
As sad as it sounds the solutions are the following:
- use an SQLite database
- use the app preferences
- use the application class
- use a SINGLETON (not kidding, actually suggested by people working at Google!!!!!!)
9 UI and Code flow
Forget about your main. There is no obvious code flow except for the callbacks described in the activity lifecycle.
The activity starts, loads the UI, displays the UI. That's it.
In order to do something useful everything has to use callbacks.
The programming model is more or less the following:
- set the xml file representing the UI
- get an object from a resource view ID that represent an item in your UI
- add a listener to that object so that when user interacts something happens
For game programming there is an exception: the OpenGL renderer is called continously. Obviously if you want to seperate the game logic from rendering you will have to create a new thread and handle rendering data synch between the two threads.
As for the UI, everything has to be done in an XML file. For an OpenGL game is quite easy as you will have a GLsurfaceview covering the entire screen. For normal activities this means to place each element relative to the other or to declare layouts that are linear or relative or scrolling. Due to the nature of android and the wide range of resolutions and devices that is the only way to create a consistent UI. Even if you have to consider many apps use many layouts (not only for landscape/portrait, also for different screen densities and resolutions).
In OpenGL the problem with the UI is you have to take care of the different aspect ratios.
Back in the days of android gingerbread (2.3) 1.2ghz dual core CPUs were the coolest guys in town. Because of what I explained before it was considered "normal" to just sit there, wait for the user to press a button and then perform the operations we needed.
The programming model is tempting from this point of view. The problem is people started performing network operations inside the callbacks. Remember it is not a desktop PC, it is a mobile device. You might have poor connectivity. What happened was that many apps looked so slow and crappy that it started affecting user experience.
Try to explain a user his brand new octa-core 2.5 GHz smartphone is totally stuck when you press the refresh button. He will blame the OS, maybe switching to the competitor next time he has to buy a device.
That's why starting from ice cream sandwich Google decided no network operation can be performed in the UI thread. So take into account every network operation has to be done on a new thread.
To sum up: an average application has the UI thread (the UI callbacks) a main thread (the activity thread) and probably another short lived thread (or asynctask or another kind or threading mechanism) for networking. In case of a good game you also have the game logic thread sitting aside the rendering thread.
If you come from desktop programming consider Android programming is ALWAYS multithreaded.
Not so shocking to see octacores around.
11 External memory, SD cards and security
You want to save data. Maybe a lot of data. Android supports SD cards. Just write garbage there and you are fine.
Release a small game, then download 1gb of data from an external website and place all that stuff somewhere in the SD card.
There are two problems with that.
The first problem is you have access only to internal memory and external memory in android. You might think internal memory = the device internal memory and external memory = SD card.
Obviously it doesn't work like this. Internal memory is a private folder for your application stored in internal memory. External memory is the rest of your internal memory.
What about the SD?
SD is mounted in a folder in internal memory and every manufacturer can change its name. Even from model to model or from OS version to OS version.
If this is not enough, let's speak about the second problem.
If you could find the SD folder, you could write into it until kitkat (4.4). Now every phone with kitkat or Lollipop does not allow apps without root privileges (and your game should never have or require these privileges) to write to the SD card.
You might ask if people were complaining for this change and if many apps stopped working.
Well, both answers are yes. People complained and many apps stopped working or suffered from reduced functionality (file managers).
Why is Google enforcing this?
There are two good reasons.
The first is android has no way to tell what you have been writing around. If everybody started writing on the SD (and many developers did) once the user uninstalled the app then you would still have garbage around. Also, relying upon data stored into a removable media for your app in internal memory to work is not a good idea, in general.
You can still write in internal memory, but writing into your private folder is the way to go. This way a user uninstalling will effectively have a clean phone.
The second reason has a lot to do with security. SD cards have FAT32 filesystems. There is no encryption. To allow apps to write there means somebody having your removable media in his hands might easily steal precious information.
I hope you will find these hints useful. Mobile programming can be confusing at the beginning, expecially for game developers used to work on desktops, where 500W beasts and huge screens are the norm. To adapt to this new environment requires a different mentality.