We decided to celebrate this with a festive tea party with all those who came to the St. Petersburg office of Yandex on the second Android Paranoid mitap. No sooner said than done. Unfortunately, marshmallows, chocolate chip cookies and jelly beans ended on March 28th.
')
Instead, there are reports recorded on video and a short squeeze of useful information for Android developers. Under the cut that
what happens after clicking on the application icon;
how to translate the application to Kotlin and fit in 300 lines of code;
how Android background tools changed;
how to quickly get animations in RecyclerView.
About animations in RecyclerView
Danil Ternov from Yandex. Money told about how quickly and without costs to get animations in RecyclerView.
For those who want to try them at work - a demo on GitHub . Implementation details - in the video.
"What happens after the tap on the application icon,
And even a little earlier? ”Said Vladimir Tebloev from Sberbank-Technologies. We recommend to watch the video if you thought that all life in Android is limited to views and activations, and did not know anything about the work of the kernel, bootloader, dalwick, and all the time wondered - why android zygote? For those interested - squeeze in three episodes.
Episode 1 - System Levels and Zygote
A long time ago, engineers of a young mobile OS designed four levels of system operation:
A little less long ago, Dalvik turned into Android Runtime, but it didn’t change the essence of the process - after tapping the Launcher icon receives a signal, sends it to the activity manager, it is transferred to Zygote, and it creates a new application.
Zygote is a daemon that starts at system startup and initializes the primary virtual machine. Zygote allows you to create processes for any Android application by cloning yourself and the root libraries that are needed to run all applications. This saves time and memory, because all the necessary libraries are already initialized in the primary Zygote instance. All that remains is to use Copy-on-Write and change the ProcessID.
On the left - the primary instance, in the middle - Zygote, responsible for Android components, on the right - our application.
Episode 2 - Life Cycle and Process Interaction
In Android, there are five types of processes and three priorities that are assigned to them.
Critical priority is assigned to active processes - those with which the user interacts right now. This can be open source or music player in the UI.
High priority is usually obtained by visible processes, for example, activites blocked by others. If there are not enough memory for active processes, the visible process will end. The priority also includes service processes with which the user does not interact - they are responsible for downloading data, synchronization, etc.
Low priority processes are related to already stopped activations. Android sorts such processes in order of launch and stores in memory to complete them in order from old to new. The last category - “zombie processes” - some stream can be initialized in them, but all components of the life cycle have already been destroyed.
The main way of process interaction is IPC through Binder. This is the driver through which all Android root structures work. The interaction model is in the diagram below.
Suppose that the activation in process A must receive data from another process. The foo () method calls Binder, which in turn serializes and packages the input data and passes it to the target process for processing. Then the necessary data is deserialized, process B does something with it and performs operations in the reverse order.
Separately, Vladimir spoke about all the stages of creating activities in Android. All the details are in the video.
"User wants 60 FPS,
This is what background work is for. ”
Vladimir Ivanov from EPAM has been writing for Android and iOS for seven years and has managed to bury the Windows Phone. Vladimir spoke about the evolution of tools for performing tasks outside the main stream on Android. Speech about a chain - AsyncTask, Loaders, Executors, EventBus, RxJava, Coroutines in Kotlin.
There are a lot of examples in the report, here is a small part.
Iteration 1 - AsyncTask
Suppose we write an application that shows the weather forecast. The sequence of actions is approximately as follows:
Define the DoInBackground () method;
With the help of the http-client we make a request to the server;
We receive and parsim the answer;
Show the user.
At the last stage, complexity arises - we cannot simply take and return the answer from the background to the UI-stream. If the UI simultaneously uses other threads, you have to think about crutches and complex locks. To avoid this, interface developers recommend updating only from the UI stream.
Accordingly, we need a way to do something on the UI. In AsyncTask, the onPostExecute method is used for this, and we use it.
This approach has one big problem and several drawbacks - AsyncTask has died, with the exception of production projects that are more than five years old.
And the disadvantages are:
1) A lot of code for network requests; 2) AsyncTask does not know anything about the life cycle of activities and potentially lead to memory leaks; 3) When changing the configuration on the fly (for example, the screen is turned over), you need to exceed the request.
Iteration 2 - Loaders
Loaders came from Android 3.0 - the Android team came up with them to solve AsyncTask problems.
In particular, we are talking about reuse of the result when changing the configuration. The problem is solved as follows:
1) LoaderManager, attached to the activity, stores links to several Loader in a special structure; 2) Activity saves all LoaderManager inside NonConfigurationInstances; 3) When creating a new activity, the system sends data from NonConfigurationInstances to it; 4) Activity restores LoaderManager with all Loader.
Minuses: 1) Still a lot of code; 2) Interfaces are still complex, and classes are still abstract; 3) Loaders is the Android platform API, which means that they cannot be reused in pure Java.
Iteration 3 - EventBus and ThreadPoolExecutors
With the advent of ThreadPoolExecutors, data transfer from the background to the UI began to look like this:
1) Set the Background class, and in it - the Service variable; 2) Initialize this class in the ScheduledThreadPoolExecutor with the size we need; 3) We write helper methods that make the class runnable or callable.
publicclassBackground{ private val mService = ScheduledThreadPoolExecutor(5) fun execute(runnable: Runnable) : Future < *>{ return mService.submit(runnable) } fun < T > submit(runnable: Callable < T > ) : Future < T > { return mService.submit(runnable) } }
In addition to running against the background, you still need to return the result to the UI. To do this, write a handler and a method that posts something on a UI thread.
publicclassBackground{…private lateinit var mUiHandler: Handler public fun postOnUiThread(runnable: Runnable){ mUiHandler.post(runnable) } }
Not all of the UI needs to know that a particular method has been executed. To share the responsibility, come up with an EventBus. This is the way to transfer events from the background thread to the UI, in which several listeners are connected to the common bus, which process these events.
There are several pre-made EventBus implementations. Some of them are Google Guava, Otto and GreenBot Eventbus.
Of the minuses:
The source of the event data knows nothing about how it should be processed;
According to the speaker’s experience, after some time, the code with EventBus becomes impossible to maintain.
Iteration four - RxJava, or “Enough to endure!”
Someone had to come up with a handy tool for background work. As a result, we have RxJava - a great framework for working with event streams.
Suppose we are writing code that must be authorized on GitHub. It is necessary to start by the method for each necessary operation (in our case - login and getting the list of repositories).
interfaceApiClientRx{ fun login(auth: Authorization) : Single < GithubUser > fun getRepositories(reposUrl: String, auth: Authorization) : Single < List < GithubRepository >> }
The result of the execution will be Single - a stream from zero or one event. The result of the work is an interface of two methods that return everything the user needs.
Minuses:
Steep learning curve, long and difficult to learn;
Many operators, the difference between them is difficult to understand;
About 20 objects are created on a simple code of two queries and two operators, which leads to excessive use of memory;
Irrelevant stacktrace, out of 30 lines only one can relate to your code.
Pros:
RxJava is the de facto standard. Vladimir conducted a survey on Twitter and found out that 65% of developers in new projects will use RxJava;
Powerful API;
RxJava is an open source framework that has a large community;
The code on RxJava is easily covered by unit tests.
Iteration Five - Coroutines
Coroutines is a library for background work with support within the Kotlin language.
Its advantages:
1. Non-blocking approach - the main thread runs during background work and embeds the results as it goes; 2. Asynchronous code in synchronous style
privatefunattemptLogin() { launch(UI) { val auth = BasicAuthorization(login, pass) try { showProgress(true) val userInfo = login(auth).await() val repoUrl = userInfo.repos_url val list = getRepositories(repoUrl, auth).await() showRepositories(this@LoginActivity, list.map { it - >it.full_name }) } catch(e: RuntimeException) { Toast.makeText(this@LoginActivity, e.message, LENGTH_LONG).show() } finally { showProgress(false) } } }
3) Means of language instead of operators; 4) Just learn - the learning curve is almost not a curve; 5) After a little thinking, unit tests become almost the same as for synchronous code.
Minuses:
1) Recently exited experimental status; 2) This is not a part of the language, but a library; 3) Not for everything there are adapters; 4) Coroutines is not a replacement for RxJava. They will not help much in difficult cases with a complex flow of events.
The rest of the Coroutines (including examples) is best heard in the report:
How to fit the code in 300 lines, programming in Kotlin
A year ago, Google IO 2017 announced that Kotlin became the official language of Android. A report by Yuri Chechetkin from Alfa-Bank on how to start moving to a new language, reducing classes to 300 lines and not going crazy.
The report is a practical educational program on how to write compactly and beautifully. It focuses on an advanced audience that knows the main features of Kotlin.
There are a lot of examples of using and comparing code in two languages ​​in the report, so here we will provide only interesting facts and conclusions.
The main problems with the migration to Kotlin:
Legacy code in Java. Large classes with several thousand lines of code are very difficult to convert by means of the development environment;
Dependencies - Lombok, Stream API, etc .;
Excessive requirements for code within the team. Regular automatic checks of the code for a limit of 300 lines and code review are carried out;
Kotlin is a new language, and it is difficult to formulate requirements, as long as there are no uniform conventions;
Kotlin compiles longer;
Syntactic sugar is “big power, but big responsibility”.
Findings:
After a year of using Kotlin, the code became smaller - it became cleaner, it became more convenient to do code review;
No old Java methods, no extra dependencies, only standard language features;
Less crutches and bugs;
More features — some things implemented on Kotlin cannot be written in Java.
The rest is in the video.
Follow the events and weekdays of the Ya. Dengi team in VK , facebook and instagram . All conferences and meetings of Yandex are on Ya . Meetings .