Add dev bundle download listener on Android

Summary:
This exposes a way to listen to JS bundle download events when creating a ReactInstanceManager. This can be used to display a custom native UI while the JS bundle is loading. It is a pretty specific use case but Expo will need this to display loading progress on the app loading splash screen.

**Test plan**
Tested by adding a listener to the ReactInstanceManager in the Expo app and checked that it gets called when the bundle is loading.
Closes https://github.com/facebook/react-native/pull/12984

Reviewed By: devknoll

Differential Revision: D4797638

Pulled By: hramos

fbshipit-source-id: 04d7cd4071535670c1bcb121566748e495197c80
This commit is contained in:
Janic Duplessis 2017-06-28 19:49:14 -07:00 committed by Facebook Github Bot
parent c848c3820b
commit 960e5db0ed
7 changed files with 53 additions and 9 deletions

View File

@ -53,6 +53,7 @@ import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.devsupport.DevSupportManagerFactory;
import com.facebook.react.devsupport.ReactInstanceDevCommandsHandler;
import com.facebook.react.devsupport.RedBoxHandler;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
import com.facebook.react.modules.appregistry.AppRegistry;
@ -221,6 +222,7 @@ public class ReactInstanceManager {
@Nullable RedBoxHandler redBoxHandler,
boolean lazyNativeModulesEnabled,
boolean lazyViewManagersEnabled,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
boolean setupReactContextInBackgroundEnabled,
boolean useSeparateUIBackgroundThread,
int minNumShakes) {
@ -242,6 +244,7 @@ public class ReactInstanceManager {
mJSMainModuleName,
useDeveloperSupport,
redBoxHandler,
devBundleDownloadListener,
minNumShakes);
mBridgeIdleDebugListener = bridgeIdleDebugListener;
mLifecycleState = initialLifecycleState;

View File

@ -15,6 +15,7 @@ import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.devsupport.RedBoxHandler;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
@ -42,6 +43,7 @@ public class ReactInstanceManagerBuilder {
protected @Nullable RedBoxHandler mRedBoxHandler;
protected boolean mLazyNativeModulesEnabled;
protected boolean mLazyViewManagersEnabled;
protected @Nullable DevBundleDownloadListener mDevBundleDownloadListener;
protected boolean mSetupReactContextInBackground;
protected boolean mUseSeparateUIBackgroundThread;
protected int mMinNumShakes = 1;
@ -189,6 +191,11 @@ public class ReactInstanceManagerBuilder {
return this;
}
public ReactInstanceManagerBuilder setDevBundleDownloadListener(@Nullable DevBundleDownloadListener listener) {
mDevBundleDownloadListener = listener;
return this;
}
public ReactInstanceManagerBuilder setSetupReactContextInBackgroundEnabled(
boolean setupReactContextInBackground) {
mSetupReactContextInBackground = setupReactContextInBackground;
@ -252,6 +259,7 @@ public class ReactInstanceManagerBuilder {
mRedBoxHandler,
mLazyNativeModulesEnabled,
mLazyViewManagersEnabled,
mDevBundleDownloadListener,
mSetupReactContextInBackground,
mUseSeparateUIBackgroundThread,
mMinNumShakes);

View File

@ -20,6 +20,7 @@ import java.util.regex.Pattern;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.common.DebugServerException;
import org.json.JSONException;
@ -36,12 +37,6 @@ import okio.Okio;
import okio.Sink;
public class BundleDownloader {
public interface DownloadCallback {
void onSuccess();
void onProgress(@Nullable String status, @Nullable Integer done, @Nullable Integer total);
void onFailure(Exception cause);
}
private final OkHttpClient mClient;
private @Nullable Call mDownloadBundleFromURLCall;
@ -51,7 +46,7 @@ public class BundleDownloader {
}
public void downloadBundleFromURL(
final DownloadCallback callback,
final DevBundleDownloadListener callback,
final File outputFile,
final String bundleURL) {
final Request request = new Request.Builder()
@ -156,7 +151,7 @@ public class BundleDownloader {
int statusCode,
BufferedSource body,
File outputFile,
DownloadCallback callback) throws IOException {
DevBundleDownloadListener callback) throws IOException {
// Check for server errors. If the server error has the expected form, fail with more info.
if (statusCode != 200) {
String bodyString = body.readUtf8();

View File

@ -28,6 +28,7 @@ import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.network.OkHttpCallUtil;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
import com.facebook.react.devsupport.interfaces.StackFrame;
import com.facebook.react.modules.systeminfo.AndroidInfoHelpers;

View File

@ -15,6 +15,7 @@ import java.lang.reflect.Constructor;
import android.content.Context;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
/**
@ -41,6 +42,7 @@ public class DevSupportManagerFactory {
packagerPathForJSBundleName,
enableOnCreate,
null,
null,
minNumShakes);
}
@ -50,6 +52,7 @@ public class DevSupportManagerFactory {
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes) {
if (!enableOnCreate) {
return new DisabledDevSupportManager();
@ -72,6 +75,7 @@ public class DevSupportManagerFactory {
String.class,
boolean.class,
RedBoxHandler.class,
DevBundleDownloadListener.class,
int.class);
return (DevSupportManager) constructor.newInstance(
applicationContext,
@ -79,6 +83,7 @@ public class DevSupportManagerFactory {
packagerPathForJSBundleName,
true,
redBoxHandler,
devBundleDownloadListener,
minNumShakes);
} catch (Exception e) {
throw new RuntimeException(

View File

@ -52,6 +52,7 @@ import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.ShakeDetector;
import com.facebook.react.common.futures.SimpleSettableFuture;
import com.facebook.react.devsupport.DevServerHelper.PackagerCommandListener;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevOptionHandler;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
@ -133,6 +134,7 @@ public class DevSupportManagerImpl implements
private @Nullable StackFrame[] mLastErrorStack;
private int mLastErrorCookie = 0;
private @Nullable ErrorType mLastErrorType;
private @Nullable DevBundleDownloadListener mBundleDownloadListener;
private static class JscProfileTask extends AsyncTask<String, Void, Void> {
private static final MediaType JSON =
@ -179,6 +181,7 @@ public class DevSupportManagerImpl implements
packagerPathForJSBundleName,
enableOnCreate,
null,
null,
minNumShakes);
}
@ -188,12 +191,14 @@ public class DevSupportManagerImpl implements
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes) {
mReactInstanceCommandsHandler = reactInstanceCommandsHandler;
mApplicationContext = applicationContext;
mJSAppBundleName = packagerPathForJSBundleName;
mDevSettings = new DevInternalSettings(applicationContext, this);
mDevServerHelper = new DevServerHelper(mDevSettings);
mBundleDownloadListener = devBundleDownloadListener;
// Prepare shake gesture detector (will be started/stopped from #reload)
mShakeDetector = new ShakeDetector(new ShakeDetector.ShakeListener() {
@ -827,11 +832,14 @@ public class DevSupportManagerImpl implements
mDevLoadingViewVisible = true;
mDevServerHelper.getBundleDownloader().downloadBundleFromURL(
new BundleDownloader.DownloadCallback() {
new DevBundleDownloadListener() {
@Override
public void onSuccess() {
mDevLoadingViewController.hide();
mDevLoadingViewVisible = false;
if (mBundleDownloadListener != null) {
mBundleDownloadListener.onSuccess();
}
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
@ -844,12 +852,18 @@ public class DevSupportManagerImpl implements
@Override
public void onProgress(@Nullable final String status, @Nullable final Integer done, @Nullable final Integer total) {
mDevLoadingViewController.updateProgress(status, done, total);
if (mBundleDownloadListener != null) {
mBundleDownloadListener.onProgress(status, done, total);
}
}
@Override
public void onFailure(final Exception cause) {
mDevLoadingViewController.hide();
mDevLoadingViewVisible = false;
if (mBundleDownloadListener != null) {
mBundleDownloadListener.onFailure(cause);
}
FLog.e(ReactConstants.TAG, "Unable to download JS bundle", cause);
UiThreadUtil.runOnUiThread(
new Runnable() {

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.devsupport.interfaces;
import javax.annotation.Nullable;
public interface DevBundleDownloadListener {
void onSuccess();
void onProgress(@Nullable String status, @Nullable Integer done, @Nullable Integer total);
void onFailure(Exception cause);
}