About the features of the architecture of Android through the eyes of a non-Android developer

    Recently we completely reworked the Pyrus app for Android. The first version of the application worked even under Android 2.2. Having abandoned Android support below 4.1, we were able to pay off the accumulated technical debt and noticeably simplified the source code. Yes, we lost some users (less than 1%), but then we saved the developers time to fix rare bugs. We will be able to invest it in the development of functionality for all current and new users. In the long run, this is much more important.

    Here we share experiences that can be useful to those who are thinking of starting development for the Android platform.

    Android attracts good development tools, time-tested language (Java) with the usual syntax, a huge audience of users. However, it is difficult to create convenient applications on Android: in spite of the advanced API, it often happens that there is no ready-made component with suitable behavior. You have to either give up the ideas of the UX-designer, or agree on dependence on third-party libraries, or write yourself. Not all architectural solutions in Android have been successful and this also increases the amount of code without obvious benefit.

    Dying and rising from the ashes Activity

    The interface in Android is built on Activity. This is a container that usually occupies the entire screen of the device, other widgets live in it and it receives user interaction events.

    When the user activates the application, the onResume event comes to the Activity. I pressed the Home button - the Activity disappeared, but before that the onPause event came to him. So far, everything is logical. What happens when the user rotates the device screen 90 degrees? You will never guess: OS kills Activity and creates anew! When you delete an Activity, all components that it contains are deleted. This means that you have to write a special code that saves the state of the widgets inside the Activity (the position of the scroll, a selected piece of text, the state of the checkboxes) and restores them after recreating the parent. Android does some of this work for you, but not everything.

    It is difficult to understand what encouraged the architects to make such a decision. Later, someone decided that it looked somehow ugly, and added a parameter that suppresses the default behavior of the system. Now, instead of re-creating the Activity, the OS can call the special onConfigurationChanged method. This “solution” is like a dirty hack and only exacerbated the problem, because the Android documentation says “this technique should be considered as a last resort and is not recommended for most applications”.

    Fragmented Fragments

    Initially, the Android OS was created for phones. With the advent of tablets, the Android team rightly decided that those interface elements that were previously separated due to the small screen sizes of the phone across different Activities can now simultaneously coexist on the big screen of the tablet. (Example: a list of letters and an email on the phone were in two different Activities, and on the tablet they share one screen together.) But the trouble is: several Activities cannot interact with the user by design at the same time. Therefore, we need another mechanism for reuse of components, and the solution was found: fragments appeared (Fragment).

    According to the definition, Fragment represents the behavior or part of the user interface in the Activity. All clear. However, in the instructions for using fragments we find the section “Adding a fragment that does not have a user interface”. WHAT? It turns out that a fragment may not have its own window for drawing in principle!

    And what happens to the fragments when the screen rotates? Android developers have thought about this - when recreating an Activity, all its fragments are also automatically recreated. However, on closer examination, a fragment has a “setRetainInstance” method, if you call it, this particular fragment will not be deleted and restored during turns.

    Slender and consistent this concept can not be called.

    Asynchronous operation

    Event processing flow cannot be blocked by long operations, therefore data exchange with the disk and the network must be done asynchronously in other threads. Android provides as many as four mechanisms for asynchronous work: AsyncTask, Service, IntentService, Thread. Developers are invited to find out for themselves what they should use. The choice is non-trivial: for example, if the application launched AsyncTask, and the user turned the screen while executing, then at the end of work AsyncTask cannot find a new (created after the rotation) Activity. And none of the four mechanisms implements simple logic: if the user started the second asynchronous process without waiting for the results of the first, and then the first still ended earlier, then it is appropriate to ignore and not reflect its results in the UI.

    The emergence of numerous libraries - data buses for the organization of asynchronous work inside an Android application is becoming clear.

    Restart application after OOM

    If Android runs out of RAM, the OS can complete any process. When the user activates the application in the future, the system will try to restore its state. In theory, this should be invisible to the developer (if he implemented the onSaveInstanceState method in all components): the system itself re-creates the Activity stack as it was during the forced stop. However, if initialization takes time (for example, loading caches from disk or network), then it will be correct to show the user a wait indicator. So, when creating an Activity, you still need to keep track of the “cold start” and initialize it manually. Not to mention the fact that when you restart, no Thread and AsyncTask are restored.

    How likely is it to face memory shortages in practice? Usually this situation occurs when processing high-resolution images. For example, the picture 2048x1536 takes 12Mb in the memory of the Bitmap object. The amount of memory available to an application depends on the specific device model and is sometimes quite small (64Mb or 128Mb). Since the garbage collector in all versions of Android before 8.0 was not compressing (compacting), even if you have 100Mb of free memory, but it is divided into blocks of 10Mb, then an attempt to allocate memory for such a picture caused the application to crash.

    Eternal garbage collection

    Once our user noticed that when scrolling through long lists, the application “lagged” - every 2-3 seconds the animation stopped for short pauses (200-300 ms), and then continued. Analysis has shown that garbage collection is run suspiciously in an application. It turned out that the standard HashMap class (which we use to get a Java object by its ID) is ineffective in our case: you need to create a wrapper object for each key that is an integer (int). Therefore, the number of memory allocations increases without any benefit. The solution was to switch to a special container SparseArray (available only in Android, not in the standard Java platform), which immediately reduced the pressure on the garbage collector.

    What have lag in animation, you ask? The fact is that the garbage collector in Android stopped all threads during its work, including the main thread that deals with rendering. In versions of Android starting with 5.0, a different virtual machine is used (ART instead of Dalvik) and a different garbage collection algorithm, which makes the animation pause less and shorter. (You can read about comparing garbage collection time here .)

    View documents

    If you want to insert a document preview into your application, you have to disappoint: Android does not have a built-in component that can display Word, Excel, Powerpoint files. Not to mention the ZIP archives or PDF documents. Output? To force the user to install third-party applications to view each type of file, or use the WebView component (actually a browser). In both cases, when a user clicks on a file, an external application will be launched and some scripts simply cannot be implemented, for example, an image tape: there is no easy way to embed WebView into a standard ViewPager.

    What's next

    Some studies claim that the complexity of developing for Android is relatively high . Our experience shows that a competent developer can easily get used to the features of this platform. And although today Android is the world's most popular mobile OS , in 5-10 years the situation may radically change.

    In recent years, Google has been secretly developing a new Fuchsia OS with a fundamentally different (compared to modern OS) security model. It is likely to use Dart as the main programming language , and Flutter framework as the main way of creating applications . Rumor has it that Fuchsia can replace Android and ChromeOSon all devices. If Google does this, the company will most likely provide native support for Android-written applications (as Microsoft did when switching from DOS to Windows). Therefore, while you can not worry and continue to accumulate expertise in Android. And for those who want to look into the future, download Flutter and play with it here .

    Also popular now: