Browse Source

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

master
Saúl Ibarra Corretgé 7 years ago
parent
commit
ef7fb1a7b0

+ 11
- 0
android/app/src/main/java/org/jitsi/meet/MainActivity.java View File

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

+ 47
- 39
android/sdk/src/main/java/org/jitsi/meet/sdk/ExternalAPIModule.java View File

21
 import com.facebook.react.bridge.ReactMethod;
21
 import com.facebook.react.bridge.ReactMethod;
22
 import com.facebook.react.bridge.ReadableMap;
22
 import com.facebook.react.bridge.ReadableMap;
23
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
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
 import java.lang.reflect.InvocationTargetException;
26
 import java.lang.reflect.InvocationTargetException;
29
 import java.lang.reflect.Method;
27
 import java.lang.reflect.Method;
150
      * @param scope
148
      * @param scope
151
      */
149
      */
152
     @ReactMethod
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 View File

22
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
22
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
23
 import com.facebook.react.bridge.ReactMethod;
23
 import com.facebook.react.bridge.ReactMethod;
24
 import com.facebook.react.bridge.ReadableArray;
24
 import com.facebook.react.bridge.ReadableArray;
25
+import com.facebook.react.bridge.UiThreadUtil;
25
 
26
 
26
 import org.jitsi.meet.sdk.JitsiMeetView;
27
 import org.jitsi.meet.sdk.JitsiMeetView;
27
 
28
 
42
      * {@code JitsiMeetView} whose {@code InviteButton} was clicked/tapped.
43
      * {@code JitsiMeetView} whose {@code InviteButton} was clicked/tapped.
43
      */
44
      */
44
     @ReactMethod
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
     private InviteController findInviteControllerByExternalAPIScope(
60
     private InviteController findInviteControllerByExternalAPIScope(
72
      */
78
      */
73
     @ReactMethod
79
     @ReactMethod
74
     public void inviteSettled(
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
      */
109
      */
99
     @ReactMethod
110
     @ReactMethod
100
     public void receivedResults(
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 View File

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

+ 7
- 0
ios/sdk/src/ExternalAPI.m View File

25
 
25
 
26
 RCT_EXPORT_MODULE();
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
  * Dispatches an event that occurred on JavaScript to the view's delegate.
36
  * Dispatches an event that occurred on JavaScript to the view's delegate.
30
  *
37
  *

+ 7
- 0
ios/sdk/src/invite/Invite.m View File

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
  * Initiates the process to add people. This involves calling a delegate method
51
  * Initiates the process to add people. This involves calling a delegate method
45
  * in the JMInviteControllerDelegate so the native host application can start
52
  * in the JMInviteControllerDelegate so the native host application can start

Loading…
Cancel
Save