React Native 初探

React Native是啥宙刘?

是一款用JavaScriptScript編寫原生(Android贯底,iOS)應用的框架碉渡。

原理是啥岗屏?

總體來看默蚌,整套React Native框架分為三層冻晤,如下圖所示:


image.png
  • Java層:該層主要提供了Android的UI渲染器UIManager(將JavaScript映射成Android Widget)以及一些其他的功能組件(例如:Fresco、Okhttp)等绸吸。
  • C++層:該層主要完成了Java與JavaScript的通信以及執(zhí)行JavaScript代碼兩件工作鼻弧。JSCore设江,即JavaScriptCore,JS解析的核心部分攘轩,IOS使用的是內(nèi)置的JavaScriptCore叉存,Androis上使用的是 https://webkit.org 家的jsc.so
  • JavaScript層:該層提供了各種供開發(fā)者使用的組件以及一些工具庫度帮。

通訊機制

關于整個RN的通信機制歼捏,可以用一句話來概括:

JNI作為C++與Java的橋梁,JSC作為C++與JavaScript的橋梁笨篷,而C++最終連接了Java與JavaScript瞳秽。
RN應用通信橋結構圖如下所示:

image

Java 調(diào)用 JS
image

JS 調(diào)用 Java
image

啟動流程

image

JavaScript層組件渲染

image

從上圖我們可以很容易看出,Java層的組件渲染分為以下幾步:

  1. JS層通過C++層把創(chuàng)建View的請求發(fā)送給Java層的UIManagerModule率翅。
  2. UIManagerModule通過UIImplentation對操作請求進行包裝练俐。
  3. 包裝后的操作請求被發(fā)送到View處理隊列UIViewOperationQueue隊列中等待處理。
  4. 實際處理View時冕臭,根據(jù)class name查詢對應的ViewNManager腺晾,然后調(diào)用原生View的方法對View進行相應的操作。

用RN寫APP是怎樣的一種體驗

Hello World快速體驗

下載安裝Node浴韭,依次執(zhí)行(讀條)下面幾行代碼

npm install -g create-react-native-app
create-react-native-app AwesomeProject
cd AwesomeProject
npm start

順利執(zhí)行完后顯示如下結果丘喻,


image

根據(jù)提示可以通過掃碼在手機上看到代碼運行效果
項目目錄結構

image

React Native使用JSX寫項目的。
JSX is a syntax extension to JavaScript.
Introducing JSX

Basic

Hello World


image

布局


image

列表
image

網(wǎng)絡請求


網(wǎng)絡請求

自定義控件

image

RN Android混合開發(fā)

環(huán)境版本
react-native-cli: 2.0.1
react-native: 0.57.1

項目初始化

choco install -y nodejs.install python2 jdk8
npm install -g react-native-cli
1. Install Android Studio
2. Install the Android SDK
3. Configure the ANDROID_HOME environment variable
Preparing the Android device
react-native init AwesomeProject
cd AwesomeProject
react-native run-android

從Android主入口到JS主入口

JS 工程目錄結構

image

App.js

import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';

const instructions = Platform.select({
    ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
    android:
    'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

type
Props = {};
export default class App extends Component<Props> {
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>Welcome to React Native!</Text>
                <Text style={styles.instructions}>To get started, edit App.js</Text>
                <Text style={styles.instructions}>{instructions}</Text>
            </View>
        );
    }
}

app.json

{
  "name": "AwesomeProject",
  "displayName": "AwesomeProject"
}

index.js

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);

JS 工程中的Android 工程結構

MainApplication.java

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage()
      );
    }

    @Override
    protected String getJSMainModuleName() {
      return "index";
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
  }
}

MainActivity.java

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "AwesomeProject";
    }
}

ReactActivity.java

public abstract class ReactActivity extends Activity
    implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mDelegate.onCreate(savedInstanceState);
  }

  @Override
  protected void onPause() {
    super.onPause();
    mDelegate.onPause();
  }

  @Override
  protected void onResume() {
    super.onResume();
    mDelegate.onResume();
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    mDelegate.onDestroy();
  }

  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    mDelegate.onActivityResult(requestCode, resultCode, data);
  }

  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    return mDelegate.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
  }

ReactActivityDelegate.java

  protected void onCreate(Bundle savedInstanceState) {
    loadApp(mMainComponentName);
  }

  protected void loadApp(String appKey) {
    mReactRootView = createRootView();
    mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      appKey,
      getLaunchOptions());
    getPlainActivity().setContentView(mReactRootView);
  }
  
  protected ReactRootView createRootView() {
    return new ReactRootView(getContext());
  }

ReactNativeHost.java

public abstract class ReactNativeHost {
  protected ReactInstanceManager createReactInstanceManager() {
    ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
    ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
      .setApplication(mApplication)
      .setJSMainModulePath(getJSMainModuleName())
      .setUseDeveloperSupport(getUseDeveloperSupport())
      .setRedBoxHandler(getRedBoxHandler())
      .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
      .setJSIModulesPackage(getJSIModulePackage())
      .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);

    for (ReactPackage reactPackage : getPackages()) {
      builder.addPackage(reactPackage);
    }

    String jsBundleFile = getJSBundleFile();
    if (jsBundleFile != null) {
      builder.setJSBundleFile(jsBundleFile);
    } else {
      builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
    }
    ReactInstanceManager reactInstanceManager = builder.build();
    ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_END);
    return reactInstanceManager;
  }

Android的 生命周期事件 是如何分發(fā)到 JS的世界 中的

ReactActivityDelegate.java

  protected void onPause() {
    getReactNativeHost().getReactInstanceManager().onHostPause(getPlainActivity());
  }

  protected void onResume() {
    getReactNativeHost().getReactInstanceManager().onHostResume(getPlainActivity(), (DefaultHardwareBackBtnHandler) getPlainActivity());
  }

  protected void onDestroy() {
    getReactNativeHost().getReactInstanceManager().onHostDestroy(getPlainActivity());
  }

ReactInstanceManager.java

  public void onHostPause() {
    moveToBeforeResumeLifecycleState();
  }
  
  private synchronized void moveToBeforeResumeLifecycleState() {
    currentContext.onHostPause();
  }

ReactContext.java

  public void onHostPause() {
    for (LifecycleEventListener listener : mLifecycleEventListeners) {
      listener.onHostPause();
    }
  }

LifecycleEventListener


image

Android 調(diào) RN

1. Java 發(fā)送事件

RCTDeviceEventEmitter.java

...
private void sendEvent(ReactContext reactContext,
                       String eventName,
                       @Nullable WritableMap params) {
  reactContext
      .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
      .emit(eventName, params);
}
...
WritableMap params = Arguments.createMap();
...
sendEvent(reactContext, "keyboardWillShow", params);

2. JavaScript 接受事件

import { DeviceEventEmitter } from 'react-native';
...

var ScrollResponderMixin = {
  mixins: [Subscribable.Mixin],


  componentWillMount: function() {
    ...
    this.addListenerOn(DeviceEventEmitter,
                       'keyboardWillShow',
                       this.scrollResponderKeyboardWillShow);
    ...
  },
  scrollResponderKeyboardWillShow:function(e: Event) {
    this.keyboardWillOpenTo = e;
    this.props.onKeyboardWillShow && this.props.onKeyboardWillShow(e);
  },

RN 調(diào) Android

1. 聲明JavaModule

  • extends ReactContextBaseJavaModule
  • getName()
  • @ReactMethod
public class ToastModule extends ReactContextBaseJavaModule {

  private static final String DURATION_SHORT_KEY = "SHORT";
  private static final String DURATION_LONG_KEY = "LONG";

  public ToastModule(ReactApplicationContext reactContext) {
    super(reactContext);
  }
  
  @Override
  public String getName() {
    return "ToastExample";
  }
  
  @ReactMethod
  public void show(String message, int duration) {
    Toast.makeText(getReactApplicationContext(), message, duration).show();
  }
}

2. 注冊JavaModule

CustomToastPackage.java

public class CustomToastPackage implements ReactPackage {

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }

  @Override
  public List<NativeModule> createNativeModules(
                              ReactApplicationContext reactContext) {
    List<NativeModule> modules = new ArrayList<>();

    modules.add(new ToastModule(reactContext));

    return modules;
  }

}

MainApplication.java

// MainApplication.java

...
import com.your-app-name.CustomToastPackage; // <-- Add this line with your package name.
...

protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new CustomToastPackage()); // <-- Add this line with your package name.
}

3. 在JavaScript中使用JavaModule

Wrap the native module in a JavaScript module

/**
 * This exposes the native ToastExample module as a JS module. This has a
 * function 'show' which takes the following parameters:
 *
 * 1. String message: A string with the text to toast
 * 2. int duration: The duration of the toast. May be ToastExample.SHORT or
 *    ToastExample.LONG
 */
import {NativeModules} from 'react-native';
module.exports = NativeModules.ToastExample;

Use the module

import ToastExample from './ToastExample';

ToastExample.show('Awesome', ToastExample.SHORT);

參考資料

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末念颈,一起剝皮案震驚了整個濱河市泉粉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌榴芳,老刑警劉巖嗡靡,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異窟感,居然都是意外死亡讨彼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門柿祈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哈误,“玉大人,你說我怎么就攤上這事躏嚎∶圩裕” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵卢佣,是天一觀的道長重荠。 經(jīng)常有香客問我,道長虚茶,這世上最難降的妖魔是什么戈鲁? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任仇参,我火速辦了婚禮,結果婚禮上婆殿,老公的妹妹穿的比我還像新娘诈乒。我一直安慰自己,他們只是感情好鸣皂,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布抓谴。 她就那樣靜靜地躺著,像睡著了一般寞缝。 火紅的嫁衣襯著肌膚如雪癌压。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天荆陆,我揣著相機與錄音滩届,去河邊找鬼。 笑死被啼,一個胖子當著我的面吹牛帜消,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播浓体,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼泡挺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了命浴?” 一聲冷哼從身側響起娄猫,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎生闲,沒想到半個月后媳溺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡碍讯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年悬蔽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捉兴。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝎困,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出倍啥,到底是詐尸還是另有隱情禾乘,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布逗栽,位于F島的核電站,受9級特大地震影響失暂,放射性物質發(fā)生泄漏彼宠。R本人自食惡果不足惜鳄虱,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凭峡。 院中可真熱鬧拙已,春花似錦、人聲如沸摧冀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽索昂。三九已至建车,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間椒惨,已是汗流浹背缤至。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留康谆,地道東北人领斥。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像沃暗,于是被迫代替她去往敵國和親月洛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 用兩張圖告訴你孽锥,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料嚼黔? 從這篇文章中你...
    hw1212閱讀 12,728評論 2 59
  • React Native是啥? 是一款用JavaScriptScript編寫原生(Android忱叭,iOS)應用的框...
    詹徐照閱讀 1,011評論 0 47
  • React Native學習<一> 認識Recat Native 博客原文:http://www.jianshu....
    AFinalStone閱讀 2,670評論 0 12
  • 或許放棄是另一種開始隔崎,是另一種努力,我放棄了去當幼師韵丑。但是我給自己定的目標是拿下普通話證爵卒,和教師資格證。只要你有能...
    方圓愛紅樓夢閱讀 340評論 0 0
  • 無以名狀的快樂與悲傷 無以名狀的快樂與悲傷—— 棉花糖樂隊 忽然想聽這首歌撵彻,想起遙遠的山城的生活钓株,在那里呆的漫長有...
    salvadoer閱讀 207評論 0 0