Android. What's not to like about this platform? It's free, it's
customizable, it's
rapidly growing and it's available not just
on your phone or tablet, but on your smartwatch, TV and car too.
With the latest
Lollipop update, Android programming
continues to improve. The platform has matured quite a bit since the
initial
AOSP release, and set the user expectations
bar quite high. Look how good the new
Material design pattern looks!
There are
thousands of different devices, with
different screen sizes, chip architectures, hardware configurations,
and software versions. Unfortunately, segmentation is the price to pay
for openness, and there are thousands of ways your app can fail on
different devices, even as an
advanced
Android programmer.
Regardless of such huge segmentation, the majority of bugs are
actually introduced because of logic errors. These bugs are easily
prevented, as long as we get the basics right!
Here is an Android programming tutorial to address the 10 most common
mistakes Android developers make.
Common Mistake #1
Developing for iOS
To my great pleasure, this Android mistake is far less common nowadays
(partially because clients are beginning to realize that the days when
Apple was setting all the design standards are long gone). But still,
every now and then, we see an app that is an iOS clone.
Don't get me wrong, I am not an Android programming evangelist! I respect
every platform that moves the mobile world a step forward. But, its
2014 and users have been using Android for quite a while now, and
they've grown accustomed to the platform. Pushing iOS design standards
to them is a terrible strategy!
Unless there is a super good reason for breaking the
guidelines, don't do it. (Google does this all
the time, but never by copy-pasting.)
Here are some of the most common examples of this Android mistake:
You should not be making static tabs, and they don't belong on the bottom
(I'm pointing at you Instagram ).
System notification icons should not have color.
App icons should not be placed inside a rounded rectangle (unless thats
your actual logo ex. facebook).
Splash screens are redundant beyond the initial setup/introduction. Do not
use them in other scenarios.
Lists should not have carets.
These are just a few of the
many other small things that can ruin the
user experience.
Common Mistake #2: Developing for Your Android Device
Unless you are building a kiosk/promo app for a single tablet, chances
are your Android app wont look good on every device. Here are a few
Android programming tips to remember:
Density-independent pixels (dp) are
different than normal pixels (px).
Resources are included multiple times to
account for different densities and orientations.
9-patch drawables are stretched to fit the
screen.
There are literally thousands of possible scenarios, but after a while
you develop a sense for covering them all with a
handful of cases.
You don't own thousands of devices? Not a problem. The Android Emulator
is super good in replicating physical devices. Even better, try
out
Genymotion, its lightning fast and comes with a
lot of different popular preset devices.
Also, have you tried rotating your device? All hell can break loose
Common Mistake #3: Not Using Intents
Intents are one of Androids key components.
Its a way of passing data between different parts of the app or, even
better, different apps on the system.
Lets say you have a gallery app that can share a download link to some
images via SMS. Which of the two options seems more logical?
Option 1:
Request the SEND_SMS permission.
<uses-permissionandroid:name="android.permission.SEND_SMS" />
Write
your own code for sending SMS using the
SmsManager
.
Explain
to your users why your gallery app needs access to services that can
cost money, and why they have to grant this permission to use your
app.
Option 2:
Start an SMS Intent and let an app designed for SMS do the work
Intent sendIntent = new Intent(Intent.ACTION_VIEW)
sendIntent.setData(Uri.parse("sms:" + telephoneNumber))
sendIntent.putExtra("sms_body", x)
startActivity(sendIntent)
In
case that you have any doubts, best solution is option 2!
This approach can be applied to almost anything. Sharing content,
taking pictures, recording video, picking contacts, adding events,
opening links with native apps, etc.
Unless there is a good reason to
make a custom implementation (ex., a camera that applies filters),
always use Intents for these scenarios. It will save you a lot of
programming time, and strip the
AndroidManifest.xml
of unnecessary permissions.
Common Mistake #4: Not Using Fragments
A while ago in Honeycomb, Android introduced the concept of fragments.
Think of them as separate building blocks with their own (rather
complex) life cycles that exist inside an Activity. They help a lot
with optimizing for various screens, they are easily managed by their
parent activity, can be reused, combined and positioned at will.
Launching a separate activity for each app screen is terribly
inefficient, since the system will try to keep them in memory as long
as it can. Killing one won't free the resources used by the others.
Unless you want to dig deep into the Android core and read
this article, advocating against fragment usage,
you should use fragments whenever possible. It basically says that
fragments and
cursor loaders have good intended purpose,
but poor implementation.
Common Mistake #5: Blocking the Main Thread
The main thread has a single purpose: keeping the user interface
responsive.
Although the science behind measuring the frame rate our eyes/brain
can perceive is complex and influenced by a lot of factors, a general
rule is that anything below 24 fps with delay greater than 100 ms wont
be perceived as smooth.
This means that the users actions will have a delayed feedback, and
the Android app you have programmed will stop responding. Stripping
the user of his control over the app leads to frustration, frustrated
users tend to give very negative feedback.
Even worse, if the main thread is blocked for a while (5 seconds for
Activities, 10 for Broadcast Receivers),
ANR will happen.
This was so common in Android 2.x, that on newer versions the system
wont let you make
network calls in the main thread.
To avoid blocking the main thread, always use worker/background
threads for: 1. network calls 2. bitmap loading 3. image processing 4.
database querying 5. SD reading / writing
Common Mistake #6: Reinventing the Wheel
OK, I won't use the main thread. I'll write my own code that
communicates with my server in a background thread.
No! Please dont do that! Network calls, image loading, database
access, JSON parsing, and social login are the most common things you
do in your app. Not just yours, every app out there. There is a better
way. Remember how Android has matured and grown as a platform? Heres a
quick list of examples:
Use gradle as a build system.
Use Retrofit / Volley for network calls.
Use Picasso for image loading.
Use Gson/Jackson for JSON parsing.
Use common implementations for social login.
If you need something implemented, chances are its already written,
tested and used widely. Do some basic research and read some
Android
programming tutorials before writing your own code!
Common Mistake #7: Not Assuming Success
Great. We have learned that there is a better way for handling long
running tasks, and we are using well documented libraries for that
purpose. But the user will still have to wait. It's inevitable.
Packages are not sent, processed and received instantly. There is a
round trip delay, there are network failures, packages get lost, and
dreams get destroyed.
But all this is measurable. Successful network calls are
far more likely than unsuccessful ones. So
why wait for server response before handling the successful request?
It's infinitely better to assume success and handle failure. So, when a
user likes a post the like count is immediately increased, and in
unlikely event that the call failed, the user is notified.
In this modern world immediate feedback is expected. People don't like
to wait. Kids don't want to sit in a classroom obtaining knowledge that
has uncertain future payoff. Apps must accommodate to the users
psychology.
Common Mistake #8: Not Understanding Bitmaps
Users love content! Especially when the content is well formatted and
looks nice. Images, for instance, are extremely nice content, mainly
due to their property of conveying a thousand words per image. They
also consume a lot of memory.A
lot of memory!
Before an image is displayed on the screen, it has to be loaded into
the memory. Since bitmaps are the most common way to do this, were
going to provide an Android programming guide for the whole process:
Lets say you want to display an
image on your screen that you just took with your camera. The total
memory needed for this is calculated with the following formula:
memory_needed_in_bytes = 4 *
image_width * image_height;
Why 4? Well, the most common /
recommended bitmap configuration is
ARGB_8888
. That means that for each pixel
we draw, we need to keep 8 bits (1 byte) for the alpha, the red, the
greed and the blue channel in memory, in order to properly display
it. There are alternatives, like the
RGB_565
configuration that requires
half the memory than
ARGB_8888
, but loses the transparency and
the color precision (while maybe adding a green tint).
Lets assume you have a brand new
device with full HD screen and 12 MP camera. The picture you just
took is 4000x3000 pixels large and the total memory needed to display
it is:
4 bytes * 4000 * 3000 = 48 MB
48 megabytes of your RAM just for a single image!? Thats a lot!
Now lets take the screen
resolution into consideration. You are trying to show a 4000x3000
image on a screen that has 1920x1080 pixels, in worst case scenario
(displaying the image full screen) you shouldnt allocate more
than
4 * 1920 * 1080 = 8.3 MB
of memory.
Measure the view you're showing your images in.
Scale
/ crop the large image accordingly.
Show
only what can be displayed.
Common Mistake #9: Using Deep View Hierarchy
Layouts have an XML presentation in Android. In order to draw content,
the XML needs to be parsed, the screen needs to be measured, and all
the elements need to be placed accordingly. Its a resource- and
time-consuming process that needs to be optimized.
If
a layout has been inflated once, the system reuses it. But still,
inflating the layout must happen at some point.
Lets say you want to make a 3x3
grid with images. One way of doing this is a vertical
LinearLayout
containing 3
LinearLayout
s with equal weight, each of them
containing 3
ImageViews
with equal weight.
What do we get with this approach? A warning that nested weights are
bad for performance.
There is a saying in the Android programming world, that I just made
up:With
little effort all hierarchy can be flattened.
In this case
RelativeLayout
or
GridLayout
will efficiently replace
the nested
LinearLayouts
.
Common Mistake #10: Not Setting the minSdkVersion to 14
Well, this is not a mistake, but it is bad practice.
Android 2.x was a huge milestone in developing this platform, but some
things should be left behind. Supporting older devices adds more
complexity for code maintenance and limits the development process.
The numbers are clear, the users have moved
on, the developers shouldnt stay behind.
I'm aware that this doesn't apply
for some big markets with old devices, and setting
the
minSdkVersion
to 14, on the Facebook App,
means leaving couple of million users without their favorite social
network. But, if you are starting fresh and trying to create a
beautiful experience for your users, do consider eliminating the
past. Users that don't have the resources, or feel the need to upgrade
their device/OS, won't have the incentive to try out a superior
version of your Android app and ultimately spend money on it.
Wrap Up
Android is a powerful platform that evolves quickly. It may not be
reasonable to expect users to keep up the pace, but it's crucial for
the Android developers to do so.
Knowing that Android is not just on our phones or tablets is even more
important. It's on our wrists, in our living rooms, in our kitchens,
and in our automobiles. Getting the basics right is of utmost
importance before we start expanding.