Do not use WeakReference for listeners (fixes #328).

This makes sure listeners are not invoked after being destroyed.

Also call onApiChange() on GUI thread.
This commit is contained in:
Felix Ableitner 2015-04-25 01:50:08 +02:00
parent 7ddee2f953
commit e821d2e468
6 changed files with 68 additions and 30 deletions

View File

@ -109,6 +109,12 @@ public class FolderPickerActivity extends SyncthingActivity
getService().registerOnApiChangeListener(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
getService().unregisterOnApiChangeListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mListView.getAdapter() == mRootsAdapter)

View File

@ -44,8 +44,6 @@ public class MainActivity extends SyncthingActivity
private AlertDialog mDisabledDialog;
private boolean mIsDestroyed = false;
/**
* Causes population of folder and device lists, unlocks info drawer.
*/
@ -55,7 +53,7 @@ public class MainActivity extends SyncthingActivity
runOnUiThread(new Runnable() {
@Override
public void run() {
if (currentState != SyncthingService.State.ACTIVE && !isFinishing() && !mIsDestroyed) {
if (currentState != SyncthingService.State.ACTIVE && !isFinishing()) {
if (currentState == SyncthingService.State.DISABLED) {
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
@ -215,7 +213,9 @@ public class MainActivity extends SyncthingActivity
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
}
mIsDestroyed = true;
getService().unregisterOnApiChangeListener(this);
getService().unregisterOnApiChangeListener(mFolderFragment);
getService().unregisterOnApiChangeListener(mDevicesFragment);
}
@Override

View File

@ -124,18 +124,22 @@ public class DeviceSettingsFragment extends PreferenceFragment implements
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
mSyncthingService.unregisterOnApiChangeListener(this);
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (getActivity() == null || getActivity().isFinishing()) {
if (currentState != SyncthingService.State.ACTIVE) {
getActivity().finish();
return;
}
else if (currentState != SyncthingService.State.ACTIVE) {
getActivity().finish();
}
if (mIsCreate) {
if (mIsCreate)
getActivity().setTitle(R.string.add_device);
} else {
else {
RestApi.Device device = null;
getActivity().setTitle(R.string.edit_device);
List<RestApi.Device> devices = mSyncthingService.getApi().getDevices(false);

View File

@ -121,10 +121,7 @@ public class FolderSettingsFragment extends PreferenceFragment
@Override
public void onApiChange(SyncthingService.State currentState) {
if (getActivity() == null || getActivity().isFinishing()) {
return;
}
else if (currentState != SyncthingService.State.ACTIVE) {
if (currentState != SyncthingService.State.ACTIVE) {
getActivity().finish();
return;
}
@ -185,6 +182,12 @@ public class FolderSettingsFragment extends PreferenceFragment
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
mSyncthingService.unregisterOnApiChangeListener(this);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);

View File

@ -160,6 +160,12 @@ public class SettingsFragment extends PreferenceFragment
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
mSyncthingService.unregisterOnApiChangeListener(this);
}
/**
* Handles ActionBar back selected.
*/

View File

@ -14,6 +14,7 @@ import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
@ -31,7 +32,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.channels.FileChannel;
import java.security.SecureRandom;
import java.util.HashSet;
@ -92,6 +92,8 @@ public class SyncthingService extends Service {
private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this);
private Handler mMainHandler;
/**
* Callback for when the Syncthing web interface becomes first available after service start.
*/
@ -106,7 +108,7 @@ public class SyncthingService extends Service {
public void onApiChange(State currentState);
}
private final HashSet<WeakReference<OnApiChangeListener>> mOnApiChangeListeners =
private final HashSet<OnApiChangeListener> mOnApiChangeListeners =
new HashSet<>();
/**
@ -139,10 +141,10 @@ public class SyncthingService extends Service {
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Just catch the empty intent and return.
if (intent == null) {
}
else if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
if (intent == null)
return START_STICKY;
if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
mApi.shutdown();
mCurrentState = State.INIT;
updateState();
@ -253,6 +255,7 @@ public class SyncthingService extends Service {
.putString("gui_password", sb.toString()).commit();
}
mMainHandler = new Handler();
mDeviceStateHolder = new DeviceStateHolder(SyncthingService.this);
registerReceiver(mDeviceStateHolder, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
new StartupTask().execute();
@ -347,13 +350,24 @@ public class SyncthingService extends Service {
* Register a listener for the syncthing API state changing.
*
* The listener is called immediately with the current state, and again whenever the state
* changes.
* changes. The call is always from the GUI thread.
*
* @see {@link #unregisterOnApiChangeListener}
*/
public void registerOnApiChangeListener(OnApiChangeListener listener) {
// Make sure we don't send an invalid state or syncthing might show a "disabled" message
// when it's just starting up.
listener.onApiChange(mCurrentState);
mOnApiChangeListeners.add(new WeakReference<>(listener));
mOnApiChangeListeners.add(listener);
}
/**
* Unregisters a previously registered listener.
*
* @see {@link #registerOnApiChangeListener}
*/
public void unregisterOnApiChangeListener(OnApiChangeListener listener) {
mOnApiChangeListeners.remove(listener);
}
private class PollWebGuiAvailableTaskImpl extends PollWebGuiAvailableTask {
@ -382,15 +396,20 @@ public class SyncthingService extends Service {
* Must only be called from SyncthingService or {@link RestApi}.
*/
private void onApiChange() {
for (Iterator<WeakReference<OnApiChangeListener>> i = mOnApiChangeListeners.iterator();
i.hasNext(); ) {
WeakReference<OnApiChangeListener> listener = i.next();
if (listener.get() != null) {
listener.get().onApiChange(mCurrentState);
} else {
i.remove();
mMainHandler.post(new Runnable() {
@Override
public void run() {
for (Iterator<OnApiChangeListener> i = mOnApiChangeListeners.iterator();
i.hasNext(); ) {
OnApiChangeListener listener = i.next();
if (listener != null) {
listener.onApiChange(mCurrentState);
} else {
i.remove();
}
}
}
}
});
}
/**