[Video] Reports from Android Paranoid mitap

    Android is almost ten years old.

    We decided to celebrate this with a festive tea party with everyone who came to Yandex's St. Petersburg office 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 about

    • 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 talked about how to quickly and cost-effectively get animations in RecyclerView.

    For those who want to try them in work - a demo on GitHub . Details of the implementation are in the video.


    “What happens after tap on the application icon,


    And even a little earlier? ”, Said Vladimir Tebloev from Sberbank Technologies. We recommend watching the video if you thought that all life in Android is limited to views and activity, and you did not know anything about the work of the kernel, bootloader, dalwick, and all the time wondered why the android needs a zygote? For those interested - squeeze in three episodes.

    Episode 1 - System Levels and Zygote

    Once upon a time, engineers of a young mobile OS designed four levels of system operation:

    • kernel with drivers and binder;
    • root libraries, OS libraries, and Dalvik;
    • Application Framework, immutable system components - content providers, activity managers, etc .;
    • Custom applications.



    A little less than a long time ago, Dalvik turned into Android Runtime, but this did not change the essence of the process - after tapping on the Launcher icon, it receives a signal, passes 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 application on Android, cloning yourself and the root libraries that are needed to run all applications. This saves time and memory, because all the required 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 is the primary instance, in the middle is Zygote, which is responsible for the Android components, on the right is our application.

    Episode 2 - Life Cycle and Process Interaction

    Android has five types of processes and three priorities that are assigned to them.

    Critical priority is assigned to active processes - those with which the user is interacting right now. This can be an open activity or a music player in the UI.

    High priority is usually given to visible processes, for example, activities blocked by others. If there is not enough memory for active processes, the visible process will end. The same priority includes service processes with which the user does not interact - they are responsible for downloading data, synchronizing, etc. Low priority

    processesbelong to the already stopped activity. Android sorts these processes in startup order and stores them in memory to complete them in order from old to new. The last category - "zombie processes" - some thread can be initialized in them, but all components of the life cycle are already destroyed.

    The main way processes interact is through IPC through Binder. This is the driver through which all Android root structures work. The interaction model is shown in the diagram below.



    Suppose that activity in process A is to receive data from another process. The Foo () method accesses the Binder, which in turn serializes and wraps the input data and passes it to the target process for processing. Then the necessary data is deserialized, process B does something with them and performs operations in the reverse order.

    Separately, Vladimir spoke about all the stages of creating activities in Android. All details are in the video.


    “The user wants 60 FPS,


    That’s what background work is for. ”

    EPAM Vladimir Ivanov has been writing for Android and iOS for seven years and managed to bury Windows Phone. Vladimir spoke about the evolution of tools for completing tasks outside the main Android thread. Speech on the chain - AsyncTask, Loaders, Executors, EventBus, RxJava, Coroutines in Kotlin.

    The report has a lot of examples, here is a small part.

    Iteration 1 - AsyncTask

    Let's say we are writing an application that shows the weather forecast. The sequence of actions is approximately the following:

    1. Define the DoInBackground () method;
    2. Using an http client, we make a request to the server;
    3. We get and parsim response;
    4. We show the user.

    At the last stage, complexity arises - we cannot just take and return the answer from the background to the UI stream. If the UI simultaneously uses other threads, you have to think through crutches and complex locks. To avoid this, interface developers recommend updating only from the UI stream.

    Accordingly, you need a way to do something on the UI. AsyncTask uses the onPostExecute method for this, and we use it.

    public class LoadWeatherForecastTask
    extends AsyncTask < String,
    Integer,
    Forecast > {
    	public void onPostExecute(Forecast forecast) {
    		mTemperatureView.setText(forecast.getTemp());
    	}
    }
    

    This approach has one big problem and several disadvantages - AsyncTask 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 turned upside down), you need to overfulfill the request.

    Iteration 2 - Loaders

    With Android 3.0, Loaders came in - the Android team came up with them to solve AsyncTask problems.

    class WeatherForecastLoader(context: Context) : AsyncTaskLoader < WeatherForecast > (context) {
    	override fun loadInBackground() : WeatherForecast {
    		try {
    			Thread.sleep(5000)
    		} catch(e: InterruptedException) {
    			return WeatherForecast("", 0F, "")
    		}
    		return WeatherForecast("Saint-Petersburg", 20F, "Sunny")
    	}
    }
    

    In particular, we are talking about reusing the result when changing the configuration. The problem is solved as follows:

    1) LoaderManager, tied to activity, stores links to several Loaders in a special structure;
    2) Activity saves all LoaderManager inside NonConfigurationInstances;
    3) When creating a new activity, the system transfers data from NonConfigurationInstances to it;
    4) Activity restores LoaderManager with all Loader.

    Cons:
    1) There is still a lot of code;
    2) The interfaces are still complex, and the classes are still abstract;
    3) Loaders are the platform API of Android, which means that they cannot be reused in pure Java.

    Iteration 3 - EventBus and ThreadPoolExecutors

    With the advent of ThreadPoolExecutors, the transfer of data from the background to the UI began to look like this:

    1) We start the Background class, and in it the Service variable;
    2) We initialize this class in ScheduledThreadPoolExecutor with the size we need;
    3) We write helper methods that make the class runnable or callable.

    public class Background {
    	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 in the background, you still need to return the result to the UI. To do this, we write a handler and a method that posts something on a UI thread.

    public class Background {…private lateinit
    	var mUiHandler: Handler
    	public fun postOnUiThread(runnable: Runnable) {
    		mUiHandler.post(runnable)
    	}
    }
    

    Not all UIs need to know that a particular method has completed. To share responsibility, came up with EventBus. This is a method of transmitting events from the background thread to the UI, in which several listeners are connected to the shared bus, which process these events.



    There are several ready-made implementations of EventBus. Some of them are Google Guava, Otto and GreenBot Eventbus.

    Of the minuses:

    1. The event data source knows nothing about how it should be handled;
    2. According to the speaker’s experience, after a while the code with EventBus becomes impossible to support.

    The fourth iteration - RxJava, or “Enough of it!”

    Someone had to come up with a convenient tool for background work. As a result, we have RxJava - a large framework for working with event streams.

    Suppose we are writing code that should be authorized on GitHub. It is necessary to start using the method for each necessary operation (in our case, login and obtaining a list of repositories).

    interface ApiClientRx {
    	fun login(auth: Authorization) 
    	    : Single < GithubUser > 
    	fun getRepositories(reposUrl: String, auth: Authorization) 
    	    : Single < List < GithubRepository >>
    }
    

    The result of 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 that the user needs.

    Minuses:

    1. A steep learning curve; learning is long and difficult;
    2. Many operators, the difference between which is difficult to understand;
    3. About 20 objects are created for simple code from two queries and two operators, which leads to excessive memory use;
    4. Irrelevant stacktrace, out of 30 lines, only one can relate to your code.

    Pros:

    1. RxJava is the de facto standard. Vladimir conducted a survey on Twitter and found that 65% of developers in new projects will use RxJava;
    2. Powerful API
    3. RxJava is an open source framework that has a large community;
    4. RxJava code is easily covered by unit tests.

    Fifth iteration - Coroutines

    Coroutines - a background library with support within the Kotlin language.

    Its advantages:

    1. Non-blocking approach - the main thread runs during the background work and embeds the results as it progresses;
    2. Asynchronous code in a synchronous style

    private fun attemptLogin() {
    	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 to study - the learning curve is almost not a curve;
    5) After a little deliberation, unit tests become almost the same as for synchronous code.

    Cons:

    1) Recently out of experimental status;
    2) This is not a part of the language, but a library;
    3) Not everything has adapters;
    4) Coroutines is not a replacement for RxJava. They will not help much in difficult cases with a complex stream of events.

    The rest about Coroutines (including examples) is better to listen to in the report:


    How to fit code into 300 lines programming in Kotlin


    A year ago, at Google IO 2017, it was announced that Kotlin became the official Android language. A report by Yuri Chechetkin from Alfa Bank on how to start moving to a new language, reduce classes to 300 lines and not go crazy.

    The report is a practical educational program on how to write compactly and beautifully. It is aimed at an advanced audience that knows the main features of Kotlin.

    The report has a lot of examples of use and comparisons of the code in two languages, so here we give only interesting facts and conclusions.

    Key migration issues to Kotlin:

    1. Legacy Java code. Large classes of several thousand lines of code are very difficult to convert using the development environment;
    2. Dependencies - Lombok, Stream API, etc .;
    3. Excessive code requirements within the team. Regular automatic checks of the code for a limit of 300 lines and code review are carried out;
    4. Kotlin is a new language, and it is difficult to formulate requirements until there are no uniform conventions;
    5. Kotlin compiles longer;
    6. Syntactic sugar is "great power, but great responsibility."



    Conclusions:

    • After a year of using Kotlin, there was less code - it became cleaner, it became more convenient to do code review;
    • No old Java methods, no extra dependencies, only standard language features;
    • Fewer crutches and bugs;
    • More features - some things implemented in Kotlin cannot be written in Java.

    The rest is in the video.


    Follow the events and everyday life of the Y. Money team on VK , Facebook and Instagram .
    All Yandex conferences and meetings are on Y. Vstrechah .

    Also popular now: