Skip to content

Commit 396b4d7

Browse files
committed
Fixes rare IllegalThreadStateException start()
* The root issue is that `Thread.start()` could be called a 2nd time if there was a problem getting a Handler after starting the thread. Getting a Handler instance was used as the deturming factor if the thread had started or not. * When calling `Thread.start()` there is no guarantee that the thread has started after finishing or that is may have alrady completed. * To overcome the following 2 changes were made 1. Override `HandlerThread`'s `onLooperPrepared` - Moving our `getLooper()` call here guarantees it will never be `null` - Then this means we can create a new `Handler` everytime successfully 2. Added a boolean to make sure we only call `start()` once - `threadStartCalled` is set to `true` after `start()` is called. - This is needed as `onLooperPrepared` may not fire by the time some other part of the OneSignal SDK calls `scheduleFlushToDiskJob` again.
1 parent 386aa03 commit 396b4d7

File tree

1 file changed

+31
-15
lines changed

1 file changed

+31
-15
lines changed

OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalPrefs.java

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -132,23 +132,43 @@ public static class WritePrefHandlerThread extends HandlerThread {
132132
super(name);
133133
}
134134

135+
@Override
136+
protected void onLooperPrepared() {
137+
super.onLooperPrepared();
138+
139+
// Getting handler here as onLooperPrepared guarantees getLooper() will not return null
140+
mHandler = new Handler(getLooper());
141+
scheduleFlushToDiskJob();
142+
}
143+
135144
private synchronized void startDelayedWrite() {
136145
// A Context is required to write,
137146
// if not available now later OneSignal.setContext will call this again.
138147
if (OneSignal.appContext == null)
139148
return;
140149

141-
if (mHandler == null) {
142-
startThread();
143-
mHandler = new Handler(getLooper());
144-
}
150+
startThread();
151+
scheduleFlushToDiskJob();
152+
}
153+
154+
private synchronized void scheduleFlushToDiskJob() {
155+
// Could be null if looper thread just started
156+
if (mHandler == null)
157+
return;
145158

146159
mHandler.removeCallbacksAndMessages(null);
160+
147161
if (lastSyncTime == 0)
148162
lastSyncTime = System.currentTimeMillis();
149-
150163
long delay = lastSyncTime - System.currentTimeMillis() + WRITE_CALL_DELAY_TO_BUFFER_MS;
151-
mHandler.postDelayed(getNewRunnable(), delay);
164+
165+
Runnable runnable = new Runnable() {
166+
@Override
167+
public void run() {
168+
flushBufferToDisk();
169+
}
170+
};
171+
mHandler.postDelayed(runnable, delay);
152172
}
153173

154174
/**
@@ -172,7 +192,11 @@ private synchronized void startDelayedWrite() {
172192
private RuntimeException threadStartRuntimeException;
173193
private Throwable threadStartThrowable;
174194

195+
private boolean threadStartCalled;
175196
private void startThread() {
197+
if (threadStartCalled)
198+
return;
199+
176200
if (threadStartError != null)
177201
throw threadStartError;
178202

@@ -199,6 +223,7 @@ private void startThread() {
199223

200224
try {
201225
start();
226+
threadStartCalled = true;
202227
} catch (InternalError e) {
203228
// Thread starting during runtime shutdown
204229
threadStartError = e;
@@ -232,15 +257,6 @@ private void startThread() {
232257
}
233258
}
234259

235-
private Runnable getNewRunnable() {
236-
return new Runnable() {
237-
@Override
238-
public void run() {
239-
flushBufferToDisk();
240-
}
241-
};
242-
}
243-
244260
private void flushBufferToDisk() {
245261
for (String pref : prefsToApply.keySet()) {
246262
SharedPreferences prefsToWrite = getSharedPrefsByName(pref);

0 commit comments

Comments
 (0)