Quellcode durchsuchen

Merge branch 'bundleReload'

Guy Carmeli vor 8 Jahren
Ursprung
Commit
7263e1b4cf

+ 13
- 0
android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java Datei anzeigen

@@ -1,6 +1,9 @@
1 1
 package com.reactnativenavigation.activities;
2 2
 
3
+import android.content.BroadcastReceiver;
4
+import android.content.Context;
3 5
 import android.content.Intent;
6
+import android.content.IntentFilter;
4 7
 import android.content.res.Configuration;
5 8
 import android.os.Build;
6 9
 import android.os.Bundle;
@@ -26,6 +29,7 @@ import com.facebook.react.bridge.Arguments;
26 29
 import com.facebook.react.bridge.ReadableMap;
27 30
 import com.facebook.react.bridge.WritableMap;
28 31
 import com.facebook.react.common.ReactConstants;
32
+import com.facebook.react.devsupport.DevServerHelper;
29 33
 import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
30 34
 import com.facebook.react.shell.MainReactPackage;
31 35
 import com.reactnativenavigation.BuildConfig;
@@ -367,6 +371,15 @@ public abstract class BaseReactActivity extends AppCompatActivity implements Def
367 371
         return super.onKeyUp(keyCode, event);
368 372
     }
369 373
 
374
+    /**
375
+     * Called after bundle was reloaded. This is a good chance to clean up previously connected react views.
376
+     */
377
+    public void onJSBundleReloaded() {
378
+        removeAllReactViews();
379
+    }
380
+
381
+    protected abstract void removeAllReactViews();
382
+
370 383
     @Override
371 384
     public void onBackPressed() {
372 385
         if (getScreenStackSize() > 1) {

+ 7
- 0
android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java Datei anzeigen

@@ -267,4 +267,11 @@ public class BottomTabActivity extends BaseReactActivity implements AHBottomNavi
267 267
         }
268 268
         this.onTabSelected(0, false);
269 269
     }
270
+
271
+    @Override
272
+    protected void removeAllReactViews() {
273
+        for (ScreenStack screenStack : mScreenStacks) {
274
+            screenStack.removeAllReactViews();
275
+        }
276
+    }
270 277
 }

+ 4
- 0
android/app/src/main/java/com/reactnativenavigation/activities/RootActivity.java Datei anzeigen

@@ -45,4 +45,8 @@ public class RootActivity extends BaseReactActivity {
45 45
         return 0;
46 46
     }
47 47
 
48
+    @Override
49
+    public void removeAllReactViews() {
50
+
51
+    }
48 52
 }

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java Datei anzeigen

@@ -103,4 +103,9 @@ public class SingleScreenActivity extends BaseReactActivity {
103 103
     public int getScreenStackSize() {
104 104
         return mScreenStack.getStackSize();
105 105
     }
106
+
107
+    @Override
108
+    protected void removeAllReactViews() {
109
+        mScreenStack.removeAllReactViews();
110
+    }
106 111
 }

+ 5
- 1
android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java Datei anzeigen

@@ -1,6 +1,5 @@
1 1
 package com.reactnativenavigation.activities;
2 2
 
3
-import android.support.design.widget.CoordinatorLayout;
4 3
 import android.support.v4.view.ViewPager;
5 4
 import android.view.Menu;
6 5
 import android.view.ViewGroup;
@@ -69,6 +68,11 @@ public class TabActivity extends BaseReactActivity {
69 68
         return ret;
70 69
     }
71 70
 
71
+    @Override
72
+    public void removeAllReactViews() {
73
+
74
+    }
75
+
72 76
     @Override
73 77
     public void push(Screen screen) {
74 78
         super.push(screen);

+ 84
- 0
android/app/src/main/java/com/reactnativenavigation/core/RctManager.java Datei anzeigen

@@ -1,15 +1,21 @@
1 1
 package com.reactnativenavigation.core;
2 2
 
3 3
 import android.app.Application;
4
+import android.util.Log;
4 5
 
5 6
 import com.facebook.react.LifecycleState;
6 7
 import com.facebook.react.ReactInstanceManager;
7 8
 import com.facebook.react.ReactPackage;
9
+import com.facebook.react.bridge.JavaJSExecutor;
8 10
 import com.facebook.react.bridge.ReactContext;
9 11
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
10 12
 import com.facebook.react.bridge.WritableMap;
13
+import com.facebook.react.devsupport.DevSupportManager;
14
+import com.facebook.react.devsupport.ReactInstanceDevCommandsHandler;
11 15
 import com.reactnativenavigation.activities.BaseReactActivity;
12 16
 import com.reactnativenavigation.core.objects.Screen;
17
+import com.reactnativenavigation.utils.ContextProvider;
18
+import com.reactnativenavigation.utils.ReflectionUtils;
13 19
 
14 20
 import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
15 21
 
@@ -17,6 +23,7 @@ import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDevice
17 23
  * Created by guyc on 22/02/16.
18 24
  */
19 25
 public class RctManager {
26
+    private static final String TAG = "RctManager";
20 27
     private static final String KEY_EVENT_ID = "id";
21 28
     private static RctManager sInstance;
22 29
 
@@ -68,9 +75,47 @@ public class RctManager {
68 75
         }
69 76
 
70 77
         mReactManager = builder.build();
78
+        if (reactActivity.getUseDeveloperSupport()) {
79
+            setupDevSupportHandler(mReactManager);
80
+        }
71 81
         return mReactManager;
72 82
     }
73 83
 
84
+    /**
85
+     * Inject our CustomDevCommandsHandler to {@code reactInstanceManager} so we can detect when the bundle was
86
+     * parsed and is about to load.
87
+     * @param reactInstanceManager
88
+     */
89
+    private void setupDevSupportHandler(ReactInstanceManager reactInstanceManager) {
90
+        final ReactInstanceDevCommandsHandler devInterface = (ReactInstanceDevCommandsHandler)
91
+                ReflectionUtils.getDeclaredField(reactInstanceManager, "mDevInterface");
92
+        if (devInterface == null) {
93
+            Log.e(TAG, "Could not get field mDevInterface");
94
+            return;
95
+        }
96
+
97
+        // Create customDevCommandsHandler
98
+        CustomDevCommandsHandler customDevCommandsHandler = new CustomDevCommandsHandler(devInterface);
99
+        boolean success = ReflectionUtils.setField(reactInstanceManager, "mDevInterface", customDevCommandsHandler);
100
+        if (!success) {
101
+            Log.e(TAG, "Could not set field mDevInterface");
102
+            return;
103
+        }
104
+
105
+        // Set customDevCommandsHandler in devSupportManager. Fun =).
106
+        DevSupportManager devSupportManager = (DevSupportManager)
107
+                ReflectionUtils.getDeclaredField(reactInstanceManager, "mDevSupportManager");
108
+        if (devSupportManager == null) {
109
+            Log.e(TAG, "Could not get field mDevSupportManager");
110
+            return;
111
+        }
112
+
113
+        success = ReflectionUtils.setField(devSupportManager, "mReactInstanceCommandsHandler", customDevCommandsHandler);
114
+        if (!success) {
115
+            Log.e(TAG, "Could not set field mReactInstanceCommandsHandler");
116
+        }
117
+    }
118
+
74 119
     public <T extends ReactContextBaseJavaModule> T getNativeModule(Class<T> nativeModuleClass) {
75 120
         if (mReactManager == null || mReactManager.getCurrentReactContext() == null) {
76 121
             return null;
@@ -113,5 +158,44 @@ public class RctManager {
113 158
         mReactManager = null;
114 159
         sInstance = null;
115 160
     }
161
+
162
+    /**
163
+     * Proxy for {@link ReactInstanceDevCommandsHandler} used by {@link DevSupportManager} for requesting React
164
+     * instance recreation. Used to notify {@link BaseReactActivity} that the bundle has been reloaded.
165
+     */
166
+    private static class CustomDevCommandsHandler implements ReactInstanceDevCommandsHandler {
167
+        private ReactInstanceDevCommandsHandler mCommandsHandler;
168
+
169
+        public CustomDevCommandsHandler(ReactInstanceDevCommandsHandler commandsHandler) {
170
+            mCommandsHandler = commandsHandler;
171
+        }
172
+
173
+        @Override
174
+        public void onReloadWithJSDebugger(JavaJSExecutor.Factory proxyExecutorFactory) {
175
+            onJSBundleReloaded();
176
+            mCommandsHandler.onReloadWithJSDebugger(proxyExecutorFactory);
177
+        }
178
+
179
+        @Override
180
+        public void onJSBundleLoadedFromServer() {
181
+            onJSBundleReloaded();
182
+            mCommandsHandler.onJSBundleLoadedFromServer();
183
+        }
184
+
185
+        /**
186
+         * Detach previously added ReactRootViews before handling bundle.
187
+         */
188
+        private void onJSBundleReloaded() {
189
+            BaseReactActivity context = ContextProvider.getActivityContext();
190
+            if (context != null) {
191
+                context.onJSBundleReloaded();
192
+            }
193
+        }
194
+
195
+        @Override
196
+        public void toggleElementInspector() {
197
+            mCommandsHandler.toggleElementInspector();
198
+        }
199
+    }
116 200
 }
117 201
 

+ 0
- 2
android/app/src/main/java/com/reactnativenavigation/utils/IconUtils.java Datei anzeigen

@@ -1,13 +1,11 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.content.Context;
4
-import android.content.res.Resources;
5 4
 import android.graphics.Bitmap;
6 5
 import android.graphics.BitmapFactory;
7 6
 import android.graphics.drawable.BitmapDrawable;
8 7
 import android.graphics.drawable.Drawable;
9 8
 import android.net.Uri;
10
-import android.util.DisplayMetrics;
11 9
 
12 10
 import com.reactnativenavigation.BuildConfig;
13 11
 

+ 25
- 4
android/app/src/main/java/com/reactnativenavigation/utils/ReflectionUtils.java Datei anzeigen

@@ -1,24 +1,45 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import java.lang.reflect.Field;
4
+import java.lang.reflect.Method;
4 5
 
5 6
 /**
6 7
  * Created by guyc on 14/04/16.
7 8
  */
8 9
 public class ReflectionUtils {
9 10
 
10
-    public static boolean setBooleanField(Object obj, String name, Boolean value) {
11
+    public static boolean setField(Object obj, String name, Object value) {
11 12
         Field field;
12 13
         try {
13 14
             field = obj.getClass().getDeclaredField(name);
14 15
             field.setAccessible(true);
15 16
             field.set(obj, value);
16 17
             return true;
17
-        } catch (NoSuchFieldException e) {
18
-            e.printStackTrace();
19
-        } catch (IllegalAccessException e) {
18
+        } catch (Exception e) {
20 19
             e.printStackTrace();
21 20
         }
22 21
         return false;
23 22
     }
23
+
24
+    public static Object getDeclaredField(Object obj, String fieldName) {
25
+        try {
26
+            Field f = obj.getClass().getDeclaredField(fieldName);
27
+            f.setAccessible(true);
28
+            return f.get(obj);
29
+        } catch (Exception e) {
30
+            e.printStackTrace();
31
+        }
32
+        return null;
33
+    }
34
+
35
+    public static Object invoke(Object object, String methodName) {
36
+        try {
37
+            Method method = object.getClass().getDeclaredMethod(methodName);
38
+            method.setAccessible(true);
39
+            return method.invoke(object);
40
+        } catch (Exception e) {
41
+            e.printStackTrace();
42
+        }
43
+        return null;
44
+    }
24 45
 }

+ 7
- 3
android/app/src/main/java/com/reactnativenavigation/views/RctView.java Datei anzeigen

@@ -67,7 +67,7 @@ public class RctView extends FrameLayout {
67 67
      */
68 68
     public void onTemporallyRemovedFromScreen() {
69 69
         // Hack in order to prevent the react view from getting unmounted
70
-        ReflectionUtils.setBooleanField(mReactRootView, "mAttachScheduled", true);
70
+        ReflectionUtils.setField(mReactRootView, "mAttachScheduled", true);
71 71
     }
72 72
 
73 73
     /**
@@ -75,7 +75,7 @@ public class RctView extends FrameLayout {
75 75
      * executed and componentWillUnmount is called
76 76
      */
77 77
     public void onRemoveFromScreen() {
78
-        ReflectionUtils.setBooleanField(mReactRootView, "mAttachScheduled", false);
78
+        ReflectionUtils.setField(mReactRootView, "mAttachScheduled", false);
79 79
     }
80 80
 
81 81
     /**
@@ -83,7 +83,11 @@ public class RctView extends FrameLayout {
83 83
      * executed and componentWillUnmount is called
84 84
      */
85 85
     public void onReAddToScreen() {
86
-        ReflectionUtils.setBooleanField(mReactRootView, "mAttachScheduled", false);
86
+        ReflectionUtils.setField(mReactRootView, "mAttachScheduled", false);
87
+    }
88
+
89
+    public void detachFromScreen() {
90
+        ReflectionUtils.invoke(mReactRootView, "onDetachedFromWindow");
87 91
     }
88 92
 }
89 93
 

+ 11
- 0
android/app/src/main/java/com/reactnativenavigation/views/ScreenStack.java Datei anzeigen

@@ -165,4 +165,15 @@ public class ScreenStack extends FrameLayout {
165 165
 
166 166
         parent.addView(this, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
167 167
     }
168
+
169
+    public void removeAllReactViews() {
170
+        while (!mStack.empty()) {
171
+            RctView view = mStack.pop().view;
172
+            // Ensure view will be properly detached and unmounted
173
+            view.onRemoveFromScreen();
174
+            // Unmount the view
175
+            view.detachFromScreen();
176
+        }
177
+        removeAllViews();
178
+    }
168 179
 }