SlideShare a Scribd company logo
React Native Android
2018.2.9 DroidKaigi 2018
Yukiya Nakagawa / @Nkzn
Room2 12:50-13:40
#droidkaigi_room2
Who are you?
• Yukiya Nakagawa / @Nkzn
• @
•
• Android 2009
• React Native v0.17
React Native
•
• Hello World
• Production
Target
• React Native 

Android
• Android React Native
Best Match
•
• JavaScript
• React Native
React Native Androidはなぜ動くのか
Agenda
Overview
React Native Androidはなぜ動くのか
React
React is
React
2017
React
https://facebook.github.io/react/
•
• JSX
•
Reactive
JSX
<div>
<Header />
<LeftPane />
<RightPane
name={myName}/>
</div>
React Native Androidはなぜ動くのか
React
import React from 'react';
import ReactDOM from 'react-dom';
const styles = {
container: {display: 'flex', flexDirection: ‘row', …}
};
class App extends React.Component {
render() {
const myName = /* props or state */;
return (
<div style={styles.container}>
<Header />
<LeftPane />
<RightPane
name={myName} />
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
Virtual DOM
<ccc
style={{
width: 200,
color: "red"
}}
enabled={true} />
React
VirtualDOM
DOM
React
VirtualDOM
DOM
React
VirtualDOM
DOM
React
VirtualDOM
DOM
https://tylermcginnis.com/an-introduction-to-life-cycle-events-in-react-js/
https://developer.android.com/reference/android/app/Activity.html
React is
• Facebook JS
• https://facebook.github.io/react/
•
•
• View
View View
React Native
React
import React from 'react';
import ReactDOM from 'react-dom';
const styles = {
container: {display: 'flex', flexDirection: ‘row', …}
};
class App extends React.Component {
render() {
const myName = /* props or state */;
return (
<div style={styles.container}>
<Header />
<LeftPane />
<RightPane
name={myName} />
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
import ReactDOM from 'react-dom';
div
div
ReactDOM.render(
<App />,
document.getElementById('app')
);
import React from "react";
import { View, AppRegistry } from "react-native";
const styles = {
container: {display: 'flex', flexDirection: ‘row', …}
};
class App extends React.Component {
render() {
const myName = /* props or state */;
return (
<View style={styles.container}>
<Header />
<LeftPane />
<RightPane
name={myName} />
</View>
);
}
}
AppRegistry.registerComponent(
"MyReactNativeApp",
() => App
);
React
ReactDOM
React
UI
React Native
React is not
React
• React DOM
• React View Web
• React Native
• React View UI
React
• View props
•
•
• DOM
• UI
•
• View
• UI
GUI
Virtual DOM
React
React DOM 

React is not
• React
• React View
• React View
React Native Android
React Native Androidはなぜ動くのか
React Native
React Native
• JavaScript
• JS Java
• React Android View
•
Activity
ReactRootView
(FrameLayout) JS
Activity ReactRootView JS
Activity ReactRootView JS
Activity ReactRootView JS
JavaScript
Activity ReactRootView JS
JS
• JavaScriptCore
• WebKit(Safari) JS
• Facebook Android NDK


https://github.com/facebook/android-jsc
• ReactRootView God FrameLayout
• ReactInstanceManager JS
• CatalystInstance JNI
• ReactInstanceManager
• JSC
• JS
• CatalystInstance
• JNI Java
• Java JSC IF
startReactApplication()
JS
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(
mReactInstanceManager,
"MyReactNativeApp",
null);
setContentView(mReactRootView);
}
Java
Activity RN
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(
mReactInstanceManager,
"MyReactNativeApp",
null);
setContentView(mReactRootView);
}
Java
ReactRootView FrameLayout
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(
mReactInstanceManager,
"MyReactNativeApp",
null);
setContentView(mReactRootView);
}
Java
ReactInstanceManager
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(
mReactInstanceManager,
"MyReactNativeApp",
null);
setContentView(mReactRootView);
}
Java
RootView InstanceManager
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(
mReactInstanceManager,
"MyReactNativeApp",
null);
setContentView(mReactRootView);
}
Java
setContentView
import React from "react";
import { View, AppRegistry } from "react-native";
const styles = {
container: {display: 'flex', flexDirection: ‘row', …}
};
class App extends React.Component {
render() {
const myName = /* props or state */;
return (
<View style={styles.container}>
<Header />
<LeftPane />
<RightPane
name={myName} />
</View>
);
}
}
AppRegistry.registerComponent(
"MyReactNativeApp",
() => App
);
MyReactNativeApp
ReactInstance
• React JS Java
• React
React
JSC JS
ReactInstanceManager
↑
View
ReactRootView
ReactRootView#startReactApplication()
Java
JavaScript
NDK
JS Java
Activity ReactRootView JS
React Native Androidはなぜ動くのか
Native Modules
Native Modules
• Java Swift
JS
• RN Android Java JS
•
Native Modules
3
const ToastAndroid = require('ToastAndroid');
ToastAndroid.show(" ",
ToastAndroid.SHORT);
JS Toast
JS
@ReactModule(name = "ToastAndroid")
public class ToastModule
extends ReactContextBaseJavaModule {
@ReactMethod
public void show(final String message,
final int duration) {
UiThreadUtil.runOnUiThread(() -> {
Toast.makeText(
getReactApplicationContext(),
message,
duration).show();
});
}
}
Toast
Java
@ReactModule(name = "ToastAndroid")
public class ToastModule
extends ReactContextBaseJavaModule {
@ReactMethod
public void show(final String message,
final int duration) {
UiThreadUtil.runOnUiThread(() -> {
Toast.makeText(
getReactApplicationContext(),
message,
duration).show();
});
}
}
ReactContextBaseJavaModule
Java
@ReactModule(name = "ToastAndroid")
public class ToastModule
extends ReactContextBaseJavaModule {
@ReactMethod
public void show(final String message,
final int duration) {
UiThreadUtil.runOnUiThread(() -> {
Toast.makeText(
getReactApplicationContext(),
message,
duration).show();
});
}
}
Native Module
Java
※name
@ReactModule(name = "ToastAndroid")
public class ToastModule
extends ReactContextBaseJavaModule {
@ReactMethod
public void show(final String message,
final int duration) {
UiThreadUtil.runOnUiThread(() -> {
Toast.makeText(
getReactApplicationContext(),
message,
duration).show();
});
}
}
JS @ReactMethod
Java
@ReactModule(name = "ToastAndroid")
public class ToastModule
extends ReactContextBaseJavaModule {
@ReactMethod
public void show(final String message,
final int duration) {
UiThreadUtil.runOnUiThread(() -> {
Toast.makeText(
getReactApplicationContext(),
message,
duration).show();
});
}
}
Android not UI Thread
Java
React Native Androidはなぜ動くのか
ReactInstanceManager
JS
public class ToastPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(
new ToastModule(reactContext));
}
}
ReactPackage
Java
public class ToastPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(
new ToastModule(reactContext));
}
}
ReactPackage
Java
public class ToastPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(
new ToastModule(reactContext));
}
}
createNativeModules
Java
ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.addPackage(new ToastPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
ReactInstanceManager
Java
ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.addPackage(new ToastPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
Java
ReactInstanceManager
JSC
JS
const ToastAndroid = require('ToastAndroid');
ToastAndroid.show(" ",
ToastAndroid.SHORT);
JavaScript
JS
Great!
@ReactMethod
Readable/Writable Map/Array
• JS
Java/C++
+ hasKey(name: String): boolean
+ isNull(name: String): boolean
+ getBoolean(name: String): boolean
+ getDouble(name: String): double
+ getInt(name: String): int
+ getString(name: String): String
+ getArray(name: String): ReadableArray
+ getMap(name: String): ReadableMap
+ getDynamic(name: String): Dynamic
+ getType(name: String): ReadableType
+ keySetIterator(): ReadableMapKeySetIterator
+ toHashMap(): HashMap<String, Object>
+ putNull(key: String)
+ putBoolean(key: String, value: boolean)
+ putDouble(key: String, value: double)
+ putInt(key: String, value: int)
+ putString(key: String, value: String)
+ putArray(key: String, value: WritableArray)
+ putMap(key: String, value: WritableMap)
+ merge(source: ReadableMap);
+ size(index: int): int
+ isNull(index: int): boolean
+ getBoolean(index: int): boolean
+ getDouble(index: int): double
+ getInt(index: int): int
+ getString(index: int): String
+ getArray(index: int): ReadableArray
+ getMap(index: int): ReadableMap
+ getDynamic(index: int): Dynamic
+ getType(index: int): ReadableType
+ toArrayList(index: int): ArrayList<Object>
+ pushNull()
+ pushBoolean(value: boolean)
+ pushDouble(value: double)
+ pushInt(value: int)
+ pushString(value: String)
+ pushArray(array: WritableArray)
+ pushMap(map: WritableMap)
JSC++Java
{
name: "nkzn",
age: 30
}
[
"cupcake",
"donut",
"eclair"
]
Promise
Promise
• ReactMethod Promise
JS Promise
•
promise.resolve() promise.reject()
JS
@ReactMethod
public void getString(Promise promise) {
try {
ClipboardManager clipboard = getClipboardService();
ClipData clipData = clipboard.getPrimaryClip();
if (clipData == null) {
promise.resolve("");
} else if (clipData.getItemCount() >= 1) {
ClipData.Item firstItem = clipboard
.getPrimaryClip()
.getItemAt(0);
promise.resolve("" + firstItem.getText());
} else {
promise.resolve("");
}
} catch (Exception e) {
promise.reject(e);
}
}
Clipboard#getString
Java
@ReactMethod
public void getString(Promise promise) {
try {
ClipboardManager clipboard = getClipboardService();
ClipData clipData = clipboard.getPrimaryClip();
if (clipData == null) {
promise.resolve("");
} else if (clipData.getItemCount() >= 1) {
ClipData.Item firstItem = clipboard
.getPrimaryClip()
.getItemAt(0);
promise.resolve("" + firstItem.getText());
} else {
promise.resolve("");
}
} catch (Exception e) {
promise.reject(e);
}
}
JS
RxJava
onNext, onError
※ Clipboard
Java
// Promise Style
Clipboard.getString()
.then(str => {
// use string
});
// async/await Style
async function getClip() {
const str = await Clipboard.getString();
return str;
}
JS
JS
Java JS
EventEmitter
Android Broadcast
// JS
class RCTDeviceEventEmitter extends EventEmitter {
emit(eventType) {...}
addListener(eventType, listener, context) {...}
removeAllListeners(eventType) {...}
removeSubscription(subscription) {...}
}
JS
RCTDeviceEventEmitter.js
※ emit EventEmitter
// interface
// ReactInstanceModule
public interface RCTDeviceEventEmitter
extends JavaScriptModule {
void emit(String eventName,
@Nullable Object data);
}
Java
DeviceEventManagerModule.java
// JS
public void emitHardwareBackPressed() {
getReactApplicationContext()
.getJSModule(RCTDeviceEventEmitter.class)
.emit("hardwareBackPress", null);
}
JS emit
Java
DeviceEventManagerModule.java
C++
JS→Java ReactModule
Java→JS JavaScriptModule
ReactModule
React
🤔
React Android 

View
Activity ReactRootView JS
React DOM
Android
Android View
React
Native UI Components
3
public class PhotoView extends ImageView {
public PhotoView(Context context) { ... }
// Android
}
Java
public class PhotoViewManager
extends SimpleViewManager<PhotoView> {
@Override
public String getName() {
return "PhotoView";
}
@Override
protected PhotoView createViewInstance(
ThemedReactContext reactContext) {
return new PhotoView(reactContext);
}
@ReactProp(name = "uri")
public void setUri(PhotoView view,
@Nullable String uri) {
view.setUri(uri);
}
}
React
Java
public class PhotoViewManager
extends SimpleViewManager<PhotoView> {
@Override
public String getName() {
return "PhotoView";
}
@Override
protected PhotoView createViewInstance(
ThemedReactContext reactContext) {
return new PhotoView(reactContext);
}
@ReactProp(name = "uri")
public void setUri(PhotoView view,
@Nullable String uri) {
view.setUri(uri);
}
}
SimpleViewManager
Java
public class PhotoViewManager
extends SimpleViewManager<PhotoView> {
@Override
public String getName() {
return "PhotoView";
}
@Override
protected PhotoView createViewInstance(
ThemedReactContext reactContext) {
return new PhotoView(reactContext);
}
@ReactProp(name = "uri")
public void setUri(PhotoView view,
@Nullable String uri) {
view.setUri(uri);
}
}
React
Java
public class PhotoViewManager
extends SimpleViewManager<PhotoView> {
@Override
public String getName() {
return "PhotoView";
}
@Override
protected PhotoView createViewInstance(
ThemedReactContext reactContext) {
return new PhotoView(reactContext);
}
@ReactProp(name = "uri")
public void setUri(PhotoView view,
@Nullable String uri) {
view.setUri(uri);
}
}
Android View
Java
public class PhotoViewManager
extends SimpleViewManager<PhotoView> {
@Override
public String getName() {
return "PhotoView";
}
@Override
protected PhotoView createViewInstance(
ThemedReactContext reactContext) {
return new PhotoView(reactContext);
}
@ReactProp(name = "uri")
public void setUri(PhotoView view,
@Nullable String uri) {
view.setUri(uri);
}
}
props
Java
//
@ReactProp(name = "uri")
public void setUri(PhotoView view,
@Nullable String uri) {
view.setUri(uri);
}
//
<PhotoView
uri="http://example.com/hoge.png" />
Java
JS
React Native Androidはなぜ動くのか
public class MyLibraryPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new PhotoViewManager()
);
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
ReactPackage
Java
public class MyLibraryPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new PhotoViewManager()
);
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
createViewManagers
Java
ReactInstanceManager
Native Modules
import PropTypes from “prop-types”;
import {
requireNativeComponent,
View
} from "react-native";
const name = "PhotoView";
const photoViewInterface = {
name: name,
displayName: name,
propTypes: {
...View.propTypes,
uri: PropTypes.string.isRequired
}
};
export default
requireNativeComponent(name, photoViewInterface);
JS PhotoView.js
JS
Java
Java
import PhotoView from "./PhotoView";
//
<PhotoView
uri="http://example.com/hoge.png" />
JS
React Native Androidはなぜ動くのか
• JS UI
props interface
•
ViewManager
React Android View
React Native Androidはなぜ動くのか
Native Module
UIManagerModule
UIManagerModule
• React
React Native React DOM
Java
• createView updateView
ReactMethod
@ReactMethod
public void createView(
int tag,
String className,
int rootViewTag,
ReadableMap props) {
mUIImplementation.createView(
tag, className, rootViewTag, props);
}
@ReactMethod
public void updateView(
int tag,
String className,
ReadableMap props) {
mUIImplementation.updateView(
tag, className, props);
}
UIManagerModule.java
React
ReactMethod
View
• React View Java
ReactShadowNode Java View
Yoga
• UIManagerModule React Java
View
• View View
Yoga
https://facebook.github.io/yoga/
• Flexbox
Yoga Android
• borderColor
ViewManager Android
setBorderColor
React
• Java C++ JS
• ReactInstanceManager
• ReactInstanceManager Native
Module JavaScript Module
• UI Native Module
React
Native Modules
Android
• Android
APK
•
APK
Application Sources
import
$buildDir/intermediates
/assets/release /res/merged/release
iOS
require("path/to/awesome.png")
iOS
React Native Androidはなぜ動くのか
React Native Android
• API View Android
•
• JS JS
• Android SDK iOS SDK
• Web
• JS
React Native Androidはなぜ動くのか
• React Native


http://tomoima525.hatenablog.com/entry/2017/12/19/180523
• Android 

7 

https://peaks.cc/Nkzn/architecture_patterns
@tomoaki_imai
React Native Androidはなぜ動くのか

More Related Content

React Native Androidはなぜ動くのか