From 88980f2ef7331aa630ff19e54427cdc3b7510869 Mon Sep 17 00:00:00 2001 From: Ray Shih Date: Thu, 1 Feb 2018 06:13:19 -0800 Subject: [PATCH] Implement bundle sync status Reviewed By: pakoito Differential Revision: D6807480 fbshipit-source-id: d71f2cecd882c47e79bb71dfb9d03d3597fa4068 --- .../DevSupport/RCTInspectorDevServerHelper.h | 3 +- .../DevSupport/RCTInspectorDevServerHelper.mm | 4 ++- .../RCTInspectorPackagerConnection.h | 10 +++++- .../RCTInspectorPackagerConnection.m | 21 ++++++++++++ .../react/devsupport/DevServerHelper.java | 14 ++++++-- .../devsupport/DevSupportManagerImpl.java | 22 ++++++++++++- .../InspectorPackagerConnection.java | 33 +++++++++++++++++-- 7 files changed, 99 insertions(+), 8 deletions(-) diff --git a/React/DevSupport/RCTInspectorDevServerHelper.h b/React/DevSupport/RCTInspectorDevServerHelper.h index eabfd99ebf84ee..42a4f6a2e408ff 100644 --- a/React/DevSupport/RCTInspectorDevServerHelper.h +++ b/React/DevSupport/RCTInspectorDevServerHelper.h @@ -5,12 +5,13 @@ #import #import +#import #if RCT_DEV @interface RCTInspectorDevServerHelper : NSObject -+ (void)connectWithBundleURL:(NSURL *)bundleURL; ++ (RCTInspectorPackagerConnection *)connectWithBundleURL:(NSURL *)bundleURL; + (void)disableDebugger; + (void)attachDebugger:(NSString *)owner withBundleURL:(NSURL *)bundleURL diff --git a/React/DevSupport/RCTInspectorDevServerHelper.mm b/React/DevSupport/RCTInspectorDevServerHelper.mm index 2fa4fae5beb1f4..0a3c1bfc1cdbf5 100644 --- a/React/DevSupport/RCTInspectorDevServerHelper.mm +++ b/React/DevSupport/RCTInspectorDevServerHelper.mm @@ -104,7 +104,7 @@ + (void)disableDebugger sendEventToAllConnections(kDebuggerMsgDisable); } -+ (void)connectWithBundleURL:(NSURL *)bundleURL ++ (RCTInspectorPackagerConnection *)connectWithBundleURL:(NSURL *)bundleURL { NSURL *inspectorURL = getInspectorDeviceUrl(bundleURL); @@ -122,6 +122,8 @@ + (void)connectWithBundleURL:(NSURL *)bundleURL socketConnections[key] = connection; [connection connect]; } + + return connection; } @end diff --git a/React/Inspector/RCTInspectorPackagerConnection.h b/React/Inspector/RCTInspectorPackagerConnection.h index b7774ef6abff6e..8499ea40632727 100644 --- a/React/Inspector/RCTInspectorPackagerConnection.h +++ b/React/Inspector/RCTInspectorPackagerConnection.h @@ -2,15 +2,23 @@ #import #import -#import #if RCT_DEV +@interface RCTBundleStatus : NSObject +@property (atomic, assign) BOOL isLastBundleDownloadSuccess; +@property (atomic, assign) NSTimeInterval bundleUpdateTimestamp; +@end + +typedef RCTBundleStatus *(^RCTBundleStatusProvider)(void); + @interface RCTInspectorPackagerConnection : NSObject - (instancetype)initWithURL:(NSURL *)url; + - (void)connect; - (void)closeQuietly; - (void)sendEventToAllConnections:(NSString *)event; +- (void)setBundleStatusProvider:(RCTBundleStatusProvider)bundleStatusProvider; @end @interface RCTInspectorRemoteConnection : NSObject diff --git a/React/Inspector/RCTInspectorPackagerConnection.m b/React/Inspector/RCTInspectorPackagerConnection.m index 9d24833fa4899e..cf1cd2abf557a3 100644 --- a/React/Inspector/RCTInspectorPackagerConnection.m +++ b/React/Inspector/RCTInspectorPackagerConnection.m @@ -14,6 +14,9 @@ const int RECONNECT_DELAY_MS = 2000; +@implementation RCTBundleStatus +@end + @interface RCTInspectorPackagerConnection () { NSURL *_url; NSMutableDictionary *_inspectorConnections; @@ -21,6 +24,7 @@ @interface RCTInspectorPackagerConnection () { dispatch_queue_t _jsQueue; BOOL _closed; BOOL _suppressConnectionErrors; + RCTBundleStatusProvider _bundleStatusProvider; } @end @@ -51,6 +55,11 @@ - (instancetype)initWithURL:(NSURL *)url return self; } +- (void)setBundleStatusProvider:(RCTBundleStatusProvider)bundleStatusProvider +{ + _bundleStatusProvider = bundleStatusProvider; +} + - (void)handleProxyMessage:(NSDictionary *)message { NSString *event = message[@"event"]; @@ -135,11 +144,23 @@ - (NSArray *)pages { NSArray *pages = [RCTInspector pages]; NSMutableArray *array = [NSMutableArray arrayWithCapacity:pages.count]; + + RCTBundleStatusProvider statusProvider = _bundleStatusProvider; + RCTBundleStatus *bundleStatus = statusProvider == nil + ? nil + : statusProvider(); + for (RCTInspectorPage *page in pages) { NSDictionary *jsonPage = @{ @"id": [@(page.id) stringValue], @"title": page.title, @"app": [[NSBundle mainBundle] bundleIdentifier], + @"isLastBundleDownloadSuccess": bundleStatus == nil + ? [NSNull null] + : @(bundleStatus.isLastBundleDownloadSuccess), + @"bundleUpdateTimestamp": bundleStatus == nil + ? [NSNull null] + : @((long)bundleStatus.bundleUpdateTimestamp * 1000), }; [array addObject:jsonPage]; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index 23534b2a23d755..69b22f49cfa75a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -116,9 +116,15 @@ public interface SymbolicationListener { private @Nullable InspectorPackagerConnection mInspectorPackagerConnection; private @Nullable OkHttpClient mOnChangePollingClient; private @Nullable OnServerContentChangeListener mOnServerContentChangeListener; + private InspectorPackagerConnection.BundleStatusProvider mBundlerStatusProvider; - public DevServerHelper(DevInternalSettings settings, String packageName) { + public DevServerHelper( + DevInternalSettings settings, + String packageName, + InspectorPackagerConnection.BundleStatusProvider bundleStatusProvider + ) { mSettings = settings; + mBundlerStatusProvider = bundleStatusProvider; mClient = new OkHttpClient.Builder() .connectTimeout(HTTP_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS) .readTimeout(0, TimeUnit.MILLISECONDS) @@ -212,7 +218,11 @@ public void openInspectorConnection() { new AsyncTask() { @Override protected Void doInBackground(Void... params) { - mInspectorPackagerConnection = new InspectorPackagerConnection(getInspectorDeviceUrl(), mPackageName); + mInspectorPackagerConnection = new InspectorPackagerConnection( + getInspectorDeviceUrl(), + mPackageName, + mBundlerStatusProvider + ); mInspectorPackagerConnection.connect(); return null; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java index 2610dba6dbc42f..b29f562f13b898 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java @@ -47,6 +47,7 @@ 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.InspectorPackagerConnection; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.DevOptionHandler; import com.facebook.react.devsupport.interfaces.DevSupportManager; @@ -156,6 +157,8 @@ private enum ErrorType { private @Nullable DevBundleDownloadListener mBundleDownloadListener; private @Nullable List mErrorCustomizers; + private InspectorPackagerConnection.BundleStatus mBundleStatus; + private static class JscProfileTask extends AsyncTask { private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); @@ -217,7 +220,17 @@ public DevSupportManagerImpl( mApplicationContext = applicationContext; mJSAppBundleName = packagerPathForJSBundleName; mDevSettings = new DevInternalSettings(applicationContext, this); - mDevServerHelper = new DevServerHelper(mDevSettings, mApplicationContext.getPackageName()); + mBundleStatus = new InspectorPackagerConnection.BundleStatus(); + mDevServerHelper = new DevServerHelper( + mDevSettings, + mApplicationContext.getPackageName(), + new InspectorPackagerConnection.BundleStatusProvider() { + @Override + public InspectorPackagerConnection.BundleStatus getBundleStatus() { + return mBundleStatus; + } + } + ); mBundleDownloadListener = devBundleDownloadListener; // Prepare shake gesture detector (will be started/stopped from #reload) @@ -1032,6 +1045,10 @@ public void reloadJSFromServer(final String bundleURL) { public void onSuccess() { mDevLoadingViewController.hide(); mDevLoadingViewVisible = false; + synchronized (DevSupportManagerImpl.this) { + mBundleStatus.isLastDownloadSucess = true; + mBundleStatus.updateTimestamp = System.currentTimeMillis(); + } if (mBundleDownloadListener != null) { mBundleDownloadListener.onSuccess(); } @@ -1057,6 +1074,9 @@ public void onProgress(@Nullable final String status, @Nullable final Integer do public void onFailure(final Exception cause) { mDevLoadingViewController.hide(); mDevLoadingViewVisible = false; + synchronized (DevSupportManagerImpl.this) { + mBundleStatus.isLastDownloadSucess = false; + } if (mBundleDownloadListener != null) { mBundleDownloadListener.onFailure(cause); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java index 534727764d42e6..1176d38caf3ec5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java @@ -8,7 +8,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import android.os.AsyncTask; @@ -33,11 +32,17 @@ public class InspectorPackagerConnection { private final Connection mConnection; private final Map mInspectorConnections; private final String mPackageName; + private BundleStatusProvider mBundleStatusProvider; - public InspectorPackagerConnection(String url, String packageName) { + public InspectorPackagerConnection( + String url, + String packageName, + BundleStatusProvider bundleStatusProvider + ) { mConnection = new Connection(url); mInspectorConnections = new HashMap<>(); mPackageName = packageName; + mBundleStatusProvider = bundleStatusProvider; } public void connect() { @@ -143,11 +148,14 @@ private void handleWrappedEvent(JSONObject payload) throws JSONException { private JSONArray getPages() throws JSONException { List pages = Inspector.getPages(); JSONArray array = new JSONArray(); + BundleStatus bundleStatus = mBundleStatusProvider.getBundleStatus(); for (Inspector.Page page : pages) { JSONObject jsonPage = new JSONObject(); jsonPage.put("id", String.valueOf(page.getId())); jsonPage.put("title", page.getTitle()); jsonPage.put("app", mPackageName); + jsonPage.put("isLastBundleDownloadSuccess", bundleStatus.isLastDownloadSucess); + jsonPage.put("bundleUpdateTimestamp", bundleStatus.updateTimestamp); array.put(jsonPage); } return array; @@ -306,4 +314,25 @@ private void closeWebSocketQuietly() { } } } + + static public class BundleStatus { + public Boolean isLastDownloadSucess; + public long updateTimestamp = -1; + + public BundleStatus( + Boolean isLastDownloadSucess, + long updateTimestamp + ) { + this.isLastDownloadSucess = isLastDownloadSucess; + this.updateTimestamp = updateTimestamp; + } + + public BundleStatus() { + this(false, -1); + } + } + + public interface BundleStatusProvider { + public BundleStatus getBundleStatus(); + } }