Some XXX you need to know before entering the pit

Time:2020-4-4

It’s the third year of flutter‘s career. In the past two years, I have written several applications such as calculator, flashlight and compass that are too small to be smaller. I have a little humble opinion about them. This article is just a bravado after the completion of the cool. It does not involve too much code, but just a simple chat about the children’s shoes that are ready to enter the pit or want to enter the pit Flutter, including its infrastructure, design principles, application scenarios and future development.

What is Flutter?

What is flutter? In the original words of the current official website of flutter:

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.

There are several key points in this sentence: “UI toolkit”, “natively compiled” and “single codebase”.

First of all, Google defines flutter as a UI toolkit, which is a UI oriented tool. Second, flutter can provide a nearly native effect. The most important thing is that it can use a piece of code for mobile, web and desktop, which is the dream function of almost all front-end developers. Imagine that you only need to write a piece of code to run it on the mobile side, Web page and desktop side, which is equivalent to directly connecting several popular front ends: Web, Android, IOS, Linux, MacOS and Windows! As of this article’s release, flutter has been updated to 1.7, and its performance on Android and IOS, the two major platforms of the mobile end, is very good. At present, the web end is also in the testing stage, and I believe that it can be released soon, while the desktop end can be seen in the near future.

The overall structure diagram of the official flutter is as follows:

It is mainly divided into three parts. The bottom platform specific is used for compatibility with different platform features. In the foreseeable future, the logic of this block will change with the number of supported platforms. Second, the engine in the middle, which is implemented by C / C + +, is mainly divided into two functions: one is used to communicate with the upper layer and the lower layer; the other is used to render graphics and images. Finally, the top-level framework, which is implemented by dart, is mainly some UI related encapsulation implementation. It’s almost impossible to say that dart related wheels are being built.

In fact, the design principle of flutter is not complicated, especially for children’s shoes that have done hand games or map classes and need to draw a lot of scenes, the principle of flutter is almost the same as those hand games. There is an option called “display layout boundary” in the developer options of Android Phones:

The so-called layout boundary is the boundary of each view in the Android application interface layout. First of all, we need to know that all views in Android are “rectangular”, even if it presents different images on the interface, such as a circle, a star. No matter what graphics these views display, they all have a rectangular boundary. You can think of Android UI as a wall, and these views are like picture frames hanging on the wall. Each picture frame is rectangular, although they are mounted with different pictures.

Open “display layout boundary”, you can see the “borders” of each view on the screen:

In most cases, we use this function to view the layout of the interface to find some layout problems. Similarly, we can use this function to view the current layout status of any application installed on the mobile phone, such as idle fish

However, you can’t always see the layout boundary of some application interfaces, such as the interface of Gaud map:

Some XXX you need to know before entering the pit

Even though there are many elements on the map interface, they are not stacked by many views like the free fish app above. Another chestnut, such as the game of King glory:

Besides the boundary of SystemUI, you can not see any boundary of View.

Here, Aigo refers to the above two types of applications as “drawing” applications. Why don’t these drawing applications use the view components provided by the system framework? Because the requirements are not consistent, neither with the technical requirements nor with the product requirements. Technically, you can try to add hundreds of buttons to a layout and update them frequently to see what the effect is; as for products, emmmmm You can imagine the effect of running Luban No. 1 sequence frame (U3D, of course) on the map interface with different angles set on the ImageView. Therefore, most of these applications with high requirements for drawing will use their own drawing methods to achieve the interface. The UI controls provided by platforms such as Android or IOS are essentially just a bunch of elements that can be drawn and packaged for reuse. For some games implemented by GL, there are a large number of encapsulated interface element components that can be directly transplanted. If we regard the interface displayed by a platform device as a canvas, then the elements on the interface are just the images on the canvas. If we provide a possibility to “tear” the canvas from one platform device and place it on another platform device, then we can at least realize cross platform from the perspective of users. As we said just now, UI controls provided by platforms like Android or IOS are essentially just a bunch of elements that are drawn and packaged for reuse. An ideal situation is to migrate one of these elements to the other party, so is it Android to IOS or IOS to Android? Obviously, neither of them is possible. Both of them have accumulated a lot of native logic on their own platforms, and they have a high degree of coupling with their own platforms. Forced implementation is no different from rewriting the entire UI level. If rewriting, then why should it be limited to two platforms and not compatible with more platforms?

One of the brilliant main ideas of software architecture is to extract commonness, which makes it possible to be designed. Take button controls of different platforms as an example, can we find common ground in button controls such as button (Android), uibutton (Apple), < button > (WEB)? The answer is of course, but it’s not realistic. If the difficulty coefficient of porting button extraction from one platform to another is 1, then the difficulty coefficient of porting to another platform will become 2. With the increase of different platforms, the difficulty coefficient will increase. What’s more, the UI layer is a variable layer, which may be modified in minutes, and the difficulty will increase exponentially. Therefore, we need a lower level of design. As mentioned above, the interface is like a canvas. We just need to transplant the whole canvas. As for what you want to draw on this canvas, you can do whatever you want. Therefore, we need to find commonalities at the lower level of each platform. At the software level, we can see different images in the display interface, mainly depending on the graphics rendering engine, such as the renderers on the browser, the IOS platform based on OpenGL’s CG, CA, CI framework, and the newly launched Skia and 3D parts drawn by 2D, which are being tapped make complaints about Metal, and Android on the Android platform. If we can use the same rendering engine on each platform, at least we can realize the unification on the canvas, so Google chose skia, which has always been used by Android. (PS: compared with “canvas”, the rendering engine here is not very accurate. The more accurate description of canvas should be the surface or layer container of each platform, but Aigo can’t find a noun bird that is easier for ordinary people to understand…)

In fact, in addition to its stable use of Android platform for more than ten years, there are also mainstream browsers that support skia. What’s more, skia is Google’s own son (although it was adopted by paying). So here we only need to use skia on IOS platform to achieve at least the common use of web, Android and IOS platforms. As for why OpenGL is not used directly, I think the main reason is that no matter which platform it is, it is mainly based on 2d rendering of ordinary applications, and skia’s 2d rendering is also very mature and stable, and it is directly used to realize 2 / 3D Although the drawing is also wide, it is unnecessary. Besides, the probability of falling in a small step is less than that of falling in a large step, isn’t it? In the future, flutter will not provide better application level support for GL. The official answer is as follows:

Today we don’t support for 3D via OpenGL ES or similar. We have long-term plans to expose an optimized 3D API, but right now we’re focused on 2D.

After finishing the canvas, all that remains is to pile up various elements, that is, the one above the flutter architecture. This is individual work. In view of the lack of space in this article, I will not talk about it.

The above is just a brief introduction to the overall design of flutter. In fact, in addition to the graphic rendering, there are many details to be implemented in flutter. But what you can see is that whether it’s design ideas or Google’s official description of it, flutter prefers a UI toolkit rather than a framework that can directly invade the native system. So we can simply say that flutter is a tool that can make cross platform UI.

Why is Flutter?

Why flutter? From the beginning of software development platform differentiation, the efforts of various gods on the cross platform have not stopped. Here, I divide the cross platform into two categories according to the implementation principle: the first category is to virtualize a running environment, such as Java running in the virtual machine; the second category is to use the ready-made cross platform environment for two times packaging, such as using browser Core to cooperate JavaScript encapsulated framework. The former not only can almost completely shield the influence of platform correlation and provide a unified interface to the outside world, but also is relatively close to the original in terms of operation efficiency. The latter makes the best use of the existing cross platform scheme. The disadvantage is that the implementation efficiency of the logic for different platforms is still very different, and in some cases, different interfaces need to be provided according to different platforms API, such as atom’s development framework, electronjs, will provide different API implementation logic for different platforms in its API documents:

Flutter combines the two. On the one hand, it needs to provide a virtualized environment for the execution of dart code; on the other hand, it directly uses a lower level graphical rendering engine scheme to directly implement UI logic. Although it does not provide different API interfaces for different platforms as naked as electronjs, the definition of its UI toolkit provides us great convenience to modify the platform native code. The organization of its project directory can well reflect this:

In Android studio, you can right-click the root directory or Android / IOS directory to directly open and modify the native Android or IOS project from the new Android studio window or Xcode:

Therefore, you can see that at this stage, Google’s definition of FLUENT is very clear: a UI toolkit is just a UI toolkit. A lot of kids will feel sorry to see this, but I think it’s a compromise and it’s just right. Why is it a compromise? In the case of electronjs or react native, does it make sense to call different APIs according to different platforms? Meaningless Why don’t I call the platform’s native API directly? Secondly, do you want to enter the native system directly? Android’s brother said, how can other platforms be so easy? I would rather believe that a sow will go to the tree than let cook or Nadra open the source code of the operating system to make adaptation for chopper or let cook or Nadra help with adaptation for flutter. Therefore, at this stage, we only let flutter as a UI toolkit to return other platform related logic to their respective platforms for implementation, which is perfect. What we can see at present is that Google is still making continuous efforts to promote the deep integration of flutter with its own Android or chrome. We believe that in the near future we can see better solutions for flutter.

Different from some other cross platform solutions, the near native rendering of flutter is more excellent. Taking the interface implemented in idle fish as an example, it is almost the same as the native rendering

Some XXX you need to know before entering the pit

To sum up, flutter is a compromise solution. As I said, it is just right for you to implement cross platform code to a certain extent.

How to use Flutter.

How to use flutter? Here, Aigo will not teach you how to develop flutter. After all, the official documents of flutter in both Chinese and English are very complete, and there are a lot of online courses from entry to abandonment. The “how to use” I’ve explained here is more likely to be used in what circumstances. To illustrate this problem, we need to know how the real experience effect of flutter is. This experience includes not only the performance and difficulty that developers care about, but also the first feeling for users. For this reason, I wrote two demo codes on three platforms, namely, flutter, Android and IOS, as a comparison:

  1. Timer for Android
  2. Timer for Flutter
  3. Timer for IOS
  4. Player for Android
  5. Player for Flutter
  6. Player for IOS

Timer is a timer, which modifies the default code when creating a flutter project. This code implements the logic that one click of a button increases the count once:

The reason for using as like as two peas is that we want to control less variable factors. Since Flutter has a default, we just need to make a Demo in the same way as Android and IOS platform. So we can maximize the performance of Flutter and will not cause any decrease due to my blind modification. However, in order to simplify the operation, Aigo modified it to click the button once, and then the number was accumulated every 100 milliseconds until the number was 100. The theoretical process would take 100ms x 100 = 10000ms = 10s:

Some XXX you need to know before entering the pit

This is just the default project code_MyHomePageStateClass_incrementCounterMethod implementation logic is modified to stream periodically setstate:

void _incrementCounter() {
//    setState(() {
//      _counter++;
//    });
  Stream<int>.periodic(Duration(milliseconds: 100), (x) => x + 1)
      .take(100)
      .forEach((int count) async {
    setState(() {
      _counter = count;
    });
  });
}

It should be noted that in order to shield the feather like impact brought by the platform API logic, the periodic implementation here does not use the timer API under their respective platforms, but uses the lower level stream in the flutter, and uses the thread under their respective platforms in Android and IOS:

var tick = 0
Thread(Runnable {
    while (tick < 100) {
        tick++
        [email protected] { count.text = tick.toString() }
        Thread.sleep(100)
    }
}).start()
@objc func createThread() {
    Thread(target: self, selector: #selector(doCount), object: nil).start()
}

@objc func doCount(){
    for i in 1 ... 100 {
        DispatchQueue.main.async {
            self.count.text = i.description
        }
        usleep(100000)
    }
}

Finally, dart distinguishes different modes. In debug mode, code like assert will have a certain impact on the execution efficiency. Therefore, for Android platform projects, Aigo will generate release packages for testing, while IOS Because I’m poor, I can’t afford 99 yuan, and I don’t want to toss over three platforms like XX Xia, so IOS platform uses debug mode as a reference for Android platform.

The other player is a player, because timer mainly tests the default project of flutter compared with Android and IOS platforms. In order to further enlarge the performance test, demo player is introduced to play a 4K video and see the performance difference between the three platforms. Similarly, in order to reduce the impact of each platform, I used its own playback framework on all three platforms, including the video player used on the flutter, the avplayer used on the IOS, and the video view used on Android.

In terms of the device, I found two old machines with similar features: an iPhone 6S plus and a Google pixel. The former accompanied me to fight for two or three years and still in service, while the latter was just starting to acquire new opportunities.

OK! Everything is ready. Now we start to generate various packages. For IOS platform, we run and install them directly to mobile phones. For Android platform, we release them as official packages. For the official package release of shuttle for Android, we refer to the official document build and release for Android.

It should be noted that the first release package of flutter build Android platform will be very time-consuming, which is still far from the native second build. This is mainly because flutter is a tripartite dependent build, which requires more time:

First let’s look at timer:

From left to right, they are Android native, flutter for Android, IOS native and flutter for IOS (if there is no special description, the following illustrations are arranged in this order). Although flutter provides us with a flutter performance to monitor performance:

However, the tool can not work on Android and IOS native platforms, lacking of contrast. Because the essence of flutter is still Android or IOS platform application after final compilation and operation, here, Aigo directly uses profiler and Xcode instruments related tools provided by Android to measure the performance, mainly to simply record the CPU and internal memory consumption of application running under each platform.

First of all, IOS platform, including a native IOS timer and a shuttle for IOS timer, will emerge. In order to shield the performance impact of just started applications, the test will be started after the application is started, waiting for a period of time when the CPU and memory become stable:

First of all, let’s look at the rendering performance of native and flutter on iPhone. The above image is directly generated by cutting the arrangement from the recording screen, without any post correction processing. By default, the default background color of flutter is slightly magenta. Aigo guesses that the default flutter project uses a material’s blue main color:

primarySwatch: Colors.blue

To be more precise, it’s sky blue (see the color of the title bar and Fab), that is, the part near green in the color ring. Maybe the designers of material think that giving the background a white department with complementary colors can enhance the visual impact (because Aigo likes to design my application UI like this, so guess so). So the default flutter The background color of the project is a light magenta, which may be a little bit off the pants for developers to fart, but in fact, in the eyes of designers, any slight change of color value can affect the user’s subjective experience, which is the same. But if you don’t think it’s necessary, you can change the background to white:

backgroundColor: Colors.white

However, I find it puzzling that, even so, the “white” rendered by flutter is still a little bit more gray than the IOS native:

Maybe it’s because the color space is different, or it’s because the rendering engine of flutter doesn’t agree with the iPhone equipped with the pro son Ca / CG rendering framework In addition, even in idle state, the flutter needs much more memory consumption than the native IOS, and 13 threads will be started on the iPhone by default. Click the Fab button to start counting:

In the 10s or so, the CPU usage of flutter was once close to 40%, while IOS native was only close to 3%. In terms of memory, IOS native was almost unchanged, while flutter consumed nearly 20 m more memory. But fortunately, thanks to dart’s excellent asynchronous framework design, stream will not generate new threads in this process. However, in the end, because IOS generated by flutter is not a formal package, it will have a performance impact on this, so we only use it as a reference on IOS platform, focusing on the comparison with the performance of Android platform:

It’s gratifying that there is no color difference on Android platform as on IOS platform. The rendering performance of the two platforms is the same, probably because skia is used:

Some XXX you need to know before entering the pit

In terms of performance, Android’s native CPU utilization fluctuates greatly, reaching 15% at the highest level, while flutter is relatively excellent, with a peak of no more than 10%; in terms of memory usage, flutter consumes about twice as much as native CPU. We also mentioned flutter above After all, as a tripartite dependent component, the problem of memory consumption may not have a good solution for a long time.

This is the end of timer demo test. We can see that the performance of flutter on Android platform is almost the same as that of native platform. In IOS, although there is no money to open a large apple member, the experience given to me in debug mode is at least the same as that of native IOS, but in terms of performance, emmmmmmm Wait for me to recharge the apple members to make up another wave of releases. But no matter IOS or Android platform, the memory consumption of applications generated by flutter is much higher than that of native applications. As I said, this may exist for a long time, so you need to make some choices when introducing flutter to a formal project. On the flutter platform, Aigo does not use DART’s real sense of thread isolate to achieve the counting function, because DART’s asynchronous framework prefers future and stream to create isolate directly. Finally, let’s take a look at the performance of 4K playback.

This kind of aigeba uses LG to promote the short film used for 4K display. The total size of the short film is about 250MB, and the duration is less than 2min. We take 50s of the short film to compare the performance under 4K playback.

First, Android platform:

Thanks to the exoplayer, the experience of flutter in playing is quite excellent. Android native video view, which is used directly, is slightly stuck at the beginning of playing, and the subsequent performance is just as good. In terms of performance, flutter is still an old problem. Both CPU and memory are very high. Android native because mediaplayer in videoview relies on the call to the underlying playerdriver, the display of CPU and memory usage may be biased. This article will not discuss it. In any case, in terms of the user experience, the fluency of the flutter application is no different from that of the native application, even due to the native application.

Finally, let’s experience the IOS platform:

summary

In general, after two years of polishing, flutter, the “UI toolkit” launched by Google, has done quite well in its functions. But there are also the following problems:

First of all, the application to the production environment is not broad enough. Based on the current performance of flutter, it is fully competent for most UI level scenarios, but it is not suitable to use flutter to implement frequent interaction with platform related features. Although flutter provides channels to interact with native code, it is not convenient at present, so if you plan to use flutter As for your production environment, Aigo prefers to use the mixed development scheme like idle fish, that is, keep using the native development for the important or more interactive interface with platform features, and use the fluent development for the secondary pure UI rendering interface. In addition, games, especially those that need 3D rendering, are not supported and do not need to use flutter.

Secondly, as we have seen above, the main performance difference between the native and the native is memory consumption. Compared with the native, the native will use more or even more memory temporarily. Therefore, if your application is sensitive to this block, such as the application developed for low-end one and other devices, it is not suitable to use the native. Furthermore, as a three-way component, the increase of packet size by flutter is inevitable:

The above is the comparison of package size generated by Android native and flutter. Even if the so of different CPU architectures is removed, the package size is bound to be larger than native. Of course, in today’s mobile phone where ROM is only tens of gigabytes, this increase is not significant.

Finally, there is the ecological problem, which consists of two parts. One is the technology ecology. Flutter does not have as many mature framework components as the native platform, which will hinder the development efficiency. The second is product ecology. At present, there are few mature products developed with flutter, which leads to the fact that some advertising revenue SDKs that developers rely on for survival hardly provide direct support for flutter.