欢迎使用CSDN-markdown编辑器
2016-03-31 14:40
323 查看
React Native Android入门实战及深入源码分析系列(3)——热部署加载离线JSBundle文件
本Markdown编辑器使用[StackEdit][6]修改而来,用它写博客,将会带来全新的体验哦:本文为老曾原创,转载需注明出处:http://blog.csdn.net/minimicall?viewmode=contents
上两节我们已经学些了如何编译源码,这一节我们来学习如何实施热部署。也就是更新我们的应用的时候,不需要发布版本,而是把JsBundle放到服务器上,然后下载下来,进行加载。
要实现这一步骤的第一个前提就是,加载离线JsBundle。
- 默认的MainActivity
我们首先来看工程默认的MainActivity 如下: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 "videolegend"; } /** * Returns whether dev mode should be enabled. * This enables e.g. the dev menu. */ @Override protected boolean getUseDeveloperSupport() { // return BuildConfig.DEBUG; return false; } /** * A list of packages used by the app. If the app uses additional views * or modules besides the default ones, add more packages here. */ @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage() ); } }
然而,这段代码并不能看出什么鬼。只是注册了了一个MainReactPackage而已。
但是,它继承了ReactActivity。所以,我们可以把注意力放到ReactActivity上。
- ReactActivity
在ReactActivity里面有两个方法:/** * Returns the name of the bundle in assets. If this is null, and no file path is specified for * the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will * always try to load the JS bundle from the packager server. * 这句话很重要,如果 getUseDeveloperSupport开关被打开,那么它总是会从packager server拉取JS bundle文件。 * e.g. "index.android.bundle" */ protected @Nullable String getBundleAssetName() { return "index.android.bundle"; }; /** * Returns a custom path of the bundle file. This is used in cases the bundle should be loaded * from a custom path. By default it is loaded from Android assets, from a path specified这句话就是说,如果你要自定义bundle加载,那么就修改这个地方的返回。 * by {@link }.getBundleAssetName * e.g. "file://sdcard/myapp_cache/index.android.bundle" */ protected @Nullable String getJSBundleFile() { Toast.makeText(this,JS_BUNDLE_LOCAL_PATH,Toast.LENGTH_SHORT); return null; }
上面,我已经备注了。你要从手机的/sdcard/里面加载jsbundle,必须满足两个条件:
1、getJSBundleFile这里设置好返jsbundle的路径;
2、getUseDeveloperSupport开关必须要关闭。因为如果asset里面没有jsbundle,而且开关打开了,那么程序会从packager server获取jsbundle。
根据上面两个点,我们进行改动。这里贴出我们改动后的代码。
- 改动后的MainActivity
改动后的MainActivity主要是要关闭开发支持开关。具体代码如下:
package com.videolegend; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import java.util.Arrays; import java.util.List; public class MainActivity extends KKReactBaseActivity { /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "videolegend"; } /** * Returns whether dev mode should be enabled. * This enables e.g. the dev menu. */ @Override protected boolean getUseDeveloperSupport() { // return BuildConfig.DEBUG;关闭开发支持开关 return false; } /** * A list of packages used by the app. If the app uses additional views * or modules besides the default ones, add more packages here. */ @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage() ); } }
注意到这里我们的MainActivity不在是继承ReactActivity
而是继承KKReactBaseActivity,我们基本是拷贝了ReactActivity的代码。然后修改的。
package com.videolegend; /** * Created by zengjinlong on 16/3/30. */ import android.app.Activity; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; import android.widget.EditText; import android.widget.Toast; import com.facebook.common.logging.FLog; import com.facebook.react.LifecycleState; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactPackage; import com.facebook.react.ReactRootView; import com.facebook.react.common.ReactConstants; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import java.io.File; import java.util.List; import javax.annotation.Nullable; /** * @Author: zengjinlong * @Date: 2016-03-30 * @purpose: 基于ReactActivity来改的,为了支持热部署,就最好支持离线bundle */ public abstract class KKReactBaseActivity extends Activity implements DefaultHardwareBackBtnHandler { private static final String TAG = "KKReactBaseActivity"; private static final String REDBOX_PERMISSION_MESSAGE = "Overlay permissions needs to be granted in order for react native apps to run in dev mode"; public static final String JS_BUNDLE_LOCAL_FILE = "videolegend.bundle"; //JS bundle的本地加载路径 public static final String JS_BUNDLE_LOCAL_PATH = Environment.getExternalStorageDirectory().toString() + File.separator + JS_BUNDLE_LOCAL_FILE; private @Nullable ReactInstanceManager mReactInstanceManager; private LifecycleState mLifecycleState = LifecycleState.BEFORE_RESUME; private boolean mDoRefresh = false; /** * Returns the name of the bundle in assets. If this is null, and no file path is specified for * the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will * always try to load the JS bundle from the packager server. * e.g. "index.android.bundle" */ protected @Nullable String getBundleAssetName() { return "index.android.bundle"; }; /** * Returns a custom path of the bundle file. This is used in cases the bundle should be loaded * from a custom path. By default it is loaded from Android assets, from a path specified * by {@link }.getBundleAssetName * e.g. "file://sdcard/myapp_cache/index.android.bundle" */ protected @Nullable String getJSBundleFile() { Toast.makeText(this,JS_BUNDLE_LOCAL_PATH,Toast.LENGTH_SHORT); return JS_BUNDLE_LOCAL_PATH;//这里返回jsbundle文件路径 // return null; } /** * Returns the name of the main module. Determines the URL used to fetch the JS bundle * from the packager server. It is only used when dev support is enabled. * This is the first file to be executed once the {@link ReactInstanceManager} is created. * e.g. "index.android" */ protected String getJSMainModuleName() { return "index.android"; } /** * Returns the launchOptions which will be passed to the {@link ReactInstanceManager} * when the application is started. By default, this will return null and an empty * object will be passed to your top level component as its initial props. * If your React Native application requires props set outside of JS, override * this method to return the Android.os.Bundle of your desired initial props. */ protected @Nullable Bundle getLaunchOptions() { return null; } /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. * e.g. "MoviesApp" */ protected abstract String getMainComponentName(); /** * Returns whether dev mode should be enabled. This enables e.g. the dev menu. */ protected abstract boolean getUseDeveloperSupport(); /** * Returns a list of {@link ReactPackage} used by the app. * You'll most likely want to return at least the {@code MainReactPackage}. * If your app uses additional views or modules besides the default ones, * you'll want to include more packages here. */ protected abstract List<ReactPackage> getPackages(); /** * A subclass may override this method if it needs to use a custom instance. */ protected ReactInstanceManager createReactInstanceManager() { ReactInstanceManager.Builder builder = ReactInstanceManager.builder() .setApplication(getApplication()) .setJSMainModuleName(getJSMainModuleName()) .setUseDeveloperSupport(getUseDeveloperSupport()) .setInitialLifecycleState(mLifecycleState); for (ReactPackage reactPackage : getPackages()) { builder.addPackage(reactPackage); } String jsBundleFile = getJSBundleFile(); Log.d(TAG,"jsBundleFile:"+jsBundleFile); if (jsBundleFile != null) { Log.d(TAG,"setJSBundleFile now"); builder.setJSBundleFile(jsBundleFile); } else { builder.setBundleAssetName(getBundleAssetName()); } return builder.build(); } /** * A subclass may override this method if it needs to use a custom {@link ReactRootView}. */ protected ReactRootView createRootView() { return new ReactRootView(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) { // Get permission to show redbox in dev builds. if (!Settings.canDrawOverlays(this)) { Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(serviceIntent); FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE); Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); } } mReactInstanceManager = createReactInstanceManager(); ReactRootView mReactRootView = createRootView(); mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions()); setContentView(mReactRootView); } @Override protected void onPause() { super.onPause(); mLifecycleState = LifecycleState.BEFORE_RESUME; if (mReactInstanceManager != null) { mReactInstanceManager.onHostPause(); } } @Override protected void onResume() { super.onResume(); mLifecycleState = LifecycleState.RESUMED; if (mReactInstanceManager != null) { mReactInstanceManager.onHostResume(this, this); } } @Override protected void onDestroy() { super.onDestroy(); if (mReactInstanceManager != null) { mReactInstanceManager.destroy(); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (mReactInstanceManager != null) { mReactInstanceManager.onActivityResult(requestCode, resultCode, data); } } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (mReactInstanceManager != null && mReactInstanceManager.getDevSupportManager().getDevSupportEnabled()) { if (keyCode == KeyEvent.KEYCODE_MENU) { mReactInstanceManager.showDevOptionsDialog(); return true; } if (keyCode == KeyEvent.KEYCODE_R && !(getCurrentFocus() instanceof EditText)) { // Enable double-tap-R-to-reload if (mDoRefresh) { mReactInstanceManager.getDevSupportManager().handleReloadJS(); mDoRefresh = false; } else { mDoRefresh = true; new Handler().postDelayed( new Runnable() { @Override public void run() { mDoRefresh = false; } }, 200); } } } return super.onKeyUp(keyCode, event); } @Override public void onBackPressed() { if (mReactInstanceManager != null) { mReactInstanceManager.onBackPressed(); } else { super.onBackPressed(); } } @Override public void invokeDefaultOnBackPressed() { super.onBackPressed(); } }
这样,我们就支持了离线的jsbundle加载,这为热部署提供了支持。下面一节,我们实现我们的热部署细节。
包括版本管理。
相关文章推荐
- 详解HDFS Short Circuit Local Reads
- SQL Server Native Client下载 SQL Server Native Client安装方法
- 详解Java中native关键字
- 深入浅析react native es6语法
- hadoop安装lzo
- H5、React Native、Native应用对比分析
- 使用Jenkins实现Jetty热部署Maven程序(20s极速)
- React Native 的那些坑
- 用Android Sutdio调试NDK
- Java中native关键字
- maven多模块eclipse jetty热部署
- 【unity】与Android Activity交互并调用JAVA代码传递参数
- tomcat 热部署和热加载 配置
- 鸟巢-一种全新的Native APP开发模式,这篇文章为您解读
- React Native for Android 官方文档中文版(最新)全国首发
- 从react来理解learn once write anywhere
- JNI_Java Native Interface
- maven远程热部署web项目到tomcat7
- java Native Method初涉