React Native实现多业务热部署

刚好最近在研究APP接入第三方业务功能的需求,本文总结一下Android平台如何使用React Native实现多业务接入并能满足各个子业务独立更新维护的要求。


一、场景分析

比如我有一个APP(暂叫主APP),现在需要接入第三方的业务(业务A、业务B、业务C等…)。但是主APP跟第三方业务是完全独立的,我们不希望主APP中嵌入大量的第三方业务代码(当然第三方也不会愿意暴露代码给主APP~),也不希望子业务更新时太依赖主APP。那怎么办呢?现在主流的做法一般是使用H5页面跳转,但是这种体验较差。除了H5之外我们想到过的能解决问题并且体验还不错的方案就是React Native、Weex、LuaView、Cordova等框架。子业务只需要把自身热更新相关的代码和需要使用Native实现的代码打包成一个很小的SDK(aar)移交给主程序集成,剩下的业务页面展示功能全部通过在线更新加载来实现。React Native、Weex、LuaView、Cordova等框架更新资源包的原理大同小异,大概分如下几步:

  1. 携带本地资源包的版本号等信息调用接口
  2. 后台根据前端传过来的版本信息告诉前端是否有升级包
  3. 有升级包则下载升级包保存到SD卡上(增量升级包或全量升级包下载)
  4. 下次启动APP时读取新的资源包

后面有时间再记录下详细的热更新实现方案,这里主要记录下载多个资源包的问题。前面也说了,我们要实现接入多个业务的需求,这就存在一个问题:APP中存在多个业务入口,如何实现进入不同业务模块时下载并加载不同的业务数据呢?比如:进入业务模块A时更新业务A的资源包并且加载A的数据,进入业务模块B时更新业务B的资源包并加载B的数据。
React Native是使用getJSBundleFile方法来指定加载JSBundle路径的,但是0.29版本开始发生了变化。0.29之前getJSBundleFile方法在ReactActivity类中,这个时候要实现上面的需求比较简单,只需要在各个业务的主Activity中集成ReactActivity并重写getJSBundleFile方法即可。但是0.29及以后的版本中ReactActivity中移除了getJSBundleFile方法,该方法在ReactNativeHost中,并且是在ReactApplication中初始化,也就是说一个应用只能初始化一个JSBundle路径,那怎么办呢?

二、解决思路

我们先来看看React Native加载Bundle的大概流程:

1.ReactActivity类

加载页面是从自定义的Activity开始,该Activity需要继承ReactActivity,里面有2个很重要的方法getMainComponentName和createReactActivityDelegate。

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

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

getMainComponentName是用来指定加载的React Native组件的名称,需要在自定义Activity中重写改方法,指定组件名称,该名称需要和index.js中注册的组件名称一致。

import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('组件名称', () => App);

2.ReactActivityDelegate类

createReactActivityDelegate方法是用来创建ReactActivityDelegate对象,ReactActivityDelegate类中有一个获取前面提到过的ReactApplication中的ReactNativeHost对象。

/**
   * Get the {@link ReactNativeHost} used by this app. By default, assumes
   * {@link Activity#getApplication()} is an instance of {@link ReactApplication} and calls
   * {@link ReactApplication#getReactNativeHost()}. Override this method if your application class
   * does not implement {@code ReactApplication} or you simply have a different mechanism for
   * storing a {@code ReactNativeHost}, e.g. as a static field somewhere.
   */
  protected ReactNativeHost getReactNativeHost() {
    return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
  }

从代码可以看到,app中使用的ReactNativeHost对象只有一份,也就是说默认情况下载Application中就已经定义好了JSBundle的加载路径,而且一次只能设置一个路径,更别说要实现不同子业务加载不同的JSBundle了。那我要实现不同子业务加载不同的JSBundle怎么办呢?我们可以自定义ReactActivityDelegate类,并且在自定义的Activity中重写父类中的createReactActivityDelegate方法,使用我们自定义的ReactActivityDelegate类。自定义ReactActivityDelegate有两种方法,一种方法是直接继承ReactActivityDelegate类,另一种方法是仿照ReactActivityDelegate类重写一个,但是后面那种方法个人觉得把整个类移植可能存在不可预测的风险。那就只能继承ReactActivityDelegate写一个了,那问题有来了,大家发现getReactNativeHost方法是protected类型的,直接继承的话不能重写该方法,我们可以变通一下,java中在相同的包名下其他类是可以访问protected方法的,所以我们可以在建一个和ReactActivityDelegate一样的包名,把自定义的类写在该包名下即可。

三、代码实现

1.环境

node.js:6.11.1
npm:6.1.0
react-native-cli:2.0.1
react-native:0.55.4

2.主APP

主APP主要是用来模拟实现个子业务入口,集成React Native基础框架和个子业务提供的SDK(aar)。

1) build.gradle

配置React Native环境,引入第三方业务的SDK

dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile "com.android.support:appcompat-v7:23.0.1"
    compile "com.facebook.react:react-native:+"  // From node_modules
    compile(name:'modelone',ext:'aar')
    compile(name:'modeltwo',ext:'aar')
    compile(name:'modelthree',ext:'aar')
}

2)MainApplication类

该类集成ReactApplication,主要用来初始化so动态库以及定义ReactNativeHost对象,指定主JSBundle文件路径。

package com.luoxudong.app.rndynamicload;

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;

import java.util.Arrays;
import java.util.List;

import javax.annotation.Nullable;

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

    @Nullable
    @Override
    protected String getJSBundleFile() {
      return super.getJSBundleFile();
    }
  };

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

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

3.子业务SDK

我业务集成到主APP时,需要提供提供一个SDK,该SDK的作用是提供业务主入口供主APP调用,实现自身业务JSBundle等资源的升级更新以及封装Native端的代码(如:敏感数据处理,自定义插件等)。
1) 自定义ReactActivityDelegate类

该类是实现多业务热更新接入的关键,集成ReactActivityDelegate并重写getReactNativeHost方法。以其中一个子业务为例:

package com.facebook.react;

import android.app.Activity;
import android.support.v4.app.FragmentActivity;

import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

import javax.annotation.Nullable;

/**
 * Created by luoxudong on 2018/7/2.
 */

public class MOReactActivityDelegate extends ReactActivityDelegate {
    private Activity mActivity = null;

    public MOReactActivityDelegate(Activity activity, @Nullable String mainComponentName) {
        super(activity, mainComponentName);
        mActivity = activity;
    }

    public MOReactActivityDelegate(FragmentActivity fragmentActivity, @Nullable String mainComponentName) {
        super(fragmentActivity, mainComponentName);
        mActivity = fragmentActivity;
    }

    @Override
    protected ReactNativeHost getReactNativeHost() {
        return new ReactNativeHost(mActivity.getApplication()) {
            @Override
            public boolean getUseDeveloperSupport() {
                return BuildConfig.DEBUG;
            }

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

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

            @Nullable
            @Override
            protected String getJSBundleFile() {
                /**
                 * 为了简单,这个路径先写死
                 * 实际开发中这个地址一般是不写死的,路径可能会变化,比如不同版本的bundle资源放在不同的目录中。
                 * 由于是写死在SD卡路径,记得6.0+系统要授权访问。
                 */
                String jsBundeFile = "/sdcard/rn/modelone/index.android.bundle";
                return jsBundeFile;
            }
        };
    }
}

在getJSBundleFile方法中指定加载JSBundle文件的路径,其中index.android.bundle是生成的bundle的js文件。实际开发过程中这里需要结合动态更新的流程来,这里简单处理先写死路径。要生成bundle文件,可以先在package.json中修改如下配置:

"scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest",
    "android-bundle": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output bundle/android/index.android.bundle --assets-dest bundle/android/assets",
    "ios-bundle": "react-native bundle --platform ios --dev false --entry-file index.js --bundle-output bundle/ios/index.android.bundle --assets-dest bundle/ios/assets"
  },

配置完成以后需要手动创建 bundle/android和bundle/ios目录,然后在控制台运行npm run android-bundle即可。

2)子业务主界面
ReactNativeActivity为业务的主界面,继承ReactActivity并且实现getMainComponentName方法,返回js中的组件名称,名称需要跟index.js中的组件名称一致。另外一个很重要的工作就是重写createReactActivityDelegate方法,使用自定义的MOReactActivityDelegate对象,这样程序进入该界面时就会加载在业务的资源,于主程序及其他业务完全独立。注意,实际开发啊过程中一般来说这个Activity不是子业务的入口,子业务的入口应该是实现热更新相关的业务逻辑,热更新检测处理完以后再进入该页面。

package com.luoxudong.app.modelone;

import com.facebook.react.MOReactActivityDelegate;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;

/**
 * Created by luoxudong on 2018/7/4.
 */

public class ReactNativeActivity extends ReactActivity {
    @Override
    protected String getMainComponentName() {
        /**
         * 这个组件名称需要跟js入口中的组件名称保持一致
         */
        String componentName = "modelone";
        return componentName;
    }

    @Override
    protected ReactActivityDelegate createReactActivityDelegate() {
        /**
         * 进入这个界面时会初始化加载bundle的路径。
         * 这里的demo是实现把bundle文件放在了sd卡中,没有写动态更新升级相关业务代码,
         * 实际项目开发过程中需要考虑更新机制,可能事前需要做一些更新资源文件等前置工作
         */
        ReactActivityDelegate delegate = new MOReactActivityDelegate(this, getMainComponentName());
        return delegate;
    }
}

以上工作完成以后就可以打包成aar,提供给主程序集成。接下来就是使用React Native语法实现界面布局以及业务实现,然后通过bundle命令打包放在服务器上供各业务更新。这样整个流程基本上就完成了。

四、实现效果

以下是简单的实现效果:

五、其他

以上哪里写的不对或者有待改进,欢迎大家提意见,谢谢!
源码地址:https://github.com/rohsuton/RnDynamicLoad
转载请注明出处:http://www.luoxudong.com/?p=393

有86人对 “React Native实现多业务热部署”留言了

  1. Hi there! This is my first comment here so I just wanted to give a quick shout out and say I genuinely enjoy reading your posts.
    Can you suggest any other blogs/websites/forums that go over the same subjects?
    Appreciate it!

  2. I constantly emailed this webpage post page to all my contacts, as
    if like to read it afterward my links will too.

  3. hello there and thank you for your info – I have certainly picked up anything new from right here.
    I did however expertise some technical issues using this site,
    since I experienced to reload the site lots of times previous to I could
    get it to load correctly. I had been wondering if your hosting is OK?
    Not that I am complaining, but sluggish loading instances times
    will often affect your placement in google and
    can damage your quality score if ads and marketing with Adwords.
    Anyway I’m adding this RSS to my e-mail and could look out for a lot
    more of your respective exciting content.
    Ensure that you update this again soon.

  4. This design is spectacular! You obviously know how to keep a reader amused.
    Between your wit and your videos, I was almost moved to start my own blog (well, almost…HaHa!) Fantastic job.
    I really enjoyed what you had to say, and more than that, how you
    presented it. Too cool!

  5. Excellent weblog right here! Also your site a lot up very
    fast! What web host are you the use of? Can I get your associate hyperlink
    in your host? I wish my website loaded up as
    fast as yours lol

  6. My programmer is trying to persuade me to move to .net from PHP.
    I have always disliked the idea because of the costs.
    But he’s tryiong none the less. I’ve been using Movable-type on a
    variety of websites for about a year and am worried about switching to another platform.
    I have heard good things about blogengine.net. Is there a
    way I can import all my wordpress posts into it? Any kind of help would be greatly appreciated!

  7. I think the admin of this site is truly working
    hard for his web site, since here every information is
    quality based information.

  8. Whats up this is kinda of off topic but I was wondering if blogs use
    WYSIWYG editors or if you have to manually code with HTML.
    I’m starting a blog soon but have no coding knowledge so I wanted to get guidance from someone with experience.
    Any help would be enormously appreciated!

  9. Hello there! Quick question that’s totally off topic. Do you know how
    to make your site mobile friendly? My website looks weird when viewing from my iphone.

    I’m trying to find a theme or plugin that might be able to correct this problem.
    If you have any suggestions, please share. Many thanks!

  10. It is perfect time to make some plans for the future and it
    is time to be happy. I’ve read this post and if I
    could I want to suggest you few interesting things
    or tips. Maybe you could write next articles referring to this article.
    I desire to read more things about it!

  11. Valuable info. Lucky me I discovered your site by accident, and I am shocked why this
    accident didn’t took place in advance! I bookmarked it.

  12. Fantastic website. Lots of helpful info here. I’m sending it to a few
    friends ans additionally sharing in delicious.
    And obviously, thanks to your effort!

  13. That is a really good tip particularly to those new to the blogosphere.
    Simple but very precise info… Many thanks for sharing this one.
    A must read post!

  14. Hi i am kavin, its my first occasion to commenting anyplace, when i
    read this post i thought i could also make comment due to this good
    post.

  15. Magnificent goods from you, man. I have understand your stuff previous to and you are
    just extremely excellent. I really like what you’ve acquired here,
    certainly like what you are stating and the way in which you
    say it. You make it entertaining and you still care for to keep
    it wise. I can not wait to read much more from you.
    This is actually a great site.

  16. Its like you read my mind! You seem to know so much about this, like
    you wrote the book in it or something. I think that you could do with a few pics to drive the message
    home a bit, but other than that, this is excellent blog.
    A fantastic read. I’ll certainly be back.

  17. I am really delighted to read this blog posts which consists of plenty of helpful facts, thanks for
    providing these information.

  18. I know this if off topic but I’m looking into starting my
    own weblog and was curious what all is required to get set
    up? I’m assuming having a blog like yours would
    cost a pretty penny? I’m not very web smart so I’m not 100% certain.
    Any suggestions or advice would be greatly appreciated.
    Thanks

  19. I really like what you guys are usually up too. This type of clever
    work and exposure! Keep up the good works guys I’ve included you guys to our blogroll.

  20. Link exchange is nothing else except it is only placing the other person’s webpage link on your page at suitable place and other person will also
    do same in favor of you.

  21. Hiya! Quick question that’s entirely off topic. Do you know how to make your site mobile friendly?
    My site looks weird when browsing from my iphone 4. I’m trying
    to find a theme or plugin that might be able
    to resolve this problem. If you have any recommendations, please share.

    Appreciate it!

  22. I all the time used to read post in news papers but now as I am a user of internet
    therefore from now I am using net for articles or reviews,
    thanks to web.

  23. I am genuinely glad to glance at this web site posts which includes plenty of helpful data, thanks for providing such information.

  24. It’s amazing to go to see this website and reading the views
    of all friends on the topic of this piece of writing, while I am also zealous of getting experience.

  25. Very nice post. I just stumbled upon your blog and wished to say that I
    have truly enjoyed surfing around your blog posts.
    In any case I will be subscribing to your rss feed and I hope you write again soon!

  26. Good day! I know this is kind of off topic but I was wondering if you knew where I could locate a
    captcha plugin for my comment form? I’m using the same blog platform as yours and I’m having problems finding
    one? Thanks a lot!

  27. Spanky approached a coworker he respected and shared his concept for writing code
    that could determine optimistic expected value in sports betting markets.

  28. It’s not my first time to pay a visit this web page, i am browsing this
    site dailly and get pleasant facts from here
    all the time.

  29. I was curious if you ever considered changing the layout of your site?
    Its very well written; I love what youve got to say. But maybe you could a little more in the
    way of content so people could connect with it better. Youve
    got an awful lot of text for only having 1 or 2 images.
    Maybe you could space it out better?

  30. What’s up to all, the contents present at this site are genuinely awesome for people experience,
    well, keep up the nice work fellows.

  31. When I initially left a comment I seem to have clicked on the -Notify me when new comments are added- checkbox and
    now whenever a comment is added I receive 4 emails with the exact same comment.
    Is there a way you can remove me from that service? Appreciate it!

  32. Hello! I’m at work surfing around your blog from my new iphone 3gs!
    Just wanted to say I love reading through your blog and look forward to all your posts!
    Carry on the excellent work!

  33. you are really a good webmaster. The web site loading speed is amazing.
    It sort of feels that you are doing any distinctive trick.
    In addition, The contents are masterpiece.
    you have done a great job on this matter!

  34. Why people still make use of to read news papers when in this technological globe everything is presented on web?

  35. I do not even know how I stopped up right here, however I
    assumed this post used to be good. I do not
    know who you are however definitely you’re going to a well-known blogger in case
    you are not already. Cheers!

  36. If some one needs expert view concerning blogging then i advise him/her to pay a quick visit this website, Keep up the nice job.

  37. Hello, i believe that i noticed you visited my site so i came to go back the favor?.I’m trying to
    in finding things to improve my site!I assume its adequate to use
    some of your concepts!!

  38. Hello, i think that i saw you visited my weblog thus i came to “return the favor”.I am attempting to find things
    to enhance my web site!I suppose its ok to use some of
    your ideas!!

  39. Hey there I am so glad I found your blog page, I really found you by
    error, while I was researching on Digg for something else, Regardless I am here
    now and would just like to say thank you for a incredible post and a
    all round enjoyable blog (I also love the theme/design), I don’t have time to browse it all at the minute but I
    have bookmarked it and also added in your RSS feeds, so when I have time I will be back to
    read a lot more, Please do keep up the fantastic job.

  40. It’s nearly impossible to find well-informed people on this topic, but you sound like you know what
    you’re talking about! Thanks

发表评论

邮箱地址不会被公开。