diff options
| author | Samuel Tardieu <sam@rfc1149.net> | 2014-03-26 15:19:03 +0100 |
|---|---|---|
| committer | Samuel Tardieu <sam@rfc1149.net> | 2014-03-26 15:19:03 +0100 |
| commit | b6e28fc56d7517116bfa9e13fcfd0424de26e04a (patch) | |
| tree | a17ffb6f1eda4c7e6b2d236643157cd19e2f55ff /main | |
| parent | 6dd33e492d9123334ec0546414e574bbb10ad760 (diff) | |
| download | cgeo-b6e28fc56d7517116bfa9e13fcfd0424de26e04a.zip cgeo-b6e28fc56d7517116bfa9e13fcfd0424de26e04a.tar.gz cgeo-b6e28fc56d7517116bfa9e13fcfd0424de26e04a.tar.bz2 | |
Put out as much sensor work as possible onto background threads
The sensors signals and merging are now delivered on background handler
threads instead of the UI thread.
While this might not change a lot of things in practice, it may help
with #3680.
Diffstat (limited to 'main')
5 files changed, 145 insertions, 49 deletions
diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java index ef224ff..21307d2 100644 --- a/main/src/cgeo/geocaching/CgeoApplication.java +++ b/main/src/cgeo/geocaching/CgeoApplication.java @@ -39,12 +39,15 @@ public class CgeoApplication extends Application { public synchronized Observable<ImmutablePair<IGeoData, Float>> geoDirObservable() { if (geoDir == null) { - geoDir = Observable.combineLatest(GeoDataProvider.create(this), DirectionProvider.create(this), new Func2<IGeoData, Float, ImmutablePair<IGeoData, Float>>() { + final Observable<IGeoData> geo = GeoDataProvider.create(this); + final Observable<Float> dir = DirectionProvider.create(this); + final Observable<ImmutablePair<IGeoData, Float>> combined = Observable.combineLatest(geo, dir, new Func2<IGeoData, Float, ImmutablePair<IGeoData, Float>>() { @Override public ImmutablePair<IGeoData, Float> call(final IGeoData geoData, final Float dir) { - return new ImmutablePair<IGeoData, Float>(geoData, dir); + return ImmutablePair.of(geoData, dir); } }); + geoDir = combined.publish().refCount(); } return geoDir; } diff --git a/main/src/cgeo/geocaching/sensors/DirectionProvider.java b/main/src/cgeo/geocaching/sensors/DirectionProvider.java index 162f14c..8efbc1f 100644 --- a/main/src/cgeo/geocaching/sensors/DirectionProvider.java +++ b/main/src/cgeo/geocaching/sensors/DirectionProvider.java @@ -1,15 +1,13 @@ package cgeo.geocaching.sensors; +import android.os.Process; import cgeo.geocaching.compatibility.Compatibility; +import cgeo.geocaching.utils.StartableHandlerThread; import rx.Observable; import rx.Observable.OnSubscribe; import rx.Subscriber; -import rx.Subscription; -import rx.observables.ConnectableObservable; import rx.subjects.BehaviorSubject; -import rx.subscriptions.Subscriptions; -import rx.functions.Action0; import android.app.Activity; import android.content.Context; @@ -17,39 +15,24 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.os.*; -public class DirectionProvider implements OnSubscribe<Float> { +public class DirectionProvider { - private final SensorManager sensorManager; - private final BehaviorSubject<Float> subject = BehaviorSubject.create(0.0f); + private static final BehaviorSubject<Float> subject = BehaviorSubject.create(0.0f); - static public Observable<Float> create(final Context context) { - return new DirectionProvider((SensorManager) context.getSystemService(Context.SENSOR_SERVICE)).worker.refCount(); - } + static class Listener implements SensorEventListener, StartableHandlerThread.Callback { - private DirectionProvider(final SensorManager sensorManager) { - this.sensorManager = sensorManager; - } + private int count = 0; + private SensorManager sensorManager; - @Override - public void call(final Subscriber<? super Float> subscriber) { - subject.distinctUntilChanged().subscribe(subscriber); - } + @Override + public void onSensorChanged(final SensorEvent event) { + subject.onNext(event.values[0]); + } - private final ConnectableObservable<Float> worker = new ConnectableObservable<Float>(this) { @Override - public Subscription connect() { - @SuppressWarnings("deprecation") - // This will be removed when using a new location service. Until then, it is okay to be used. - final Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); - final SensorEventListener listener = new SensorEventListener() { - @Override - public void onSensorChanged(final SensorEvent event) { - subject.onNext(event.values[0]); - } - - @Override - public void onAccuracyChanged(final Sensor sensor, final int accuracy) { + public void onAccuracyChanged(final Sensor sensor, final int accuracy) { /* * There is a bug in Android, which apparently causes this method to be called every * time the sensor _value_ changed, even if the _accuracy_ did not change. So logging @@ -60,19 +43,43 @@ public class DirectionProvider implements OnSubscribe<Float> { * See for example https://code.google.com/p/android/issues/detail?id=14792 */ - //Log.i(Settings.tag, "Compass' accuracy is low (" + accuracy + ")"); - } - }; - - sensorManager.registerListener(listener, defaultSensor, SensorManager.SENSOR_DELAY_NORMAL); - return Subscriptions.create(new Action0() { - @Override - public void call() { - sensorManager.unregisterListener(listener); - } - }); + //Log.i(Settings.tag, "Compass' accuracy is low (" + accuracy + ")"); } - }; + + // This will be removed when using a new location service. Until then, it is okay to be used. + @SuppressWarnings("deprecation") + @Override + public void start(final Context context, final Handler handler) { + if (++count == 1) { + sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL, handler); + } + } + + @Override + public void stop() { + if (--count == 0) { + sensorManager.unregisterListener(this); + } + } + + } + + private static final StartableHandlerThread handlerThread = + new StartableHandlerThread("DirectionProvider thread", Process.THREAD_PRIORITY_BACKGROUND, new Listener()); + static { + handlerThread.start(); + } + + static public Observable<Float> create(final Context context) { + return Observable.create(new OnSubscribe<Float>() { + @Override + public void call(final Subscriber<? super Float> subscriber) { + handlerThread.start(subscriber, context); + subject.subscribe(subscriber); + } + }); + } /** * Take the phone rotation (through a given activity) in account and adjust the direction. diff --git a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java index 160d98d..a77b477 100644 --- a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java +++ b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java @@ -1,7 +1,9 @@ package cgeo.geocaching.sensors; +import android.os.*; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.StartableHandlerThread; import org.apache.commons.lang3.StringUtils; import rx.Observable; import rx.Observable.OnSubscribe; @@ -22,7 +24,6 @@ import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; -import android.os.Bundle; import java.util.concurrent.TimeUnit; @@ -33,6 +34,11 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> { private final LocationData gpsLocation = new LocationData(); private final LocationData netLocation = new LocationData(); private final BehaviorSubject<IGeoData> subject; + private static final StartableHandlerThread handlerThread = + new StartableHandlerThread("GeoDataProvider thread", android.os.Process.THREAD_PRIORITY_BACKGROUND); + static { + handlerThread.start(); + } public boolean gpsEnabled = false; public int satellitesVisible = 0; @@ -92,7 +98,7 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> { @Override public Subscription connect() { final CompositeSubscription subscription = new CompositeSubscription(); - AndroidSchedulers.mainThread().schedule(new Action1<Inner>() { + AndroidSchedulers.handlerThread(handlerThread.getHandler()).schedule(new Action1<Inner>() { @Override public void call(final Inner inner) { synchronized(lock) { @@ -112,7 +118,7 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> { subscription.add(Subscriptions.create(new Action0() { @Override public void call() { - AndroidSchedulers.mainThread().schedule(new Action1<Inner>() { + AndroidSchedulers.handlerThread(handlerThread.getHandler()).schedule(new Action1<Inner>() { @Override public void call(final Inner inner) { synchronized (lock) { diff --git a/main/src/cgeo/geocaching/sensors/GeoDirHandler.java b/main/src/cgeo/geocaching/sensors/GeoDirHandler.java index 0af2cc8..588bd84 100644 --- a/main/src/cgeo/geocaching/sensors/GeoDirHandler.java +++ b/main/src/cgeo/geocaching/sensors/GeoDirHandler.java @@ -42,12 +42,12 @@ public abstract class GeoDirHandler { * preferences allow it). */ public Subscription start() { - return app.geoDirObservable().subscribe(new Action1<ImmutablePair<IGeoData, Float>>() { + return app.geoDirObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<ImmutablePair<IGeoData, Float>>() { @Override public void call(final ImmutablePair<IGeoData, Float> geoDir) { handleGeoDir(geoDir); } - }, AndroidSchedulers.mainThread()); + }); } } diff --git a/main/src/cgeo/geocaching/utils/StartableHandlerThread.java b/main/src/cgeo/geocaching/utils/StartableHandlerThread.java new file mode 100644 index 0000000..152badc --- /dev/null +++ b/main/src/cgeo/geocaching/utils/StartableHandlerThread.java @@ -0,0 +1,80 @@ +package cgeo.geocaching.utils; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import org.eclipse.jdt.annotation.NonNull; +import rx.Subscriber; +import rx.functions.Action0; +import rx.subscriptions.Subscriptions; + +/** + * Derivated class of {@link android.os.HandlerThread} with an exposed handler and a start/stop mechanism + * based on subscriptions. + */ + +public class StartableHandlerThread extends HandlerThread { + + private final static int START = 1; + private final static int STOP = 2; + + static public interface Callback { + public void start(final Context context, final Handler handler); + public void stop(); + } + + private class StartableHandler extends Handler { + public StartableHandler() { + super(StartableHandlerThread.this.getLooper()); + } + + @Override + public void handleMessage(final Message message) { + if (callback != null) { + switch (message.what) { + case START: + callback.start((Context) message.obj, this); + break; + case STOP: + callback.stop(); + break; + } + } + } + } + + private Handler handler; + private Callback callback; + + public StartableHandlerThread(@NonNull final String name, final int priority, final Callback callback) { + super(name, priority); + this.callback = callback; + } + + public StartableHandlerThread(@NonNull final String name, final int priority) { + this(name, priority, null); + } + + public Handler getHandler() { + if (handler == null) { + synchronized(this) { + if (handler == null) { + handler = new StartableHandler(); + } + } + } + return handler; + } + + public void start(final Subscriber<?> subscriber, final Context context) { + getHandler().obtainMessage(START, context).sendToTarget(); + subscriber.add(Subscriptions.create(new Action0() { + @Override + public void call() { + getHandler().sendEmptyMessage(STOP); + } + })); + } + +} |
