Преглед на файлове

[RN] Make all delegate / listener methods run in the main / UI thread

j8
Saúl Ibarra Corretgé преди 7 години
родител
ревизия
ef7fb1a7b0

+ 11
- 0
android/app/src/main/java/org/jitsi/meet/MainActivity.java Целия файл

@@ -28,6 +28,7 @@ import org.jitsi.meet.sdk.invite.InviteController;
28 28
 import org.jitsi.meet.sdk.invite.InviteControllerListener;
29 29
 
30 30
 import com.calendarevents.CalendarEventsPackage;
31
+import com.facebook.react.bridge.UiThreadUtil;
31 32
 
32 33
 import java.util.ArrayList;
33 34
 import java.util.HashMap;
@@ -65,6 +66,8 @@ public class MainActivity extends JitsiMeetActivity {
65 66
         if (BuildConfig.DEBUG && view != null) {
66 67
             view.setListener(new JitsiMeetViewListener() {
67 68
                 private void on(String name, Map<String, Object> data) {
69
+                    UiThreadUtil.assertOnUiThread();
70
+
68 71
                     // Log with the tag "ReactNative" in order to have the log
69 72
                     // visible in react-native log-android as well.
70 73
                     Log.d(
@@ -112,6 +115,8 @@ public class MainActivity extends JitsiMeetActivity {
112 115
             inviteController.setListener(new InviteControllerListener() {
113 116
                 public void beginAddPeople(
114 117
                         AddPeopleController addPeopleController) {
118
+                    UiThreadUtil.assertOnUiThread();
119
+
115 120
                     onInviteControllerBeginAddPeople(
116 121
                         inviteController,
117 122
                         addPeopleController);
@@ -129,6 +134,8 @@ public class MainActivity extends JitsiMeetActivity {
129 134
     private void onAddPeopleControllerInviteSettled(
130 135
             AddPeopleController addPeopleController,
131 136
             List<Map<String, Object>> failedInvitees) {
137
+        UiThreadUtil.assertOnUiThread();
138
+
132 139
         // XXX Explicitly invoke endAddPeople on addPeopleController; otherwise,
133 140
         // it is going to be memory-leaked in the associated InviteController
134 141
         // and no subsequent InviteButton clicks/taps will be delivered.
@@ -142,6 +149,8 @@ public class MainActivity extends JitsiMeetActivity {
142 149
             AddPeopleController addPeopleController,
143 150
             List<Map<String, Object>> results,
144 151
             String query) {
152
+        UiThreadUtil.assertOnUiThread();
153
+
145 154
         int size = results.size();
146 155
 
147 156
         if (size > 0) {
@@ -182,6 +191,8 @@ public class MainActivity extends JitsiMeetActivity {
182 191
     private void onInviteControllerBeginAddPeople(
183 192
             InviteController inviteController,
184 193
             AddPeopleController addPeopleController) {
194
+        UiThreadUtil.assertOnUiThread();
195
+
185 196
         // Log with the tag "ReactNative" in order to have the log visible in
186 197
         // react-native log-android as well.
187 198
         Log.d(

+ 47
- 39
android/sdk/src/main/java/org/jitsi/meet/sdk/ExternalAPIModule.java Целия файл

@@ -21,9 +21,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
21 21
 import com.facebook.react.bridge.ReactMethod;
22 22
 import com.facebook.react.bridge.ReadableMap;
23 23
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
24
-
25
-import org.jitsi.meet.sdk.JitsiMeetView;
26
-import org.jitsi.meet.sdk.JitsiMeetViewListener;
24
+import com.facebook.react.bridge.UiThreadUtil;
27 25
 
28 26
 import java.lang.reflect.InvocationTargetException;
29 27
 import java.lang.reflect.Method;
@@ -150,43 +148,53 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
150 148
      * @param scope
151 149
      */
152 150
     @ReactMethod
153
-    public void sendEvent(String name, ReadableMap data, String scope) {
154
-        // The JavaScript App needs to provide uniquely identifying information
155
-        // to the native ExternalAPI module so that the latter may match the
156
-        // former to the native JitsiMeetView which hosts it.
157
-        JitsiMeetView view = JitsiMeetView.findViewByExternalAPIScope(scope);
158
-
159
-        if (view == null) {
160
-            return;
161
-        }
162
-
163
-        maybeSetViewURL(name, data, view);
164
-
165
-        JitsiMeetViewListener listener = view.getListener();
166
-
167
-        if (listener == null) {
168
-            return;
169
-        }
170
-
171
-        Method method = JITSI_MEET_VIEW_LISTENER_METHODS.get(name);
172
-
173
-        if (method != null) {
174
-            try {
175
-                method.invoke(listener, toHashMap(data));
176
-            } catch (IllegalAccessException e) {
177
-                // FIXME There was a multicatch for IllegalAccessException and
178
-                // InvocationTargetException, but Android Studio complained
179
-                // with:
180
-                // "Multi-catch with these reflection exceptions requires
181
-                // API level 19 (current min is 16) because they get compiled to
182
-                // the common but new super type ReflectiveOperationException.
183
-                // As a workaround either create individual catch statements, or
184
-                // catch Exception."
185
-                throw new RuntimeException(e);
186
-            } catch (InvocationTargetException e) {
187
-                throw new RuntimeException(e);
151
+    public void sendEvent(final String name,
152
+                          final ReadableMap data,
153
+                          final String scope) {
154
+        UiThreadUtil.runOnUiThread(new Runnable() {
155
+            @Override
156
+            public void run() {
157
+                // The JavaScript App needs to provide uniquely identifying
158
+                // information to the native ExternalAPI module so that the
159
+                // latter may match the former to the native JitsiMeetView which
160
+                // hosts it.
161
+                JitsiMeetView view
162
+                    = JitsiMeetView.findViewByExternalAPIScope(scope);
163
+
164
+                if (view == null) {
165
+                    return;
166
+                }
167
+
168
+                maybeSetViewURL(name, data, view);
169
+
170
+                JitsiMeetViewListener listener = view.getListener();
171
+
172
+                if (listener == null) {
173
+                    return;
174
+                }
175
+
176
+                Method method = JITSI_MEET_VIEW_LISTENER_METHODS.get(name);
177
+
178
+                if (method != null) {
179
+                    try {
180
+                        method.invoke(listener, toHashMap(data));
181
+                    } catch (IllegalAccessException e) {
182
+                        // FIXME There was a multicatch for
183
+                        // IllegalAccessException and InvocationTargetException,
184
+                        // but Android Studio complained with: "Multi-catch with
185
+                        // these reflection exceptions requires API level 19
186
+                        // (current min is 16) because they get compiled to the
187
+                        // common but new super type
188
+                        // ReflectiveOperationException. As a workaround either
189
+                        // create individual catch statements, or
190
+                        // catch Exception."
191
+                        throw new RuntimeException(e);
192
+                    } catch (InvocationTargetException e) {
193
+                        throw new RuntimeException(e);
194
+                    }
195
+                }
188 196
             }
189
-        }
197
+        });
190 198
     }
191 199
 
192 200
     /**

+ 52
- 36
android/sdk/src/main/java/org/jitsi/meet/sdk/invite/InviteModule.java Целия файл

@@ -22,6 +22,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
22 22
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
23 23
 import com.facebook.react.bridge.ReactMethod;
24 24
 import com.facebook.react.bridge.ReadableArray;
25
+import com.facebook.react.bridge.UiThreadUtil;
25 26
 
26 27
 import org.jitsi.meet.sdk.JitsiMeetView;
27 28
 
@@ -42,13 +43,18 @@ public class InviteModule extends ReactContextBaseJavaModule {
42 43
      * {@code JitsiMeetView} whose {@code InviteButton} was clicked/tapped.
43 44
      */
44 45
     @ReactMethod
45
-    public void beginAddPeople(String externalAPIScope) {
46
-        InviteController inviteController
47
-            = findInviteControllerByExternalAPIScope(externalAPIScope);
46
+    public void beginAddPeople(final String externalAPIScope) {
47
+        UiThreadUtil.runOnUiThread(new Runnable() {
48
+            @Override
49
+            public void run() {
50
+                InviteController inviteController
51
+                    = findInviteControllerByExternalAPIScope(externalAPIScope);
48 52
 
49
-        if (inviteController != null) {
50
-            inviteController.beginAddPeople(getReactApplicationContext());
51
-        }
53
+                if (inviteController != null) {
54
+                    inviteController.beginAddPeople(getReactApplicationContext());
55
+                }
56
+            }
57
+        });
52 58
     }
53 59
 
54 60
     private InviteController findInviteControllerByExternalAPIScope(
@@ -72,21 +78,26 @@ public class InviteModule extends ReactContextBaseJavaModule {
72 78
      */
73 79
     @ReactMethod
74 80
     public void inviteSettled(
75
-            String externalAPIScope,
76
-            String addPeopleControllerScope,
77
-            ReadableArray failedInvitees) {
78
-        InviteController inviteController
79
-            = findInviteControllerByExternalAPIScope(externalAPIScope);
81
+            final String externalAPIScope,
82
+            final String addPeopleControllerScope,
83
+            final ReadableArray failedInvitees) {
84
+        UiThreadUtil.runOnUiThread(new Runnable() {
85
+            @Override
86
+            public void run() {
87
+                InviteController inviteController
88
+                    = findInviteControllerByExternalAPIScope(externalAPIScope);
80 89
 
81
-        if (inviteController == null) {
82
-            Log.w(
83
-                "InviteModule",
84
-                "Invite settled, but failed to find active controller to notify");
85
-        } else {
86
-            inviteController.inviteSettled(
87
-                addPeopleControllerScope,
88
-                failedInvitees);
89
-        }
90
+                if (inviteController == null) {
91
+                    Log.w(
92
+                        "InviteModule",
93
+                        "Invite settled, but failed to find active controller to notify");
94
+                } else {
95
+                    inviteController.inviteSettled(
96
+                        addPeopleControllerScope,
97
+                        failedInvitees);
98
+                }
99
+            }
100
+        });
90 101
     }
91 102
 
92 103
     /**
@@ -98,22 +109,27 @@ public class InviteModule extends ReactContextBaseJavaModule {
98 109
      */
99 110
     @ReactMethod
100 111
     public void receivedResults(
101
-            String externalAPIScope,
102
-            String addPeopleControllerScope,
103
-            String query,
104
-            ReadableArray results) {
105
-        InviteController inviteController
106
-            = findInviteControllerByExternalAPIScope(externalAPIScope);
112
+            final String externalAPIScope,
113
+            final String addPeopleControllerScope,
114
+            final String query,
115
+            final ReadableArray results) {
116
+        UiThreadUtil.runOnUiThread(new Runnable() {
117
+            @Override
118
+            public void run() {
119
+                InviteController inviteController
120
+                    = findInviteControllerByExternalAPIScope(externalAPIScope);
107 121
 
108
-        if (inviteController == null) {
109
-            Log.w(
110
-                "InviteModule",
111
-                "Received results, but failed to find active controller to send results back");
112
-        } else {
113
-            inviteController.receivedResultsForQuery(
114
-                addPeopleControllerScope,
115
-                query,
116
-                results);
117
-        }
122
+                if (inviteController == null) {
123
+                    Log.w(
124
+                        "InviteModule",
125
+                        "Received results, but failed to find active controller to send results back");
126
+                } else {
127
+                    inviteController.receivedResultsForQuery(
128
+                        addPeopleControllerScope,
129
+                        query,
130
+                        results);
131
+                }
132
+            }
133
+        });
118 134
     }
119 135
 }

+ 14
- 0
ios/app/src/ViewController.m Целия файл

@@ -14,6 +14,8 @@
14 14
  * limitations under the License.
15 15
  */
16 16
 
17
+#import <assert.h>
18
+
17 19
 #import "ViewController.h"
18 20
 
19 21
 /**
@@ -64,6 +66,9 @@ void _onJitsiMeetViewDelegateEvent(NSString *name, NSDictionary *data) {
64 66
     NSLog(
65 67
         @"[%s:%d] JitsiMeetViewDelegate %@ %@",
66 68
         __FILE__, __LINE__, name, data);
69
+
70
+    assert([NSThread isMainThread]
71
+        && "Delegate method called in a non-main thread");
67 72
 }
68 73
 
69 74
 - (void)conferenceFailed:(NSDictionary *)data {
@@ -97,6 +102,9 @@ void _onJitsiMeetViewDelegateEvent(NSString *name, NSDictionary *data) {
97 102
         @"[%s:%d] JMInviteControllerDelegate %s",
98 103
         __FILE__, __LINE__, __FUNCTION__);
99 104
 
105
+    assert([NSThread isMainThread]
106
+        && "Delegate method called in a non-main thread");
107
+
100 108
     NSString *query = ADD_PEOPLE_CONTROLLER_QUERY;
101 109
     JitsiMeetView *view = (JitsiMeetView *) self.view;
102 110
     JMInviteController *inviteController = view.inviteController;
@@ -119,6 +127,9 @@ void _onJitsiMeetViewDelegateEvent(NSString *name, NSDictionary *data) {
119 127
 - (void)addPeopleController:(JMAddPeopleController * _Nonnull)controller
120 128
           didReceiveResults:(NSArray<NSDictionary *> * _Nonnull)results
121 129
                    forQuery:(NSString * _Nonnull)query {
130
+    assert([NSThread isMainThread]
131
+        && "Delegate method called in a non-main thread");
132
+
122 133
     NSUInteger count = results.count;
123 134
 
124 135
     if (count) {
@@ -151,6 +162,9 @@ void _onJitsiMeetViewDelegateEvent(NSString *name, NSDictionary *data) {
151 162
 
152 163
 - (void) inviteSettled:(NSArray<NSDictionary *> * _Nonnull)failedInvitees
153 164
   fromSearchController:(JMAddPeopleController * _Nonnull)addPeopleController {
165
+    assert([NSThread isMainThread]
166
+        && "Delegate method called in a non-main thread");
167
+
154 168
     // XXX Explicitly invoke endAddPeople on addPeopleController; otherwise, it
155 169
     // is going to be memory-leaked in the associated JMInviteController and no
156 170
     // subsequent InviteButton clicks/taps will be delivered. Technically,

+ 7
- 0
ios/sdk/src/ExternalAPI.m Целия файл

@@ -25,6 +25,13 @@
25 25
 
26 26
 RCT_EXPORT_MODULE();
27 27
 
28
+/**
29
+ * Make sure all methods in this module are called in the main (i.e. UI) thread.
30
+ */
31
+- (dispatch_queue_t)methodQueue {
32
+    return dispatch_get_main_queue();
33
+}
34
+
28 35
 /**
29 36
  * Dispatches an event that occurred on JavaScript to the view's delegate.
30 37
  *

+ 7
- 0
ios/sdk/src/invite/Invite.m Целия файл

@@ -40,6 +40,13 @@ RCT_EXPORT_MODULE();
40 40
     ];
41 41
 }
42 42
 
43
+/**
44
+ * Make sure all methods in this module are called in the main (i.e. UI) thread.
45
+ */
46
+- (dispatch_queue_t)methodQueue {
47
+    return dispatch_get_main_queue();
48
+}
49
+
43 50
 /**
44 51
  * Initiates the process to add people. This involves calling a delegate method
45 52
  * in the JMInviteControllerDelegate so the native host application can start

Loading…
Отказ
Запис