@ammarahmed/react-native-background-fetch 4.2.2 → 4.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,12 @@ import android.content.Context;
4
4
  import android.os.Handler;
5
5
  import android.util.Log;
6
6
 
7
+ import androidx.annotation.NonNull;
8
+ import androidx.annotation.Nullable;
9
+
10
+ import com.facebook.infer.annotation.Assertions;
7
11
  import com.facebook.react.ReactApplication;
12
+ import com.facebook.react.ReactInstanceEventListener;
8
13
  import com.facebook.react.ReactInstanceManager;
9
14
  import com.facebook.react.ReactNativeHost;
10
15
  import com.facebook.react.bridge.ReactContext;
@@ -13,50 +18,34 @@ import com.facebook.react.bridge.WritableMap;
13
18
  import com.facebook.react.bridge.WritableNativeMap;
14
19
  import com.facebook.react.jstasks.HeadlessJsTaskConfig;
15
20
  import com.facebook.react.jstasks.HeadlessJsTaskContext;
16
- import com.facebook.react.jstasks.HeadlessJsTaskEventListener;
21
+
17
22
  import com.transistorsoft.tsbackgroundfetch.BGTask;
18
23
  import com.transistorsoft.tsbackgroundfetch.BackgroundFetch;
19
24
  import com.facebook.react.common.LifecycleState;
20
25
 
26
+ import java.lang.reflect.Method;
27
+
21
28
  /**
22
29
  * Created by chris on 2018-01-17.
23
30
  */
24
31
 
25
- public class HeadlessTask implements HeadlessJsTaskEventListener {
26
- private static String HEADLESS_TASK_NAME = "BackgroundFetch";
27
- private static Handler mHandler = new Handler();
28
- private ReactNativeHost mReactNativeHost;
29
- private HeadlessJsTaskContext mActiveTaskContext;
32
+ public class HeadlessTask {
33
+ private static final String HEADLESS_TASK_NAME = "BackgroundFetch";
34
+ private static final Handler mHandler = new Handler();
30
35
 
36
+ private final BGTask mBGTask;
31
37
  public HeadlessTask(Context context, BGTask task) {
32
- try {
33
- ReactApplication reactApplication = ((ReactApplication) context.getApplicationContext());
34
- mReactNativeHost = reactApplication.getReactNativeHost();
35
- } catch (AssertionError | ClassCastException e) {
36
- Log.e(BackgroundFetch.TAG, "Failed to fetch ReactApplication. Task ignored.");
37
- return; // <-- Do nothing. Just return
38
- }
38
+ mBGTask = task;
39
39
  WritableMap clientEvent = new WritableNativeMap();
40
40
  clientEvent.putString("taskId", task.getTaskId());
41
41
  clientEvent.putBoolean("timeout", task.getTimedOut());
42
42
  HeadlessJsTaskConfig config = new HeadlessJsTaskConfig(HEADLESS_TASK_NAME, clientEvent, 30000);
43
- startTask(config);
44
- }
45
-
46
- public void finish() {
47
- if (mActiveTaskContext != null) {
48
- mActiveTaskContext.removeTaskEventListener(this);
43
+ try {
44
+ startTask(config, context);
45
+ } catch (AssertionError e) {
46
+ Log.d(BackgroundFetch.TAG, "[HeadlessTask] Failed invoke HeadlessTask: " + e.getMessage());
49
47
  }
50
48
  }
51
- @Override
52
- public void onHeadlessJsTaskStart(int taskId) {
53
- Log.d(BackgroundFetch.TAG,"onHeadlessJsTaskStart: " + taskId);
54
- }
55
- @Override
56
- public void onHeadlessJsTaskFinish(int taskId) {
57
- Log.d(BackgroundFetch.TAG, "onHeadlessJsTaskFinish: " + taskId);
58
- mActiveTaskContext.removeTaskEventListener(this);
59
- }
60
49
 
61
50
  /**
62
51
  * Start a task. This method handles starting a new React instance if required.
@@ -65,27 +54,13 @@ public class HeadlessTask implements HeadlessJsTaskEventListener {
65
54
  *
66
55
  * @param taskConfig describes what task to start and the parameters to pass to it
67
56
  */
68
- protected void startTask(final HeadlessJsTaskConfig taskConfig) {
57
+ protected void startTask(final HeadlessJsTaskConfig taskConfig, Context context) {
69
58
  UiThreadUtil.assertOnUiThread();
70
- final ReactInstanceManager reactInstanceManager = mReactNativeHost.getReactInstanceManager();
71
- ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
59
+
60
+ ReactContext reactContext = getReactContext(context);
61
+
72
62
  if (reactContext == null) {
73
- reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
74
- @Override
75
- public void onReactContextInitialized(final ReactContext reactContext) {
76
- // Hack to fix unknown problem executing asynchronous BackgroundTask when ReactContext is created *first time*. Fixed by adding short delay before #invokeStartTask
77
- mHandler.postDelayed(new Runnable() {
78
- @Override
79
- public void run() {
80
- invokeStartTask(reactContext, taskConfig);
81
- }
82
- }, 500);
83
- reactInstanceManager.removeReactInstanceEventListener(this);
84
- }
85
- });
86
- if (!reactInstanceManager.hasStartedCreatingInitialContext()) {
87
- reactInstanceManager.createReactContextInBackground();
88
- }
63
+ createReactContextAndScheduleTask(taskConfig, context);
89
64
  } else {
90
65
  invokeStartTask(reactContext, taskConfig);
91
66
  }
@@ -96,24 +71,86 @@ public class HeadlessTask implements HeadlessJsTaskEventListener {
96
71
  return;
97
72
  }
98
73
  final HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(reactContext);
99
- headlessJsTaskContext.addTaskEventListener(this);
100
- mActiveTaskContext = headlessJsTaskContext;
74
+
75
+ UiThreadUtil.runOnUiThread(() -> {
76
+ try {
77
+ final int taskId = headlessJsTaskContext.startTask(taskConfig);
78
+ Log.d(BackgroundFetch.TAG, "[HeadlessTask] start taskId: " + taskId);
79
+ // Add a BGTask.finish(taskId) listener. This is executed when the user runs BackgroundFetch.finish(taskId).
80
+ // We use this to finish the RN headless task.
81
+ mBGTask.setCompletionHandler(() -> {
82
+ Log.d(BackgroundFetch.TAG, "[HeadlessTask] end taskId: " + taskId);
83
+ if (headlessJsTaskContext.isTaskRunning(taskId)) {
84
+ headlessJsTaskContext.finishTask(taskId);
85
+ }
86
+ });
87
+ } catch (IllegalStateException exception) {
88
+ Log.e(BackgroundFetch.TAG, "[HeadlessTask] task attempted to run in the foreground. Task ignored.");
89
+ }
90
+ });
91
+ }
92
+
93
+ private ReactNativeHost getReactNativeHost(Context context) {
94
+ return ((ReactApplication) context.getApplicationContext()).getReactNativeHost();
95
+ }
96
+
97
+ /**
98
+ * Get the {ReactHost} used by this app. ure and returns null if not.
99
+ */
100
+ private @Nullable Object getReactHost(Context context) {
101
+ context = context.getApplicationContext();
101
102
  try {
102
- UiThreadUtil.runOnUiThread(new Runnable() {
103
+ Method getReactHost = context.getClass().getMethod("getReactHost");
104
+ return getReactHost.invoke(context);
105
+ // Original non-reflection return:
106
+ //return ((ReactApplication) context.getApplicationContext()).getReactHost();
107
+ } catch (Exception e) {
108
+ Log.d(BackgroundFetch.TAG, "[HeadlessTask] Reflection error ReactHost: " + e);
109
+ return null;
110
+ }
111
+ }
112
+
113
+ private ReactContext getReactContext(Context context) {
114
+
115
+ Object reactHost = getReactHost(context);
116
+ Assertions.assertNotNull(reactHost, "getReactHost() is null in New Architecture");
117
+ try {
118
+ Method getCurrentReactContext = reactHost.getClass().getMethod("getCurrentReactContext");
119
+ return (ReactContext) getCurrentReactContext.invoke(reactHost);
120
+ } catch (Exception e) {
121
+ Log.e(BackgroundFetch.TAG, "[HeadlessTask] Reflection error getCurrentReactContext: " + e);
122
+ return null;
123
+ }
124
+ }
125
+
126
+ private void createReactContextAndScheduleTask(final HeadlessJsTaskConfig taskConfig, Context context) {
127
+ Log.d(BackgroundFetch.TAG, "[HeadlessTask] initializing ReactContext");
128
+
129
+ final Object reactHost = getReactHost(context);
130
+
131
+ ReactInstanceEventListener callback = new ReactInstanceEventListener() {
103
132
  @Override
104
- public void run() {
133
+ public void onReactContextInitialized(@NonNull ReactContext reactContext) {
134
+ mHandler.postDelayed(() -> invokeStartTask(reactContext, taskConfig), 500);
105
135
  try {
106
- int taskId = headlessJsTaskContext.startTask(taskConfig);
107
- } catch (IllegalStateException exception) {
108
- Log.e(BackgroundFetch.TAG, "Headless task attempted to run in the foreground. Task ignored.");
109
- return; // <-- Do nothing. Just return
136
+ Method removeReactInstanceEventListener = reactHost.getClass().getMethod("removeReactInstanceEventListener", ReactInstanceEventListener.class);
137
+ removeReactInstanceEventListener.invoke(reactHost, this);
138
+ } catch (Exception e) {
139
+ Log.e(BackgroundFetch.TAG, "[HeadlessTask] reflection error removeReactInstanceEventListener" + e);
110
140
  }
111
141
  }
112
- });
113
- } catch (IllegalStateException exception) {
114
- Log.e(BackgroundFetch.TAG, "Headless task attempted to run in the foreground. Task ignored.");
115
- return; // <-- Do nothing. Just return
116
- }
142
+ };
143
+
144
+ try {
145
+ Method addReactInstanceEventListener = reactHost.getClass().getMethod("addReactInstanceEventListener", ReactInstanceEventListener.class);
146
+ addReactInstanceEventListener.invoke(reactHost, callback);
147
+ Method startReactHost = reactHost.getClass().getMethod("start");
148
+ startReactHost.invoke(reactHost);
149
+ } catch (Exception e) {
150
+ Log.e(BackgroundFetch.TAG, "[HeadlessTask] reflection error addReactInstanceEventListener: " + e);
151
+ }
117
152
 
118
153
  }
119
- }
154
+
155
+
156
+ }
@@ -65,6 +65,7 @@ public class BGTask {
65
65
  }
66
66
  }
67
67
 
68
+ private final List<FetchJobService.CompletionHandler> mCompletionHandlers = new ArrayList<>();
68
69
  private FetchJobService.CompletionHandler mCompletionHandler;
69
70
  private String mTaskId;
70
71
  private int mJobId;
@@ -73,7 +74,8 @@ public class BGTask {
73
74
 
74
75
  BGTask(final Context context, String taskId, FetchJobService.CompletionHandler handler, int jobId) {
75
76
  mTaskId = taskId;
76
- mCompletionHandler = handler;
77
+ //mCompletionHandler = handler;
78
+ mCompletionHandlers.add(handler);
77
79
  mJobId = jobId;
78
80
 
79
81
  mTimeoutTask = new Runnable() {
@@ -96,18 +98,30 @@ public class BGTask {
96
98
  return ((mTaskId != null) && mTaskId.equalsIgnoreCase(taskId));
97
99
  }
98
100
 
99
- void setCompletionHandler(FetchJobService.CompletionHandler handler) {
100
- mCompletionHandler = handler;
101
+ public void setCompletionHandler(FetchJobService.CompletionHandler handler) {
102
+ synchronized (mCompletionHandlers) {
103
+ mCompletionHandlers.add(handler);
104
+ }
105
+ //mCompletionHandler = handler;
101
106
  }
102
107
 
103
108
  void finish() {
109
+ synchronized (mCompletionHandlers) {
110
+ for (FetchJobService.CompletionHandler handler : mCompletionHandlers) {
111
+ handler.finish();
112
+ }
113
+ mCompletionHandlers.clear();
114
+ }
115
+ /*
104
116
  if (mCompletionHandler != null) {
105
117
  mCompletionHandler.finish();
106
118
  }
119
+ */
107
120
  if (mTimeoutTask != null) {
108
121
  BackgroundFetch.getUiHandler().removeCallbacks(mTimeoutTask);
109
122
  }
110
- mCompletionHandler = null;
123
+
124
+ //mCompletionHandler = null;
111
125
  removeTask(mTaskId);
112
126
  }
113
127
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ammarahmed/react-native-background-fetch",
3
- "version": "4.2.2",
3
+ "version": "4.2.3",
4
4
  "description": "iOS & Android BackgroundFetch API implementation for React Native",
5
5
  "scripts": {
6
6
  "build": "yarn run build:expo",