React Native 热加载指定目录bundle资源

目录

搭建服务提供下载资源

资源下载

重新指定RN资源加载路径并加载相关资源


ReactNative在Android环境中运行时候会先将RN相关资源打包并合并到android应用的assets目录,相关资源包内容如下(包括但不限于一下资源,这里是demo,比较简单):

Drawable-x包下面主要是一些图片的资源

raw包配置相关资源

index.androd.bindlejs相关代码打包后的特殊格式文件,其中包含应用相关信息等,也只android应用主要加载的目标。

 

 

apk运行起来后,需要加载RN相关资源的时候应用默认会去assets目录下找,这个目录是固定不变,而且从云端下拉的资源也无法放入,但云端下啦的资源可以放在固定的目录下,所以本地热加载RN资源的重点是重新指定资源加载路径,并让android应用将这些资源加载运行起来。

 

思路:

  1. 搭建本地服务器,事先准备好RN打包好的bundle.zip资源包;
  2. 下拉bundle.zip资源包并解压到指定目录;
  3. 重写React native相关代码指定资源包加载路径和加载逻辑

 

 

搭建服务提供下载资源

mac上相对简单,主要通过python,实现在桌面建立文件见并将bundle.zip包放入,命令行

1)$ cd /Users/xxxx/Desktop/server

2)$ python -m SimpleHTTPServer 8900

3)验证服务可用,浏览器输入:http://0.0.0.0:8900/,检测可以下载

React Native 热加载指定目录bundle资源_第1张图片

React Native 热加载指定目录bundle资源_第2张图片

 

4)但是通过android模拟器是无法建立链接的需要修改为http://10.0.2.2:8900/bundle.zip

 

 

资源下载

相关代码结构如下,

React Native 热加载指定目录bundle资源_第3张图片

 

下载的zip包存放路径是android的根目录的bundles包

 

 

重新指定RN资源加载路径并加载相关资源

首先需要重写一些方法,主要是在Application中进行,整体代码如下:

public class MainApplication extends Application implements ReactApplication {

    public static Context appContext;
    private static MainApplication instance;
    private ReactInstanceManager mReactInstanceManager;
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return false;//在Debug模式下,会去加载JS Server服务的bundle。在Release模式下会去加载本地的bundle
        }

        @Override
        protected List getPackages() {
            @SuppressWarnings("UnnecessaryLocalVariable")
            List packages = new PackageList(this).getPackages();
            // Packages that cannot be autolinked yet can be added manually here, for example:
            packages.add(new UpgradePackage());
            return packages;
        }

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

        @Nullable
        @Override
        protected String getJSBundleFile() {
            String path = getBundlePath();
            
            File file = new File(path);
            if (file != null && file.exists()) {
                return path;
            }
            
            return null;

        }

        @Override
        protected ReactInstanceManager createReactInstanceManager() {
            ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
                    .setApplication(getApplication())
                    .setJSMainModulePath(getJSMainModuleName())
                    .setUseDeveloperSupport(getUseDeveloperSupport())
                    .setRedBoxHandler(getRedBoxHandler())
                    .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
                    .setUIImplementationProvider(getUIImplementationProvider())
                    .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()));
            }
            mReactInstanceManager = builder.build();
            return mReactInstanceManager;
        }
    };

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

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        appContext = getApplicationContext();
        SoLoader.init(this, false);
    }

    /**
     * 获取Application实例
     */
    public static MainApplication getInstance() {
        return instance;
    }

    /**
     * 获取包名
     */
    public String getAppPackageName() {
        return this.getPackageName();
    }


    public ReactContext getReactContext(){
        return mReactNativeHost.getReactInstanceManager().getCurrentReactContext();
    }

    public String getBundlePath() {
        return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "bundles/index.android.bundle";
    }
}

最主要的是构建:mReactNativeHost,其中有两个重要的实现方法,getJSBundleFile()和createReactInstanceManager()

getJSBundleFile就是用来指定资源加载路径的,也就是Android应用加载RN资源包的路径;

createReactInstanceManager是用来创建ReactInstanceManager的,而该类就是用来重新加载RN资源的,具体加载代码如下:

/**
     * 重新加载
     */
    private void reloadJSBundle() {
        File file = new File(MainApplication.getInstance().getBundlePath());
        if (file == null || !file.exists()) {
            Log.i(TAG, "download error, check URL or network state");
            return;
        }

        Log.i(TAG, "download success, reload js bundle");

        Toast.makeText(this, "Downloading complete", Toast.LENGTH_SHORT).show();
        Field jsBundleField;
        try {

            ReactInstanceManager reactInstanceManager = MainApplication.getInstance().getReactNativeHost().getReactInstanceManager();
            Class RIManagerClazz = reactInstanceManager.getClass();
            jsBundleField = RIManagerClazz.getDeclaredField("mBundleLoader");
            jsBundleField.setAccessible(true);
            jsBundleField.set(reactInstanceManager, JSBundleLoader.createFileLoader(file.getPath()));
            if (!reactInstanceManager.hasStartedCreatingInitialContext()) {
                reactInstanceManager.createReactContextInBackground();
            } else {
                reactInstanceManager.recreateReactContextInBackground();
            }

            reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener(){

                @Override
                public void onReactContextInitialized(ReactContext context) {
                    reactInstanceManager.removeReactInstanceEventListener(this);
                    try {
                        CatalystInstance catalystInstance = reactInstanceManager.getCurrentReactContext().getCatalystInstance();
                        Method method = CatalystInstanceImpl.class.getDeclaredMethod("loadScriptFromFile", String.class, String.class, boolean.class);
                        method.setAccessible(true);
                        method.invoke(catalystInstance, file.getPath(), file.getPath(), false);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            });
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

注意上面使用的ReactInstanceManager 实例便是先前在Application中重写的createReactInstanceManager方法返回的。

ReactInstanceManager reactInstanceManager = MainApplication.getInstance().getReactNativeHost().getReactInstanceManager();

上面工作基本工作就绪后运行验证,可能不注意修改getUseDeveloperSupport()返回值,默认是返回BuildConfig.DEBUG,这样运行不会成功的,因为在debug模式下,会去加载JS Server服务的bundle,在release模式下会去加载本地的bundle,返回值false表示release模式

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

如果运行如下图,就是不成功的,加载的是JS Server服务器中的代码

React Native 热加载指定目录bundle资源_第4张图片

修改返回值为false,重新运行:

        @Override
        public boolean getUseDeveloperSupport() {
            return false;
        }

React Native 热加载指定目录bundle资源_第5张图片

验证成功

 

 

你可能感兴趣的:(React,Native学习)