artem-zinnatullin / TheContext-Podcast

Podcast about Android Development with Hannes Dorfmann, Artem Zinnatullin, Artur Dryomov and wonderful guests!
Apache License 2.0
624 stars 17 forks source link

Discussion Episode 7: React Native with Felipe Lima from Airbnb #55

Open sockeqwe opened 7 years ago

artem-zinnatullin commented 7 years ago

cc @felipecsl 📧

felipecsl commented 7 years ago

🎉

konmik commented 7 years ago

Thanks guys, it was very interesting!

hotchemi commented 7 years ago

Quite interesting episode! Are there any oss project that I can glance how to bridge react native and Android Java code?

felipecsl commented 7 years ago

@hotchemi check out the library https://github.com/airbnb/react-native-maps, it integrates Google Maps with React Native using Google Play Services

hotchemi commented 7 years ago

@felipecsl Awesome! I'll check it:D

sebaslogen commented 7 years ago

Great episode, thanks guys, I learned a lot of stuff about React Native 👍 One correction, at the beginning of the program Felipe said that Xamarin platform is like a webview without native widgets but as far as I understood from the main developer, it also uses only native widgets and every time Android publishes a new widget the Xamarin guys update the SDK to support it.

The part that is still not clicking for me is how threading works. It was briefly mentioned but I can't imagine how you perform in javascript the typical task of triggering a long running method (e.g. sorting or networking) that stays inside your javascript code and then you update the UI to reflect that a long process is happening (like loading). Typically the long process method is launched into another thread and then you immediately update the UI to not block the user and inform her that request was received. How is this handled in single threaded javascript?

sockeqwe commented 7 years ago

Thank you very much for your feedback. Indeed, Xamarin is not like a webview and yes, xamarin uses native widgets. As far as I know, Xamarin runs in its own Virutal Machine interpreting byte code (generated from C#). For android development that means, you have a "virtual machine" embedded in your apk (which themself runs on a java virtual machine). This is a similar concept to React Native, where you have a JavaScript Engine embedded in your apk.

Regarding JavaScript: It is a common mistake that JavaScript is single threaded. You are right that the JavaScript language per se doesn't have mulithreaded language concepts like Thread or async-await like other programming languages have. Nevertheless, you have functions like setTimeout(function, milliseconds) that setInterval(function, milliseconds) where you can schedule some things. This already gives a hint how a javascript engine works "under the hood". Ever wondered why Node.js is "non-blocking" and why especially in JavaScript the term "callback hell" is so popular?

If you search on Google for "JavaScript language" you will find such definitions:

JavaScript is a single Threaded non blocking asynchronous concurrent programming language

ovsswr

hold on, it really makes sense as you will see in a minute: A JavaScript engine uses an EventLoop (in the javascript engine in your Browser) to run JavaScript. The equivalent on Android would be Android's main looper. You can post some peace of code (as functions / callback) on the EventLoop with setTimeout(function, miliseconds) for instance. Androids equivalent would be handler.post(Runnable).

JavaScript's EventLoop and Android's main looper are basically working the same way: Think of a Queue of Runnables. The EventLoop always takes the first element from the queue and executes this "peace of code" / Runnable (Android Main Looper does the same). In JavaScript there is only one Thread taking the first element from this queue and the execute this "peace of code" (like a runnable). Once this peace of code is executed the next element from the queue will processed and so on. Therefore, yes, JavaScript is single threaded because there is only one thread executing these "peace of code" (only one at the time) from the Queue from the EventLoop. When the Queue is empty, the EventLoop is idle until a new "peace of code" has been put on the queue

The first "peace of code" put on the EventLoop queue is your main java script code included in your website (or ReactiveNative entry point). Each click event results in putting the callback function (which is a "peace of code") onto the queue. Hence non-blocking (you can put as many "peace of code" on the queue as you want, but that will not run immediately but once this "peace of code" is on the first position on the queue).

Last but not least asynchronous concurrent: So while its true that only one thread executes the topmost "peace of code" from the EventLoop Queue, many threads can put "peace of code" on the queue of the EventLoop. For example, making an ajax http request: The JavaScript engine uses a browser API ( which then runs in on its own thread owned by the Browser and not by the javascript engine) to start making a http call and the result will be put back on the Queue of the EventLoop and then eventually the ajax http request callback will be executed when it is the first element on the Queue. ReactNative works the same way, except the fact that instead of having a browser executing an async http call, React Native delegates the http call to OkHttp (or you could register your own http library to make this calls).

I hope that clarifies your question.

artem-zinnatullin commented 7 years ago

@sockeqwe I see what you did here, you're starting longblogposting! 😹

konmik commented 7 years ago

One of my co-workers (a web developer) listening about how we're solving different concurrency issues on Android often says: "no concurrency no problems". I should agree with him. We almost always can do things in one thread having less complicated code.

sebaslogen commented 7 years ago

@sockeqwe Excellent explanation 😍 One big limitation of React Native that comes from not having real concurrency is that it can't take advantage of all those fancy CPUs in our phones/PCs, or is there some extra piece that I'm missing?

skrugly commented 7 years ago

@sebaslogen YAGNI doing development on React Native, or you chose a bad technology to build concurrent programs

sebaslogen commented 7 years ago

To help complete the picture on multiplatform SDKs, there is also Flutter from Google that compiles Dart to native, it's also inspired by React and produces Android and iOS apps: https://flutter.io/widgets-intro/

@Adambl4 knowing the advantages and limitations of React Native helps me figure out for which kind of apps or features this could be the right tool.

sockeqwe commented 7 years ago

@artem-zinnatullin I. CANT. STOP. IT. 😄 @konmik that is so true! Cant agree more with you.

@sebaslogen As @Adambl4 already said, you only hardly ever will need it. However, with HTML5 Workers where introduced. Basically you could offload some work from your main javascript file (think main thread) to other javascript files that run on their own "thread". For react native this comes with the cost of some memory overhead.

https://www.facebook.com/notes/andy-street/react-native-scheduling/10153916310914590/
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers

Just a side note: Workers are not really used widely in web development because as @Adambl4 pointed out correctly, usually you dont need them because the way javascript works is enough and the fact that you have a powerful backend for "computational stuff".

Last but not least, you can bridge from React Native to native C or Java or Swift to make this computations outside of javascript.

Regarding CPU: This is a "implementation detail" and responsibility of the JavaScript engine. On a PC also the browser tries to make use of the resources like CPU as best as possible. As Felipe explained in the podcast, react native uses at least 3 Threads (UI Thread, Rendering / Layouting Thread and "javascript main thread"). But yes, you as developer have no api for resource management (like CPU etc). That should be done / managed properly by the Browser / JavaScript engine.

felipecsl commented 7 years ago

Wow excellent discussion, guys. Just learned a bunch of stuff! 😄

AAverin commented 7 years ago

I was a bit confused by the part where there is no RecyclerView concept made in ReactNative. Both iOS and Android have some way to recycle views, on android it have been since ListView and now with RecyclerView, iOS has it's own TableCell mechanism that works kinda the same. Almost all mobile apps use lists in one way or another. Is the list component in Airbnb app built with react or is kept native for now? @felipecsl how would you solve this problem? I'm about to start a list-heavy app development and no RecyclerView might be a killer

AAverin commented 7 years ago

In other articles about ReactNative I have seen many people mentioning issues like animations stutter and screens opening slow. Also, routing and navigation concept seems to be a bit complex in ReactNative. The Javascript Core in ReactNative is most likely written in C++, so it's kind of Javascript VM, right? Wouldn't then bridging to native components and passing data back and forth mean effectively passing data through JNI layer which is known to be slow? This is probably a reason why you can only pass simple types to React javascript from native. My question is, wouldn't bridging mean that the more bridges and native components you have the more slow and error prone your application becomes?

felipecsl commented 7 years ago

@AAverin on the Airbnb app specifically, we've been avoiding building scrolling-intensive UI with React Native exactly because of that problem (lack of view recycling). AFAIK there is no easy solution for that issue (yet). There is a new implementation for the Android layout engine released recently called "Android nodes" which is supposed to address some of the performance issues, but it's still experimental.

The Javascript Core in ReactNative is most likely written in C++, so it's kind of Javascript VM, right?

Correct.

Wouldn't then bridging to native components and passing data back and forth mean effectively passing data through JNI layer which is known to be slow?

This is done in a background thread. I don't know how slow/fast it is, but yeah, you have to pass it through the JNI layer.

This is probably a reason why you can only pass simple types to React javascript from native.

Correct. However, there are ways to work around that.

wouldn't bridging mean that the more bridges and native components you have the more slow and error prone your application becomes?

Since all RN bridging is done in the background, it doesn't impact the experience as much as you'd expect, but of course it will "always" be slower than building in pure native because there is an overhead involved. "Error prone" is also relative. If you have good automated test coverage (unit and integration) then you should be fine.

AAverin commented 7 years ago

@felipecsl Thanks for your answers. Does RN provide any means for unit-testing javascript code? Can we use jsUnit? Are there any mocking libraries or testing utilities? maybe something typescript can offer?

Also, Airbnb app in Play Store is >40Mb. How much of that is RN extra weight? How much of that is iOS part that is not really needed in Android APK? What are the chances to squeeze RN app into Instant Apps requirements for 3Mb? For comparison, our native app written in full Kotlin now is <10Mb, and we ship a lot of libraries that can be stripped for Instant App support

felipecsl commented 7 years ago

Does RN provide any means for unit-testing javascript code? Can we use jsUnit? Are there any mocking libraries or testing utilities? maybe something typescript can offer?

You can test your javascript like you would for any web application. Just pick your favorite unit testing lib. I like Jasmine, but any should do.

Also, Airbnb app in Play Store is >40Mb. How much of that is RN extra weight?

We have a lot of stuff in the app, tons of dependencies (some which also include native code). RN contributes to ~10mb extra, which could be quite a lot depending on your current app size.

How much of that is iOS part that is not really needed in Android APK?

None. All the RN code is shared between both platforms and we have very little platform specific checks in the JS code, so virtually all of it is shared by iOS and Android.

What are the chances to squeeze RN app into Instant Apps requirements for 3Mb?

I think getting it under 3Mb including RN is virtually impossible due to the JSC native binary size.

If app size is a concern for you, I'd definitely stay away from RN.