[ReactNative]Android原生源码解读(用Markdown写PPT)

尝试了用Markdown写PPT,阅读效果更佳,欢迎品尝:http://www.vmfor.com/md/2019-01-10ed7ba094-ae05-42c7-a998-8b3d5d31f46c.md

[slide style="background-image:url('/img/4.jpg')"]

Android-RN 应用程序

混合开发

热更新

麦文昌 2019-01

[slide style="background-color:#31456A"]

前言

  • 原生APP在性能方面具有优势,而ReactNative更加灵活 {:&.fadeIn}
  • 我们将逐渐把APP往原生方面过渡,包括第三方库以及重要的逻辑和界面。
  • Native层提供React原生模块以供React层使用,包括API,UI和后台任务。

[slide style="background-color:#31456A"]

目录


  • ReactNative启动流程 {:&.fadeIn}
  • ReactNative混合开发
  • Native模块
  • 热更新
  • 启动优化
  • bundle文件安全
  • 总结

[slide style="background-color:#31456A"]

ReactNative启动流程


{:&.zoomIn}

    AppRegistry.registerComponent('cassecapp', () => cassecapp);

[slide style="background-color:#31456A"]

ReactNative启动流程

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

    @Override
    protected String getJSBundleFile() {//“index.android.bundle”文件的存储路径
        return CodePush.getJSBundleFile();
    }

    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List getPackages() {
      return Arrays.asList(
        new MainReactPackage(),
        new CodePush(BuildConfig.CODEPUSH_KEY, getApplicationContext(), BuildConfig.DEBUG, "https://codepush.cassmall.com/"),
        ...
      );
    }
  };

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

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
    GrowingIO.startWithConfiguration(this, new Configuration()
        .useID()
        .trackAllFragments()
        .setChannel("测试")
    );
    CrashReport.initCrashReport(getApplicationContext(), "b8b801654b", true);
  }
}

[slide style="background-color:#31456A"]

ReactNative启动流程


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 "cassecapp";
    }

  ...
}

[slide style="background-color:#31456A"]

ReactNative启动流程

package com.facebook.react;
public abstract class ReactActivity extends Activity
    implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {

  private final ReactActivityDelegate mDelegate;

  protected ReactActivity() {
    mDelegate = createReactActivityDelegate();
  }

  ...

  /**
   * Called at construction time, override if you have a custom delegate implementation.
   */
  protected ReactActivityDelegate createReactActivityDelegate() {
    return new ReactActivityDelegate(this, getMainComponentName());
  }

  @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);
  }
  ...
}

[slide style="background-color:#31456A"]

ReactNative启动流程

package com.facebook.react
/**
 * Delegate class for {@link ReactActivity} and {@link ReactFragmentActivity}. You can subclass this
 * to provide custom implementations for e.g. {@link #getReactNativeHost()}, if your Application
 * class doesn't implement {@link ReactApplication}.
 */
public class ReactActivityDelegate {

  ...

  private final @Nullable Activity mActivity;
  private final @Nullable FragmentActivity mFragmentActivity;
  private final @Nullable String mMainComponentName;

  private @Nullable ReactRootView mReactRootView;

  public ReactActivityDelegate(Activity activity, @Nullable String mainComponentName) {
    mActivity = activity;
    mMainComponentName = mainComponentName;
    mFragmentActivity = null;
  }

  protected ReactRootView createRootView() {
    return new ReactRootView(getContext());
  }

  protected ReactNativeHost getReactNativeHost() {
    return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
  }

  public ReactInstanceManager getReactInstanceManager() {
    return getReactNativeHost().getReactInstanceManager();
  }

  protected void onCreate(Bundle savedInstanceState) {
    if (mMainComponentName != null) {
      loadApp(mMainComponentName);
    }
    mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
  }

  protected void loadApp(String appKey) {
    if (mReactRootView != null) {
      throw new IllegalStateException("Cannot loadApp while app is already running.");
    }
    mReactRootView = createRootView();//ReactRootView: 加载React视图的容器
    mReactRootView.startReactApplication(
      //ReactInstanceManager: 负责加载JS,并且管理原生与JS通信
      getReactNativeHost().getReactInstanceManager(),
      appKey,//“cassecapp”
      getLaunchOptions());//加载界面时的一些设置
    getPlainActivity().setContentView(mReactRootView);
  }

  ...
}

[slide style="background-color:#31456A"]

ReactNative启动流程

package com.facebook.react;
/**
 * Simple class that holds an instance of {@link ReactInstanceManager}. This can be used in your
 * {@link Application class} (see {@link ReactApplication}), or as a static field.
 */
public abstract class ReactNativeHost {

  private final Application mApplication;
  private @Nullable ReactInstanceManager mReactInstanceManager;

  protected ReactNativeHost(Application application) {
    mApplication = application;
  }

  /**
   * Get the current {@link ReactInstanceManager} instance, or create one.
   */
  public ReactInstanceManager getReactInstanceManager() {
    if (mReactInstanceManager == null) {
      ReactMarker.logMarker(ReactMarkerConstants.GET_REACT_INSTANCE_MANAGER_START);
      mReactInstanceManager = createReactInstanceManager();
      ReactMarker.logMarker(ReactMarkerConstants.GET_REACT_INSTANCE_MANAGER_END);
    }
    return mReactInstanceManager;
  }

  protected ReactInstanceManager createReactInstanceManager() {
    ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
    ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
      .setApplication(mApplication)//必要
      .setJSMainModulePath(getJSMainModuleName())//必要,对应index.js
      .setUseDeveloperSupport(getUseDeveloperSupport()//必要
      .setRedBoxHandler(getRedBoxHandler())
      .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
      .setUIImplementationProvider(getUIImplementationProvider())
      .setJSIModulesPackage(getJSIModulePackage())
      .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);//必要,声明加载时机

    for (ReactPackage reactPackage : getPackages()) {
      builder.addPackage(reactPackage);//提供给RN交互的原生模块
    }

    String jsBundleFile = getJSBundleFile();
    if (jsBundleFile != null) {
      // 外部存储目录下的index.android.bundle文件
      builder.setJSBundleFile(jsBundleFile);
    } else {
      // assets/index.android.bundle
      builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
    }
    ReactInstanceManager reactInstanceManager = builder.build();
    ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_END);
    return reactInstanceManager;
  }

}

[slide style="background-color:#31456A"]

ReactNative启动流程


{:&.zoomIn}

ReactRootView {:.highlight}

ReactInstanceManager {:.highlight}

[slide style="background-color:#31456A"]

ReactNative混合开发


如何在一个在原生界面上显示React组件?

.

.

[slide style="background-color:#31456A"]

ReactNative混合开发


{:&.zoomIn}

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

export default class HelloWorld extends React.Component {
  render() {
    return (
      
        Hello, World
      
    );
  }
}
// .../index.js
AppRegistry.registerComponent('HelloWorld', () => HelloWorld);
AppRegistry.registerComponent('RNOnLayout', () => RNOnLayout);
AppRegistry.registerComponent('ExternalBundle', () => ExternalBundle);

[slide style="background-color:#31456A"]

ReactNative混合开发

public class HelloWorldActivity extends Activity {

    private ReactInstanceManager mReactInstanceManager;
    private ReactRootView mReactRootView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")//assets 文件目录下的bundle文件
                .setJSMainModulePath("index")//对应index.js
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        //  "HelloWorld" 对应index.js中的:
        // AppRegistry.registerComponent('HelloWorld', () => HelloWorld);
        mReactRootView.startReactApplication(mReactInstanceManager, "HelloWorld", null);

        setContentView(mReactRootView);
    }
}

[slide style="width:400"]

ReactNative混合开发

.

.

[slide style="background-color:#31456A"]

ReactNative混合开发




    

    

[slide style="background-color:#31456A"]

ReactNative混合开发

public class NativeReactActivity extends BaseReactActivity {

    private ReactInstanceManager mReactInstanceManager;
    private ReactRootView mReactRootView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_react_test);
        //找到布局中的ReactRootView
        mReactRootView = findViewById(R.id.react_view);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle") //assets 文件目录下的bundle文件
                .setJSMainModulePath("index")  //对应index.js
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        mReactRootView.startReactApplication(mReactInstanceManager, "RNOnLayout", null);
    }
}

[slide style="background-color:#31456A"]

ReactNative混合开发

.

.

[slide style="background-color:#31456A"]

ReactNative混合开发

public class ExternalReactActivity extends BaseReactActivity {

    private ReactInstanceManager mReactInstanceManager;
    private ReactRootView mReactRootView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_react_test);
        //找到布局中的ReactRootView
        mReactRootView = findViewById(R.id.react_view);
        //获取bundle文件路径:在外部存储空间"test"文件夹下
        String path = Environment.getExternalStorageDirectory() + "/test/index.android.bundle";
        //初始化ReactInstanceManager
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                // .setBundleAssetName("index.android.bundle")
                .setJSMainModulePath("index") //对应index.js
                .addPackage(new MainReactPackage())//原生模块
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setJSBundleFile(path) //设置文件路径
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        //"ExternalBundle" 对应js中 :AppRegistry.registerComponent('ExternalBundle', () => ExternalBundle);
        mReactRootView.startReactApplication(mReactInstanceManager, "ExternalBundle", null);
    }
}

[slide style="background-color:#31456A"]

ReactNative混合开发

.

.

[slide style="background-color:#31456A"]

Native原生模块

.

.

[slide style="background-color:#31456A"]

Native原生模块(UI)

/**
* 1.创建一个ViewManager类
* 例子: 实现一个图片显示器
*/
public class RCTImageManager extends SimpleViewManager {

    private static final String RC_IMAGE_VIEW = "RCImageView";

     /**
     * 2. 实现getName方法,返回该组件的名字
     */
    @Override
    public String getName() {
        return RC_IMAGE_VIEW;
    }

    /**
     * 3. 实现createViewInstance方法,创建视图,并初始化为默认的状态
     */
    @Override
    protected RCImageView createViewInstance(ThemedReactContext reactContext) {
        return new RCImageView(reactContext, Fresco.newDraweeControllerBuilder(), null, null);
    }

  /*
  * 4. 通过@ReactProp(或@ReactPropGroup)注解来导出属性的设置方法。
  */

    //url属性 src = {'https://www.baidu.com/img/bd_logo1.png'}
    @ReactProp(name = "src")
    public void setSrc(ReactImageView view, @Nullable String sources) {
        WritableMap map = Arguments.createMap();
        map.putString("uri", sources);
        WritableArray array = Arguments.createArray();
        array.pushMap(map);
        //转换成ReadableArray传给View
        view.setSource(array);
    }

    @ReactProp(name = "borderRadius", defaultFloat = 0f)
    public void setBorderRadius(ReactImageView view, float borderRadius) {
        view.setBorderRadius(borderRadius);
    }

    @ReactProp(name = ViewProps.RESIZE_MODE)
    public void setResizeMode(ReactImageView view, @Nullable String resizeMode) {
        view.setScaleType(ImageResizeMode.toScaleType(resizeMode));
    }

    /**
     * 注册回调事件
     */
    @javax.annotation.Nullable
    @Override
    public Map getExportedCustomBubblingEventTypeConstants() {
        return MapBuilder.builder()
                //把topChange注册到系统中
                .put("topChange",
                        MapBuilder.of(
                                "phasedRegistrationNames",
                                MapBuilder.of("bubbled", "onChange")))//onChange是暴露给 R 层的接口
                .build();
    }

}

[slide style="background-color:#31456A"]

Native原生模块(UI)

参数对照表

|Java | JavaScript|
:-------|:-------|:------
布尔型|Boolean | Bool
整形|Integer | Number
双精度浮点型|Double | Number
浮点型|Float | Number
字符串|String | String
回调|Callback | function
映射|ReadableMap | Object
数组|ReadableArray | Array

[slide style="background-color:#31456A"]

Native原生模块(UI)

@Override
public class NativePackage implements ReactPackage {
    /**
     * 5.在ReactPackage中注册ViewManager
     */
    @Override
    public List createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.asList(
                new RCTImageManager() //原生图片显示UI
        );
    }
}

[slide style="background-color:#31456A"]

Native原生模块(UI)

//6.实现对应的JavaScript模块
class NativeUIExample extends Component {

  static navigationOptions = {
    title: 'NativeUIExample',
  };

  _onButtonPress(){
      alert("onButtonPress");
  }

  render() {
    return (
      
        
      
    );
  }
  ...
}

[slide style="background-color:#31456A"]

Native原生模块(UI)

.

.

[slide style="background-color:#31456A"]

Native原生模块(API)

原生模块给React层提的供API:

  • 原生的第三方库API {:&.fadeIn}
  • 可复用的原生代码
  • 高性能的、多线程的原生代码、譬如图片处理、数据库、或者各种高级扩展等等

[slide style="background-color:#31456A"]

Native原生模块(API)

/*
* 1. 创建一个新的Java类继承ReactContextBaseJavaModule,并命名为ToastModule.java
* 例子: 实现一个吐司模块
*/
public class ToastModule extends ReactContextBaseJavaModule {

  private static final String TOAST_MODULE = "ToastExample";

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

    public ToastModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    //2. 实现getName方法,返回该模块的名字
    @Override
    public String getName() {
        return TOAST_MODULE;
    }

    //3. 实现getConstants方法,返回需要导出给JavaScript使用的常量
    @Nullable
    @Override
    public Map getConstants() {
        Map constants = new HashMap<>();
        constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
        constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
        return constants;
    }

    //4. 使用注解@ReactMethod导出一个方法给JavaScript使用
    @ReactMethod(isBlockingSynchronousMethod = false)
    public void show(String message, int duration) {
        Toast.makeText(getReactApplicationContext(), message, duration).show();
    }
}

[slide style="background-color:#31456A"]

Native原生模块(API)

public class NativePackage implements ReactPackage {

    /**
     * 5.在ReactPackage中注册ToastModule模块
     */
    @Override
    public List createNativeModules(ReactApplicationContext reactContext) {
        return Arrays.asList(
                new ToastModule(reactContext),//吐司模块
    }
}

[slide style="background-color:#31456A"]

Native原生模块(API)


{:&.fadeIn}

// ToastExample.js
// 通常我们都会把原生模块封装成一个 JavaScript 模块
import { NativeModules } from "react-native";

module.exports = NativeModules.ToastExample;
// 在JavaScript代码中可以这样调用:
import ToastExample from "./ToastExample";

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

[slide style="background-color:#31456A"]

Native原生模块(API)

其他特性

  • 回调函数:com.facebook.react.bridge.Callback
  • Promises: 原生模块还可以使用Promise来简化代码
  • 事件发射器:RCTDeviceEventEmitter
  • 生命周期监听:ActivityEventListener&LifecycleEventListener

[slide style="background-color:#31456A"]

Native原生模块(Task)

/*
* 1.创建一个类继承HeadlessJsTaskService
* 例子: 模拟网络状态监听
*/
public class OnNetworkChangeTaskService extends HeadlessJsTaskService {

    /**
     * 2.配置任务
     */
    @Nullable
    @Override
    protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
        if (intent==null)return null;
        Bundle extras = intent.getExtras();
        if (extras == null) return  null;
        HeadlessJsTaskConfig taskConfig = new HeadlessJsTaskConfig(
                "NetworkChangeTask", //任务名称
                Arguments.fromBundle(extras), // 任务参数
                5000, //任务超时时间
                true // 是否允许在前台运行,默认false
        );
        return taskConfig;
    }
}

[slide style="background-color:#31456A"]

Native原生模块(Task)

/*
* 3.在APP切换到后台的时候开启后台任务监听网络状态,
* 并且在网络发生改变时执行JavaScript任务;
*/

Intent serviceIntent = new Intent(context, OnNetworkChangeTaskService.class);
serviceIntent.putExtra("hasInternet", hasInternet);
context.startService(serviceIntent);

[slide style="background-color:#31456A"]

Native原生模块(Task)

// 5. 创建执行任务的NetworkChangeTask.js文件
module.exports = async (taskData) => {
    if (taskData["hasInternet"]) {
        console.log('网络可用')
    }else{
        console.log('网络不可用')
    }
};
/*
*5.在index.js中引用创建执行任务的NetworkChangeTask.js文件;
*  并注册`NetworkChangeTask`任务
*/
import NetworkChangeTask from './NetworkChangeTask';

AppRegistry.registerHeadlessTask("NetworkChangeTask", () => require("NetworkChangeTask"));

[slide style="background-color:#31456A"]

Native原生模块(Task)

module.exports = async (taskData) => {
    if (taskData["hasInternet"]) {
        console.log('网络可用')
    }else{
        console.log('网络不可用')
    }
};

[slide style="background-color:#31456A"]

热更新

ReactNative热更新原理:

  1. 版本更新时,从服务器重新拉取bundle文件和图片资源到本地; {:&.fadeIn}
  2. 在setJSBundleFile()方法中返回新的bundle文件路径;
  3. ReactInstanceManager加载新的bundle文件。

[slide style="background-color:#31456A"]

热更新 第三方库

react-native-pushy

ReactNative中文网推出的代码热更新服务:

  1. 每个应用每个月不超过10000次下载; {:&.fadeIn}
  2. 基于bsdiff算法创建的超小更新包,通常版本迭代后在1-10KB之间,避免数百KB的流量消耗;
  3. 支持崩溃回滚,安全可靠;
  4. 开放API,提供更高扩展性;
  5. 跨越多个版本进行更新时,只需要下载一个更新包,不需要逐版本依次更新。

[slide style="background-color:#31456A"]

热更新 第三方库

react-native-code-push

微软推出的代码热更新服务:

  1. 支持版本回滚; {:&.fadeIn}
  2. 支持图片增量更新;
  3. 官方服务器在国外,国内访问受限,建议自建服务;
  4. 没有开放后台源码。

[slide style="background-color:#31456A"]

热更新

搭建自己的热更新服务

  1. 打包bundle文件和图片文件,并上传服务器; {:&.fadeIn}
  2. 使用bsdiff对比两个版本的bundle文件得到补丁文件;
  3. 检查更新,下载补丁文件和图片;
  4. 使用bspatch对文件打补丁;
  5. 增量更新bundle文件和图片文件(Bsdiff工具);
  6. ReactInstanceManager空闲时重新加载bundle文件(或重启app时生效)。

[slide style="background-color:#31456A"]

启动优化

jsBundle文件预加载

  1. ReactRootView.startReactApplication() {:&.fadeIn}
  2. ReactInstanceManager.recreateReactContextInBackground()
  3. ReactInstanceManager.runCreateReactContextOnNewThread()
public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle initialProperties) {
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "startReactApplication");
    try {
      ...
      if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
      //后台创建ReactContext(加载jsBundle)
        mReactInstanceManager.createReactContextInBackground();
      }
      attachToReactInstanceManager();
    } finally {
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    }
  }

[slide style="background-color:#31456A"]

启动优化

jsBundle文件预加载

private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
    ...
  //开启子线程
    mCreateReactContextThread =
        new Thread((Runnable)()-> {
                ...
                try {
                  Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
                  final ReactApplicationContext reactApplicationContext =
                      createReactContext(
                          //加载jsBundle
                          initParams.getJsExecutorFactory().create(),
                          initParams.getJsBundleLoader());
                  ...

                  Runnable setupReactContextRunnable = new Runnable() {()->{
                      ...
                      //在主线程设置ReactRootView
                      setupReactContext(reactApplicationContext);
                      ...
                  }};
                  reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable);
                  UiThreadUtil.runOnUiThread(maybeRecreateReactContextRunnable);
                } catch (Exception e) {
                  mDevSupportManager.handleException(e);
                }
              }
            });
    ReactMarker.logMarker(REACT_CONTEXT_THREAD_START);
    mCreateReactContextThread.start();
}

[slide style="background-color:#31456A"]

启动优化

  • 全局单例的ReactInstanceManager {:&.fadeIn}
  • ReactInstanceManager的创建时机

[slide style="background-color:#31456A"]

启动优化

  • 预加载ReactRootView {:&.fadeIn}
  • 缓存并复用ReactRootView

[slide style="background-color:#31456A"]

bundle文件安全

裸奔的jsBundle

  1. 网络接口安全。 {:&.fadeIn}

  2. jsBundle防篡改。为防止篡改js入侵app业务,需对jsBundle做签名校验,
    一是下载文件后校验其完整性,二是每次加载jsBundle时校验。

  3. jsBundle业务安全。因为jsBundle是明文,所以业务中需要进行加解密等
    敏感措施就不能在js层实现,应该在native层实现加解密暴露给js接口调用。

[slide style="background-color:#31456A"]

总结

  • ReactNative启动流程 {:&.fadeIn}
  • ReactNative混合开发
  • Native模块
  • 热更新
  • 启动优化
  • bundle文件安全

[slide style="background-color:#31456A"]


End

「Talk is cheap. Show me the code」

你可能感兴趣的:([ReactNative]Android原生源码解读(用Markdown写PPT))