⬆️ ⬇️

How Android works, part 3



In this article, I’ll talk about the components that make up Android applications, and the ideas behind this architecture.



Articles series:





Web vs desktop



If you think about the differences between modern web applications and "regular" desktop applications, you can - among the shortcomings - highlight several advantages of the web:





In addition, web applications exist in the form of pages that can link to each other - both within one site and between sites. At the same time, a page on one site is not obliged to limit itself only to the main page of another, it can link to a specific page within another site (this is called deep linking ). Referring to each other, individual sites are united in a common network, the web.



Multiple copies of the same page — for example, several profiles in a social network — can be simultaneously opened in several browser tabs. The browser interface is designed to switch between simultaneous sessions (tabs), and not between separate sites - within the same tab you can navigate through the links (and forward-back through the history) between different pages of different sites.



All this is opposed to the "desktop", where each application works separately and often independently of the others - and in this regard, the way Android applications are arranged is much closer to the web than to "traditional" applications.



Activities & intents



The main view of Android application components is activity . An activity is one “screen” of an application. An activity can be compared with a page on the web and with an application window in a traditional window interface.



About windows

Actually, windows in Android are also at a lower level - the window manager level. Each activity usually has its own window. Most often, activity windows are deployed on the entire available screen, but:



  • First, Android supports multi-window mode - split-screen, picture-in-picture, and even freeform.
  • Secondly, Android supports connecting multiple displays.
  • Thirdly, the activity may intentionally occupy a small part of the screen ( Theme_Dialog ).






For example, in an email client application, there can be such an activity as Inbox Activity (list of incoming letters), Email Activity (reading one letter), Compose Activity (writing a letter) and Settings Activity (settings).



As well as pages of one site, the activity of one application can be launched both from each other and independently from each other (by other applications). If the web to another page is accessed by URL (link), then Android activity is launched through intent.



Intent is a message that tells the system what to “do” (for example, open a given URL, write a letter to a given address, call a given phone number or take a photo).



An application can create such an intent and transfer it to the system, and the system decides which activity (or other component) will handle it. This activity is started by the system (in the existing application process or in the new one, if it is not already running), this intent is passed to it, and it executes it.



The standard way to create intent is through the corresponding class in the Android Framework. To work with activity and intent from the command line on Android, there is the am command - a wrapper over the standard Activity Manager class:



 #  -a ACTION -d DATA #   $ am start -a android.intent.action.VIEW -d http://example.com #    $ am start -a android.intent.action.CALL -d tel:+7-916-271-05-83 


Intents can be explicit and implicit . An explicit intent indicates the identifier of a particular component that needs to be launched — most often it is used to launch another activity from one activity within the same application (the intent may not even contain other useful information).



The implicit intent must specify the action to be taken. Each activity (and other components) indicate in the application manifest which intent they are ready to process (for example, ACTION_VIEW for links with the https://example.com domain). The system selects the appropriate component among the installed ones and launches it.



If the system has several activities that are ready to process an intent, the user will be given a choice. This usually happens when several similar applications are installed, for example, several browsers or photo editors. In addition, an application can explicitly ask the system to show a selection dialog (in fact, the transmitted intent turns into a new intent with ACTION_CHOOSER ) - this is usually used to create a beautiful Share dialog:





In addition, activity can return the result to the activity that caused it. For example, activity in a camera application that can handle an “take a photo” intent ( ACTION_IMAGE_CAPTURE ) returns the taken photo to the activity that created this intent.



However, the application containing the original activity does not need permission to access the camera.



Thus, the correct way for an Android application to take a photo is not to require permission to access the camera and use the Camera API, but to create the desired intent and allow the system application-camera to take a photo. Similarly, instead of using the READ_EXTERNAL_STORAGE permission and direct access to the user's files, you should give the user the opportunity to select the file in the system file manager (then the source application will be allowed to access this file).



The app can start another app's component. For example, if you want to take a picture of yourself, you can. From the camera app. Instead, you can simply take it up. When complete, you can use it. The app is actually a part of your app.

At the same time, the “system” application is not necessarily the one that was preinstalled by the manufacturer (or the author of the Android assembly). All installed applications that can handle a given intent are equal in this sense. The user can choose any of them as the default application for such intent'ov, and can choose the right one every time. The selected application becomes “systemic” in the sense that the user chooses that it performs all the tasks (ie, intent) of this type arising in the system.



The permission to access the camera itself is necessary only for those applications that implement their own camera interface - for example, the actual camera application, video call application or augmented reality. On the contrary, an ordinary messenger access to the camera "so that you can send photos" is not needed , as is not needed, and access to making calls to the application of a large bank .



About launcher

Even such “parts of the system”, such as the home screen (launcher), obey this logic. Launcher is a special application with its own activity (which uses special flags like excludeFromRecents and launchMode="singleTask" ).



Pressing the “home” button creates an intent of the HOME category, which then goes through the usual intent processing mechanism — including if there are several launchers installed in the system and none are selected as the default launcher, the system will display a selection dialog.



The "launch" of the application from the launcher also occurs via the intent. Launcher creates an explicit LAUNCHER category LAUNCHER that is “processed” by running the main activity of the application.



An application can have several activities that support such an intent, and be displayed in the launcher several times (you may need to point them to different taskAffinity ). Or do not have one and do not appear in the launcher at all (but still appear in the full list of installed applications in the settings). "Normal" applications do this quite rarely; The most famous example of this behavior is Google Play Services.



Many operating systems are divided into the actual operating system and applications installed on top, they do not know and cannot interact with each other. The system of components and intent of Android allows applications, still without knowing anything about each other , to make up for the user one integrated system user experience - the installed applications implement parts of one large system, they make up the system themselves. And this, on the one hand, is transparent to the user, on the other, it presents unlimited possibilities for customization.



In my opinion, it is very beautiful.



Tasks & back stack



As I said, in the browser, the user can not switch between sites, but between tabs, the history of each of which may contain many pages of different sites. Similarly, in Android, a user can switch between tasks (tasks), which are displayed as cards on the recents screen . Each task is a back stack - several activities “superimposed” on each other.



When one activity starts another, the new activity is pushed onto the stack on top of the old one. When the top activity in the stack ends - for example, when the user presses the system button “back” - the previous activity in the stack is displayed again on the screen.



Back stack



Each stack can include an activity from different applications, and multiple copies of the same activity can be simultaneously open for different tasks or even within the same stack.



When launching a new activity, special flags can be specified, such as singleTop , singleTask , singleInstance and CLEAR_TOP , which modify this mechanism. For example, browser applications typically allow only one copy of their primary activity to be launched, and implement their own tabs system to switch between open pages. On the other hand, Custom Tabs is an example of activity in the browser (most often Chrome), which behaves almost “as usual”, that is, it shows only one page, but allows you to simultaneously open several of your copies.



App lifecycle



One of the main limitations of embedded and mobile devices is a small amount of random access memory (RAM). If modern flagship devices are already equipped with several gigabytes of RAM, then in the first Android smartphone, HTC Dream (aka T-Mobile G1), released in September 2008, it was only 192 megabytes.



HTC Dream



The limited memory problem is further complicated by the fact that mobile devices, unlike “ordinary” computers, do not use swap partitions (and swap files) - including due to the low access speed (compared to SSD and HDD) to SD cards and built-in flash memory where they could be placed. Starting from version 4.4 KitKat, Android uses zRAM swap, that is, it effectively compresses little-used chunks of memory. However, the problem of limited memory remains.



If all processes are a black box for the system, the best possible strategy for behavior in the event of a lack of free memory is to forcibly terminate (“kill”) some processes, which is what the Linux Out Of Memory (OOM) Killer does. But Android knows what is happening in the system, he knows which applications and what their components are running, which makes it possible to implement a much smarter memory freeing scheme.



First, when free memory runs out, Android explicitly asks applications to free up unnecessary memory (for example, onTrimMemory cache) by calling onTrimMemory / onLowMemory . Secondly, Android can efficiently collect garbage in background applications, freeing up memory that they no longer use (at the Java level), while not slowing down the current application.



But the main mechanism for freeing memory in Android is the completion of the least used applications . The system automatically selects applications that are least important to the user (for example, those from which the user has long left), gives their components a chance to free up resources by calling methods such as onDestroy , and terminates them, completely freeing the memory and resources they use.



If the user returns to the activity of the application terminated by the system due to low memory, this activity is started again. At the same time, the restart occurs transparently for the user , since the activity retains its state on completion ( onSaveInstanceState ) and restores it on the next launch. The widgets implemented in the Android Framework use this mechanism to automatically save the state of the interface (UI) when it is restarted — up to the text entered in EditText, the cursor position, the scroll position, etc. The application developer can additionally implement the saving and recovery of some other data specific to this application.



I emphasize that Android can restart applications not completely, but component by component , leaving unused parts complete - for example, of two copies of one activity, one can be restarted, and the other can remain complete.



From the user's point of view, this mechanism is similar to using swap: in both cases, when returning to the unloaded part of the application, you have to wait a little while it loads again - in one case, from the disk, in the other - it is recreated from the saved state.



It is this automatic restarting and restoring mechanism that creates the user’s feeling that applications are “always running”, eliminating the need to explicitly start and close applications and save the data entered into them.



Services



Applications may need to perform actions that are not directly related to any activity, including continuing to do them in the background, when all the activities of this application are completed. For example, an application can download a large file from the network, process photos, play music, synchronize data, or simply maintain a TCP connection to the server to receive notifications.



Such functionality cannot be implemented simply by running a separate thread — this would be a black box for the system; including, the process would be completed at the completion of all the activity, regardless of the state of such background operations. Instead, Android offers to use another type of component - a service .



The service is needed to inform the system that during the application process, actions are taken that are not part of the activity of this application. By itself, a service does not mean the creation of a separate thread or process — its entry points are started in the main application thread. Typically, a service implementation starts additional threads and manages them independently.



Services are in many ways similar to activity: they are also started using intent and can be completed by the system when there is a shortage of memory.



Running services can be in three states:









The recommended way to perform background actions is to use JobScheduler , a system mechanism for scheduling background work. JobScheduler allows an application to specify the criteria for starting a service, such as:





JobScheduler plans to execute (implemented as a call through the binder) services registered in it in accordance with the specified criteria. Since JobScheduler is a system-wide mechanism, it takes into account when scheduling the criteria for registered services of all installed applications. For example, it can start services in turn rather than at the same time, to prevent a sudden load on a device during use, and schedule periodic execution of several services in small groups (batch) to prevent a permanent energy-consuming on-off radio equipment.



Pro TCP connection

As you can see, using JobScheduler can not replace one of the options for using background services - maintaining a TCP connection to the server to receive push notifications. If Android provided applications with this capability, the device would have to keep all applications connecting to their servers running all the time , which is, of course, impossible.



The solution to this problem is special push services , the most famous of which is Google's Firebase Cloud Messaging (formerly Google Cloud Messaging).



The client part of FCM is implemented in the Google Play Services application. This application, which is specifically excluded from the usual restrictions on background services, supports one connection to Google servers. A developer who wants to send a push notification to his application sends it through the FCM server side, after which the Play Services application, after receiving the message, sends it to the application to which it is intended.



This scheme allows, on the one hand, to instantly deliver push notifications to all applications (without waiting for the next synchronization period), on the other hand, not to keep many applications running simultaneously.



Broadcast receivers & content providers



In addition to activity and services, Android applications have two other types of components that are less interesting for discussion: broadcast receivers and content providers.



Broadcast receiver is a component that allows an application to receive broadcasts , a special kind of message from the system or other applications. Initially, broadcasts, as the name suggests, were mainly used to send broadcast messages to all subscribing applications — for example, the system sends an AIRPLANE_MODE_CHANGED message when the airplane mode is turned on or off.



Now, instead of subscribing to broadcasts like NEW_PICTURE and NEW_VIDEO , applications should use JobScheduler. Broadcasts are used either for more rare events (such as BOOT_COMPLETED ) or with explicit intent, that is, as a message from one application to another.



The content provider is a component that allows an application to give other applications access to the data it manages. An example of data that can be accessed in this way is the user's contact list.



, , (SQLite) . content provider — , .



content provider' REST API. - URI (, content://com.example.Dictionary.provider/words/42 ) ContentResolver. - , , UriMatcher , (query, insert, update, delete).



content provider' Storage Access Framework , , (, Dropbox Google Photos) , .






Android, , , , root-.



')

Source: https://habr.com/ru/post/338494/



All Articles