Android Go is the next billion devices and 50 MB limit. Yandex lecture

    New directions for the development of an already familiar platform are always interesting. On the one hand, you are expanding your client base, on the other hand, you are not investing in creating software from scratch, but using existing practices. But if the direction is really new, with its own specifics, then it will not be possible to manage with just a little blood. At the next meeting of the Mosdroid community in our office, developer Artur Vasilov Arturka told about the adaptation of the Yandex application to the Android Go system.


    On average, if you don’t write a calculator, an alarm clock, etc., then either you are very cool, well done and done well, or your application is 150–170 megabytes.

    - My name is Arthur, I am an Android developer, working on the Yandex application. Today I will share with you the story of how we adapted to Android Go. I'll tell you what kind of rake we stumbled upon, what we did not succeed in and how it all works, why it is necessary.

    A small digression about what this is all about. Android Go is a special version of Android, which is designed for cheap devices. They cost 60-100 dollars, and therefore they are very weak, slow, inhibited. So, Google decided for them to make their system so that they would somehow work normally. It was announced on Google I / O in 2017, that is, a year and a few months have passed. Therefore, when the mitap was announced, there was a logical question: “Are you still alive or what?”. I then said that everything is fine, everything is fine, I will tell. And now, as a typical Internet hero, I will answer after the fact why this happened.


    What exactly happened? Google said: "We make such a system." Then he said: “Okay, we need time for refinement” and all that. After that, vendors always have some delay in adapting this version to themselves. And the new version with us, and so goes no earlier than a year, so there is nothing surprising. In addition, they needed to decide whether it was necessary to do such a thing at all: it is a question of cheap devices and it is not clear whether there will be a profit from them or not. In addition, this device should be new, it must be done, learn to sell, understand how it will work.

    The first smartphone with Android Go appeared not so long ago. Somewhere in April, probably, sales began, and maybe in May. This is Nokia 1, it is sold everywhere. I'm lying around here. Now, in my opinion, there are only nine such smartphones on the market, but by the end of the year they are promised more than a hundred. In principle, none of the major players like Huawei, Samsung and others have said their word, so they will add something else, they will not be able to stay away from such a large market.

    Before the report, I went to the Android Go page and saw that they had done Android Pie Go Edition. But they didn’t do anything there, they just reduced the number of installed applications and their weight in advance. Said: - "You now have twice the amount of free space." And standard excuses: bug fix, performance improvements, everything. But at least they called, and therefore did not forget.

    What are the limitations of these devices, specifically? First, they have 512 megabytes or 1 gigabyte of RAM, 8 or 16 gigabytes of storage. It is clear that under such conditions they are extremely slow and all normal applications will work on them in about the same way. In order for applications to work on them with minimal adequacy, Google said: "Let's introduce the following requirements." They are quite logical, stemming from what was on the previous slide.



    First of all, this is an abstract good performance. Your application should be well and quickly run and run on such a device. We are like, “Great. We work. "

    Then there are specific numbers that need to be consistent. The space occupied after unpacking and installing the APK is no more than 40 megabytes. Sometimes this is a problem, because someone and the APK weighs all 80 megabytes. It will hurt. Moreover, it cannot be adequately taken and measured. That is, you cannot say: “I know that my APK weighs so much, so after installation the application will take up so much.” All this very much depends on the vendor, the version of the device, Android, etc. But if your APK is 10 megabytes, then, in principle, everything is fine and you will never exceed this number.

    And now the most fun and cool requirement: consumed RAM while working with the application should not exceed 50 megabytes.

    Who knows how much memory his application takes on average during operation? Who ever was interested in this question? Are there those who have less than 100 megabytes? Pretty boy. But maybe you're lying. In general, on average, if you don’t write a calculator, an alarm clock, etc., then either you are very cool, well done and done well, or your application is 150–170 megabytes. Putting it in 50 megabytes is very difficult. Therefore, the rest of the time we will talk about it, we will discuss how to shove a globe into an owl, etc.


    What does our memory conversation include? You need to directly understand what we are measuring, then what is the best way to measure it. Also, for Android Go devices there is a specificity that needs to be taken into account, and we'll talk about it too. And I will also tell you some general things, general tips that you might not have guessed, but they can really eat a lot of memory from you.

    In 2018, after Google I / O has passed, it’s necessary to begin a story about memory with reference to this report . Who watched it?

    Fine. All the other 180 people know what to do in the near future. In my opinion, this is one of the best reports on Google I / O. Dude told unrealistically cool stuff. He told everything on the shelves, well, and with many deep details. Those who watched it and remembers well will surely notice that I copied some things from there, because otherwise it is impossible, he told everything, so I will repeat.

    What do we measure? There was such a thing called PSS (Proportional Set Size). That is, the RAM in Android is represented by some blocks of 40 kilobytes each, and these blocks may either belong entirely to the application or be framed between processes and applications.

    And the question is: how exactly to understand, to which application to assign this shared memory? There are several approaches, they are quite logical. PSS says that if memory is fumbled between N processes, then we will assume that 1 / N of this memory belongs to your application. And there is absolutely similarly the Residential Set Size and Unique Set Size, which says that “Nothing from the shared memory does not belong to me” and “Everything belongs to”. In principle, PSS is the most logical here.

    How exactly can you measure memory consumption? Everything is simple here. Either this is a profiler in Android Studio, or it is dumpsys. There are, of course, other tools. They can give you more detailed results, something more complicated, but the problem is that to understand their results, to use them all is very, very difficult. Often you need either root or custom build Android. And you don't really need them, the first two tools are enough.


    Link from the slide

    I will not speak on the profiler in Android Studio, I think many people used it. Who have not used, be sure to poke. In particular, I gave the link below - just a good article from the documentation from the video, how to use it, with demos. And, in principle, everything is clear. It shows where your memory goes, shows it in real time. The only thing that needs to be remembered is that it still imposes certain errors that arise from the fact that we constantly measure this memory. But they are within acceptable limits.


    Link from the slide

    Dumpsys is a simple console piece that doesn’t require anything from you, just the connected phone and adb. And you can execute the following command: call dumpsys meminfo, give it a packet, and it will return approximately the same picture to you. And if we are interested in exactly how much our application consumes, then we can look specifically at TOTAL, which says that "your application is eating about 168 megabytes." A lot, but what to do?


    Link from the slide

    He also shows you a breakdown of what it takes up in this consumed memory. There are various sections here, they are complex, we will talk about them further, but for now we can notice the main thing - this is the Java Heap, our java objects, and all that is further is more complicated.

    What else is important? Memory is a very sensitive thing to all kinds of tests and all kinds of external conditions. That is, all the tests you need to perform as much as possible in the same conditions. It is clear that this should ideally be one device, because the memory depends on the version of Android. Here it is enough to recall the differences between the fours and the fives. It depends on the size or resolution of the screen, because the larger the screen, the more content it fits, the more memory it takes to draw all this. The higher the resolution, the more pixels your bitmap occupies, and the more memory is required to store them.

    The application scenario also affects the tests. You can read the text or you can scroll the gallery with a bunch of pictures. And it is clear where the memory will be more.

    Another important thing is the load on the device. That is, you may be all the same, but in one case your application is the only one that works, and in another case you have a lot of applications that are still doing something in the background, are downloading, deleting, working on the foreground , and at the same time, your application just squeezes the memory, because you have to give it to other applications. Therefore, ideally, it is better to take and first kill all other applications that work, not yours. All that you can reach, then kill. Just in this case, PSS will thank you exactly, because there will be no need to fumble memory between processes.


    You can, for example, take and view current information on free memory, on occupied memory. He will print you something like this, which says that "I have so much free memory, so much cached memory, so much occupied memory." And if you have 200–250 megabytes of free memory there for yourself, then this is good, most likely, then nothing will affect your tests.

    Perhaps someone now has the question "Why do I need all this?". This is such a break in which I will additionally say the motivation for all this.

    Firstly, even if you are not going to do anything under Android Go now and think that it is dead, it may well develop, come to you, and you will have to deal with all this at some point.

    The second thing that I consider very important is that you can simply do regression tests on memory. That is, you can simply write a script that will launch the application, do dumpsys, perform such measurements, and watch how these indicators change between releases. Such a script can be written in a couple of hours, the infrastructure can be set up - longer, but it seems to me that this is a good thing.

    If we talk about the specifics of Android Go - our third item in the fight against memory - then there is some good news. Firstly, no one really demands for you to follow these restrictions. That is, you can use the application as you have it, put it on the device with Android Go, and everything is fine. The problem is that the user is very likely to remove you, because you occupy a lot of space. Also, your application may run slowly and there is a lot of memory, yes. But so far no one forbids it, because otherwise there would not be a single application, except for Google on Android Go. But if further this thing develops, many will adapt to such conditions, then in the end, your application can simply be reduced in the output of Android Go, or say how would the user install the application on their Android Go smartphone, they can show Alert to him: “Dude, the application does not work well with Android Go. Maybe you will not put it? ".

    There is one more thing - you can exclude Android Go devices from Google Play, that is, say that the application cannot be installed on Android Go devices. It appeared not so long ago.

    And Google is also quite smart, and the 50 megabytes that sounded in the title of the report is not a fixed number, it depends on the resolution of the device, on the screen size, and, besides, on the type of application. For example, games are allocated more, in my opinion, 115 megabytes. In principle, this is quite understandable.


    Link from the slide

    What if we talk directly about this test? There is one more thing that particularly concerns us in particular - working with presets. When vendors make a new phone, they often put some sort of pre-installed applications there. In particular, we are very involved in this, and the problem is that there they chase away such things as the Compatibility Test Suite. These are google tests, they chase them away. And there, if your application does not fit the 50 megabytes, then everything is bad, and your application cannot be pre-installed on such a device.

    Unfortunately, the tests that Google does are not from open source, I cannot tell them, they are under NDA, but good developers from Google have written such an article about Android Go, and there, in principle, there are recommendations that are good enough

    That is all trite. Run the application. We are waiting for 5 seconds until everything is loaded. We do dumpsys, write the value TOTAL, perform a bunch of times, we get the result. Everything is very simple and trite.


    The only thing is that they did not take into account such a small feature in their article, or perhaps they didn’t talk about it - there is such a thing as work in another process, and they often do it to fight, including memory consumption.

    Who thinks it's good for Android Go? And who thinks it's bad? Boldly.


    The problem is that, yes, this is bad, because in the end, your application consumed memory is considered for all processes. If we make such an empty process without anything and take the dumpsys of this particular process, we will see that it takes 7 megabytes. 5-8 megabytes is such an overhead from process creation. Therefore, when we fight for every megabyte, to squeeze it all into 50, then such a thing is given to us very badly. In particular, let's say Yandex has the most popular Yandex.Metrica library, it also works in a different process, and this can also give us pain.


    Therefore, if some external libraries come to you, for example, you can simply say: “Dude, please work in the main process. I agree that it may be slower, but it will not eat extra memory. ” So this is also a subtle point.



    If we talk about memory consumption, let's move on to this label, which is there. We take an empty project, and run this dumpsys, set it on this matter, and see that 23 megabytes out of 50 are already taken. Empty “Hello, world!” Without anything, just activate with text. It gets pretty sad. It becomes even sadder because of the fact that we can directly influence such a parameter as the Java Heap, that is, it is our own java objects that we can track explicitly, which we can delete, reduce and somehow interact with.

    And with this whole thing, it’s difficult to interact normally, because it’s all framework code and you don’t have any normal tools directly from Java to understand how all this is used. But the good news is that we can influence the whole thing indirectly, so let's talk about what it is.


    What is Java Heap is understandable. What is Native Heap is also quite logical to assume. These are the same allocations, only plus. They come from the framework and from your native libraries, .so files and so on.

    The code is directly related to the storage of the code. This is the size of your .dex, these are your .so, these are resources, mmap-files and all that. That is, the less code, the better. The simplest truth that works at all about everything.

    Stack is a stack of Java / C ++ threads. That is, each thread has its own call stack, so each thread is created such a specific area of ​​memory to store the whole thing.

    Graphics is the UI drawing. There are partially stored and bitmaps that are drawn, and what is connected with it.

    Private other, System is something that we cannot influence at all, and, in fact, everything else that is not very categorical.



    If we talk about native libraries, then, let's say, you can take an empty application ... you can take the usual application that we have, and take dumpsys from it, see that it takes 146 megabytes. And if you go and cut something ... in particular, I took and cut out the two largest native libraries, which we have in total for 15 megabytes, and take the dumpsys after that, you can easily see that we have lost consumption from Native Heap and from the code. That is, with such a simple gesture, we saved about 35 megabytes. Not bad enough.



    Streams. Let's do the easiest test. There is an empty application, and there is the same empty application, where we take and make a loop that will do new Thread (), sleep in it for five seconds, and start this thread. You can see that in this case we have a big stack. That is, we can influence the whole thing, but indirectly, by reducing the number of threads in the Java code. That is, through Java objects, including influencing all of these locations that exist in other elements.



    If you continue to talk about threads, you can easily see what you have for threads, what they do in the application. There are different tools. You can use the same systrace, you can just find the id of your process with the console, and ps to understand what threads are in the system at all.

    If you do well and use all sorts of ThreadFactory to name your threads, then you can understand what you should have, what you should not have, and thus reduce the whole thing, because it is especially meaningless to have 100 threads in the application.

    What else can you do? There is a completely well-known set of bayan tips: watch for leaks, use pools, do not create objects when not needed, and all this nonsense, all this applies. Fine.

    There is a good relationship between how much your application consumes memory and how big it is. That is, the more your application, the more memory it will eat, so, in a simple way, reduce the weight of the APK, reduce the weight of .dex, reduce the .so, remove everything that can be removed.

    Memory reduction is incredibly poorly matched with optimization. Suppose we want to make the simplest case, when the user scrolls the tape of pictures, the pictures are downloaded from the network. What are we doing in this case so that the user sees the pictures faster? We in advance - while he has not yet scrolled up to these pictures - are starting to load the necessary pictures so that he does not see the gray squares. In this case, if we are talking about memory, then we will simply occupy the memory with these pictures. Therefore, memory and optimization are extremely poorly combined. In the sense of - optimization of this type.


    If you look at dumpsys, then it is good, it brings you a lot of interesting things, besides the memory consumption itself. For example, the number of your objects, such as the number of View, the number of WebView, the number of Assets, information on databases.


    In principle, the databases do not occupy much, but perhaps if you are critical to every kilobyte, you will have to use SharedPreference instead of SQLite in some cases, and perhaps this will help you.



    Also, for example, it is very pleasant to look at the selected Assets. In particular, the most popular thing to see is fonts. And these fonts occupy 1.5 megabytes. Again, when we fight for every megabyte, gentle design feelings are sent far in the first place. Forgive me colleagues.


    Unexpectedly the disgusting thing is WebView. If you create an empty activation, launch WebView, load a Yandex page, take dumpsys, it will be 100 megabytes, just right away. If you open, say, some pictures, skip a couple of times, then 300 megabytes is easy. Even though WebView is chrome, and the process is considered yours. So that.



    Google knows about it. They made a little hack. That is, they have a WebView, they have a Google application in which you can search for something. Search results are opened in WebView. Fine.



    And there is a Google Go app that is designed for Android Go devices. And here they open the output already natively, that is, they create some kind of layout to display this output, they display all this with native components. And everything that needs to be opened directly in the browser is not opened in its internal browser, but in Chrome Tabs, so that Chrome can deal with this matter. Such are the tricks.

    If we talk about the application directly under the Android Go, then what's so interesting? There are certain specifics how to develop an application for Android Go, and let's talk a little bit about it.


    There are several ways. Firstly, everything can be cool with you, the application can work well and you can give it to Android Go without any modifications. The same thing in case you write an alarm clock, a calculator. But more often than not, everything is difficult with you, so you need, let's say, to do some build flags, disable some modules, features at the compilation stage, something else to do. And in this way you will have two APKs, and you can load one as usual, the second - use the use-feature "low-memory", and it will be like two APKs.

    Or you can make two applications, that is, one application is normal, the other application is specially sharpened for Android Go. So does Google. In particular, they released Go versions of many of their applications.

    What is good, what is bad? The good thing is that you initially make a new application, and if you initially write something in it and exceed memory consumption, then you say: “Yeah, you did something wrong. Went to do fine. " And you do not need to immediately reduce this memory four times. In addition, it is good and it is easy, and the bad thing is that you need to make a new application, besides, it must be supported somehow, the same with features with the main application, and somehow it must be combined. This requires a huge pile of resources, and, in principle, it is not necessary to do this, if you are not Google, because if Google does not make such applications, then no one will do them. And now you don’t need to make such an application, because it requires a lot of resources, and it’s unclear whether Android Go really shoots well,

    In any case, a multi-modular approach will help you very well here, because you can easily disable modules, something else. It is very good.


    Link from the slide

    Another rather important point from the report that was on Google I / O: the guys correctly say that you shouldn't be afraid to turn off some of your features for such weak devices in runtime. Perhaps you have a very cool photo editor who can do anything, apply any filters, but it requires a lot of memory, and on a weak device it will work poorly. Therefore, do not be afraid to say that “Let's not launch a photo editor on your device, because you have a weak device. Buy normal. " So this is a good schedule. This is a graph of the dependence of the number of features on the memory consumed, and vice versa. And this can be followed, in principle.

    What else is worth considering? It is clear that the issue here is not only memory. Memory is the most difficult, the most interesting, and that with which we were busy 98% of the time. But it is clear that we need to think about the speed of work, start-up, battery consumption, etc. In addition, you can make some additional features for Android Go-applications. In particular, YouTube Go has the ability to download an application for offline viewing. This was not the case in the main YouTube application, and maybe not even now. I do not know. The fact is that devices with Android Go will be used in countries where, perhaps, not very good Internet - so let's download in advance. There are other good examples - Facebook Lite, Twitter Lite, and Yandex Browser Lite, because not everyone needs some fully functional features. Someone, maybe, needs only correspondence or only posts. There is no need to download a huge Facebook when you can put a little one. And all this should be for users, you should think and make sure that they feel good. In general, this is a cool challenge - to make such an application so that it works under Android Go.

    What else on the references? There is a cool answer to Stack Overflow, which got almost 1000 likes. You can help him dial more. The answer clarifies that in general with memory, what is responsible for what, what is in memory. And, in principle, this whole answer is for dumpsys.

    Whether many people know this or not, but Android can work with devices that have 512 megabytes of RAM, only starting from Android 4.4. There just appeared a special API for interacting with such devices, and this can help you.

    It could be finished here, it would be beautiful. And now about how it really was. In short - not so beautiful, not at all so beautiful.

    They came to me in May, they said: "We must do it." I said, "Good." Let's go figure it out. Read - all is well, except for the memory. Memory somewhere 170 megabytes. Great start. We must somehow reduce. What is the easiest to do? Take and disable all disconnected modules, see what happens, because you need to make sure that it somehow works. If there aren't any features there - well, okay. 105 megabytes left. It was painful.


    Therefore, a big refactoring went on. On the basis of the build-flag began the alteration of everything. We made densitySplit so that resource tables take up less, resConfig, left only Russian and English locales there. The cache of pictures is twisted, preloading is removed, all optimizations are removed - pain. ProGuard is wound up even harder. In particular, ProGuard has inlining, which allows reducing the number of methods, and in return, it is possible to sacrifice performance, including readability, of stacktraces.

    Further absolutely big refactoring went already. The transfer is even more into modules, so that all this can be turned off, so that it is beautiful and that the Android Go application can be assembled from our main application.

    After a week there is about 64 megabytes left. I think, “OK, I have to finish today.” It took 14 hours, time 4 in the morning, memory 60 megabytes. And I do not know what to do next. I'm rage. Moreover, it has become an irreversible refactoring. Therefore, the next morning a volitional decision was made - all this is not that, life is disgusting, I hate everything, and we make a special new branch, we start everything from scratch. We take and delete all the code, which, most likely, should not be in the Android Go, and fix compilation errors. 10 hours, APK with the remaining basic functionality is ready, tests pass, fine. The problem is that this thing is unsupported, we have it all.

    Thus, the best solution is the second application. However, few people will spend resources on it, while the real profit is not clear. Perhaps your story will not be so sad, because I have a little bit of effort left, I would have climbed, but I was already unbearable. But such is the story. On this relatively positive note, you can probably finish. Thanks to all.

    Also popular now: