aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Tardieu <sam@rfc1149.net>2014-03-26 15:19:03 +0100
committerSamuel Tardieu <sam@rfc1149.net>2014-03-26 15:19:03 +0100
commitb6e28fc56d7517116bfa9e13fcfd0424de26e04a (patch)
treea17ffb6f1eda4c7e6b2d236643157cd19e2f55ff
parent6dd33e492d9123334ec0546414e574bbb10ad760 (diff)
downloadcgeo-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.
-rw-r--r--main/src/cgeo/geocaching/CgeoApplication.java7
-rw-r--r--main/src/cgeo/geocaching/sensors/DirectionProvider.java91
-rw-r--r--main/src/cgeo/geocaching/sensors/GeoDataProvider.java12
-rw-r--r--main/src/cgeo/geocaching/sensors/GeoDirHandler.java4
-rw-r--r--main/src/cgeo/geocaching/utils/StartableHandlerThread.java80
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);
+ }
+ }));
+ }
+
+}